feat(scripts): 异步执行、全局进度看板与完成提示音设置
脚本 exec 立即返回 running 并在后台跑批次;/ws/alerts 推送 script_progress/complete; 新增 ScriptRunsPage、Telegram 汇总通知与可配置的浏览器完成提示音。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
## 栈
|
||||
|
||||
FastAPI + Async SQLAlchemy + Redis · Vue 3 + Vuetify 4 SPA(14 页)· Vite → `web/app/`
|
||||
FastAPI + Async SQLAlchemy + Redis · Vue 3 + Vuetify 4 SPA(15 页)· Vite → `web/app/`
|
||||
|
||||
## 环境(勿在仓库写密码)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## 项目概要
|
||||
|
||||
Nexus:2000+ 子机运维平台。后端 FastAPI + MySQL + Redis;前端 **Vue 3 + Vuetify 4 + TypeScript SPA**(14 页,`frontend/src/pages/`);构建输出 `web/app/`;安装向导 `web/app/install.html`。
|
||||
Nexus:2000+ 子机运维平台。后端 FastAPI + MySQL + Redis;前端 **Vue 3 + Vuetify 4 + TypeScript SPA**(15 页,`frontend/src/pages/`);构建输出 `web/app/`;安装向导 `web/app/install.html`。
|
||||
|
||||
## 本地开发
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# 审计 — 脚本执行全局进度与看板(2026-06-08)
|
||||
|
||||
## 范围
|
||||
|
||||
异步脚本执行、WebSocket 进度、Telegram 汇总、ScriptRunsPage、App 全局 UI。
|
||||
|
||||
## 安全
|
||||
|
||||
| 项 | 结论 |
|
||||
|----|------|
|
||||
| 后台任务 DB 会话 | 独立 `AsyncSessionLocal`,请求结束不泄漏 |
|
||||
| WS 鉴权 | 复用 `/ws/alerts` JWT,无新公开端点 |
|
||||
| Telegram | 无失败明细、无命令输出;链接为公开 SPA hash 路由(需登录) |
|
||||
| CUD 审计 | `execute_started` / `execute_command` 仍在 service 层写入 |
|
||||
|
||||
## 可靠性
|
||||
|
||||
| 项 | 结论 |
|
||||
|----|------|
|
||||
| 后台异常 | `_mark_execution_catastrophic_failure` → status=failed + 通知 |
|
||||
| 长任务回调 | `script_job_callback` 终态触发 `script_complete` |
|
||||
| WS 断线 | 前端 3s 轮询降级 |
|
||||
| 任务引用 | `_BG_EXEC_TASKS` 防止 task 被 GC |
|
||||
|
||||
## 架构
|
||||
|
||||
- 通知编排独立于 `script_execution_store`,避免 store → websocket 循环依赖
|
||||
- 进度分子仍为 `success/total`(失败不计入 success);`done/remaining` 供文案
|
||||
|
||||
## 测试证据
|
||||
|
||||
- `tests/test_script_execution_progress_stats.py`(3 条)
|
||||
- 构建:`cd frontend && npm run build`
|
||||
|
||||
## 残留风险(可接受)
|
||||
|
||||
- 调度器 `schedule_runner` 触发脚本执行时同样走后台任务,不阻塞 60s tick(预期行为)
|
||||
- 多 worker 下 WS 经 Redis pub/sub 广播(与告警一致)
|
||||
@@ -0,0 +1,35 @@
|
||||
# 2026-06-08 — 脚本执行完成提示音可配置
|
||||
|
||||
## 摘要
|
||||
|
||||
将原先硬编码在 `App.vue` 的双音提示改为系统设置项,支持关闭/三种音色,并可试听。
|
||||
|
||||
## 动机
|
||||
|
||||
用户需在「设置」中控制脚本执行结束时的浏览器提示音,而非写死在代码里。
|
||||
|
||||
## 涉及文件
|
||||
|
||||
- `frontend/src/utils/scriptCompleteSound.ts` — 音色预设与播放
|
||||
- `frontend/src/composables/useScriptCompleteSound.ts` — 与 MySQL 设置同步
|
||||
- `frontend/src/App.vue` — 使用 composable
|
||||
- `frontend/src/pages/SettingsPage.vue` — 「界面提示」卡片
|
||||
- `server/api/settings.py` — `script_exec_complete_sound` 白名单与枚举校验
|
||||
|
||||
## 设置项
|
||||
|
||||
| Key | 可选值 | 默认 |
|
||||
|-----|--------|------|
|
||||
| `script_exec_complete_sound` | `off` / `beep_short` / `beep_double` / `chime_soft` | `beep_double`(未配置时) |
|
||||
|
||||
API:`PUT /api/settings/script_exec_complete_sound` `{ "value": "beep_double" }`
|
||||
|
||||
## 迁移 / 重启
|
||||
|
||||
- 无需迁移;首次保存时写入 `settings` 表
|
||||
- 仅前端热更新即可试听;后端需 reload 以识别新白名单键
|
||||
|
||||
## 验证
|
||||
|
||||
- 设置 → 界面提示 → 切换音色并「试听」
|
||||
- 触发脚本执行完成,确认按所选音色播放或关闭
|
||||
@@ -0,0 +1,54 @@
|
||||
# 2026-06-08 — 脚本执行:全局进度 + 看板 + Telegram 汇总
|
||||
|
||||
## 摘要
|
||||
|
||||
脚本批量执行改为真后台(API 立即返回 `running`);扩展 `/ws/alerts` 推送 `script_progress` / `script_complete`;全局顶栏进度条与完成 Alert+提示音;新建「脚本看板」页;Telegram 仅发成功/失败汇总与看板深链。
|
||||
|
||||
## 动机
|
||||
|
||||
366 台同步执行阻塞 HTTP 请求;用户需跨页面查看进度;失败明细应在看板而非 Telegram 长列表。
|
||||
|
||||
## 涉及文件
|
||||
|
||||
### 后端
|
||||
|
||||
- `server/application/services/script_service.py` — `asyncio` 后台批次
|
||||
- `server/application/services/script_execution_notify.py` — WS + Telegram 编排
|
||||
- `server/application/services/script_job_callback.py` — 长任务终态通知
|
||||
- `server/infrastructure/redis/script_execution_store.py` — `execution_progress_stats`
|
||||
- `server/api/websocket.py` — `broadcast_script_progress/complete`
|
||||
- `server/infrastructure/telegram/__init__.py` — `send_telegram_script_summary`
|
||||
- `server/config.py` / `server/api/settings.py` — `NOTIFY_SCRIPT_COMPLETE`
|
||||
|
||||
### 前端
|
||||
|
||||
- `frontend/src/composables/useScriptExecutionQueue.ts`
|
||||
- `frontend/src/composables/useWebSocket.ts`
|
||||
- `frontend/src/App.vue` — 全局进度与完成 Alert
|
||||
- `frontend/src/pages/ScriptRunsPage.vue` — 第 15 页
|
||||
- `frontend/src/pages/ScriptsPage.vue` — 移除页内批量状态面板
|
||||
- `frontend/src/router/index.ts` / `frontend/src/types/api.ts`
|
||||
|
||||
### 文档
|
||||
|
||||
- `docs/project/nexus-functional-development-guide.md` — 15 页 SSOT
|
||||
- `docs/changelog/2026-06-08-script-exec-global-progress-board.md`
|
||||
- `docs/audit/2026-06-08-script-exec-global-progress-board.md`
|
||||
|
||||
## 迁移 / 重启
|
||||
|
||||
- 无需 DB 迁移
|
||||
- 需重启 API worker;前端 `vite build` 后部署 `web/app/`
|
||||
- 可选:在设置中配置 `notify_script_complete`
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
pytest tests/test_script_execution_progress_stats.py tests/test_exec_detail_grouping.py -q
|
||||
cd frontend && npm run build
|
||||
bash scripts/local_verify.sh
|
||||
```
|
||||
|
||||
- 提交执行后 1s 内对话框关闭,顶栏显示「已进行 X 台,剩余 Y 台」
|
||||
- 结束:提示音 + Alert,Telegram 仅汇总 + `#/script-runs/{id}` 链接
|
||||
- WS 断线:3s 轮询直至终态
|
||||
@@ -0,0 +1,173 @@
|
||||
# 实施计划 — 脚本库全局执行进度 + Telegram 通知
|
||||
|
||||
**日期**: 2026-06-08
|
||||
**设计**: [2026-06-08-script-exec-global-progress-telegram-design.md](../specs/2026-06-08-script-exec-global-progress-telegram-design.md)
|
||||
|
||||
## 分期概览
|
||||
|
||||
| 阶段 | 内容 | 估时 | 依赖 |
|
||||
|------|------|------|------|
|
||||
| **P0** | 后端异步 exec + terminal hook | 1d | — |
|
||||
| **P1** | WS `script_progress` / `script_complete` | 0.5d | P0 |
|
||||
| **P2** | Telegram `send_telegram_script_complete` | 0.5d | P0 |
|
||||
| **P3** | 前端全局 SnackbarQueue + Progress + Alert + 声音 | 1d | P1 |
|
||||
| **P4** | ScriptsPage 接入 / 移除页内面板 | 0.5d | P3 |
|
||||
| **P5** | 测试、changelog、audit、部署 | 0.5d | P0–P4 |
|
||||
|
||||
**合计**:约 3–4 人日(单 Agent 连续实施)。
|
||||
|
||||
---
|
||||
|
||||
## P0 — 后端:异步执行(关键路径)
|
||||
|
||||
### 涉及文件
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| [server/application/services/script_service.py](../../server/application/services/script_service.py) | `execute_command` 拆为 `start_execution` + `_run_execution_batches` |
|
||||
| [server/api/scripts.py](../../server/api/scripts.py) | `/exec`、`/retry` 立即返回 running |
|
||||
| [server/main.py](../../server/main.py) | 确保 task 异常日志 + 可选 task 注册防 shutdown 丢失 |
|
||||
|
||||
### 步骤
|
||||
|
||||
1. `execute_command` 创建 DB/Redis 记录后 `asyncio.create_task(_run_execution_batches(...))`。
|
||||
2. 将现有 batch 循环移入 `_run_execution_batches`,逻辑不变。
|
||||
3. Task 顶层 `try/except`:异常时 `apply_update(status=failed, terminal=True)`。
|
||||
4. API 返回 `get_execution_detail(id)`(status=running, progress=0/N)。
|
||||
5. **注意**:同一 worker 内 task 即可;多 worker 时 Redis live 已是 SSOT(已满足)。
|
||||
|
||||
### 测试
|
||||
|
||||
- `tests/test_script_execution_async.py`:mock dispatch,断言 POST /exec 立即返回 & 后台终态。
|
||||
|
||||
---
|
||||
|
||||
## P1 — WebSocket 进度广播
|
||||
|
||||
### 涉及文件
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| [server/api/websocket.py](../../server/api/websocket.py) | `broadcast_script_progress` / `broadcast_script_complete` |
|
||||
| [server/infrastructure/redis/script_execution_store.py](../../server/infrastructure/redis/script_execution_store.py) | `apply_update` 中调用 broadcast(或 script_service 批次回调) |
|
||||
| [server/application/services/script_job_callback.py](../../server/application/services/script_job_callback.py) | long_task 单台 callback 时也 broadcast |
|
||||
|
||||
### 步骤
|
||||
|
||||
1. 定义消息 schema(见 design doc)。
|
||||
2. 每批 `batch_progress` 或每台完成后推送(366 台建议**每完成 1 台或每批**推送,避免 WS 风暴可 batch 每 5 台)。
|
||||
3. 前端 [useWebSocket.ts](../../frontend/src/composables/useWebSocket.ts) 解析 `script_progress` / `script_complete`。
|
||||
|
||||
---
|
||||
|
||||
## P2 — Telegram 脚本执行结果
|
||||
|
||||
### 涉及文件
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| [server/infrastructure/telegram/__init__.py](../../server/infrastructure/telegram/__init__.py) | `send_telegram_script_complete` |
|
||||
| [server/config.py](../../server/config.py) | `NOTIFY_SCRIPT_COMPLETE` |
|
||||
| [server/api/settings.py](../../server/api/settings.py) | 设置项读写(若 NOTIFY_* 走 settings 表) |
|
||||
| terminal hook | 组装 failed 列表(server name + exit_code + stderr 首行) |
|
||||
|
||||
### 步骤
|
||||
|
||||
1. 复用 `send_telegram_sync_complete` 结构与 `_notify_enabled`。
|
||||
2. 失败明细:查 server repo 取 name;summary 用 `sanitize_external_message`。
|
||||
3. 超长分片或截断 + 指向 execution #id。
|
||||
4. 单测:mock httpx,断言 HTML 含失败台名。
|
||||
|
||||
---
|
||||
|
||||
## P3 — 前端全局 UI(Vuetify 4)
|
||||
|
||||
### 涉及文件
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| [frontend/src/App.vue](../../frontend/src/App.vue) | `VSnackbarQueue` 替换单条 `v-snackbar`;挂载 `ScriptExecProgressHost` |
|
||||
| **新建** `frontend/src/components/script/ScriptExecProgressHost.vue` | Queue item 模板:ProgressLinear chunks + 文案 |
|
||||
| **新建** `frontend/src/composables/useScriptExecutionQueue.ts` | track / onProgress / onComplete / playSound |
|
||||
| **新建** `frontend/src/components/script/ScriptExecCompleteAlert.vue` | VAlert closable + 1s leave transition |
|
||||
| `frontend/public/notify.mp3` 或 assets | 提示音(短、免版权) |
|
||||
|
||||
### UI 行为
|
||||
|
||||
1. **进行中**:SnackbarQueue 顶部,`location="top"`,`timeout="-1"`(手动关闭或完成关闭)。
|
||||
2. **进度条**:`v-progress-linear` `rounded` `height="6"` `:model-value="done/total*100"` `:buffer-value="..."` 制造流动感(见 Vuetify chunks 示例)。
|
||||
3. **完成**:
|
||||
- Snackbar queue 项 resolve(Promise API)。
|
||||
- 显示 `VAlert`(type=success/warning/error),`closable`,点击关闭触发 `v-fade-transition` 1000ms。
|
||||
- `new Audio(...).play().catch(() => {})`(静默处理自动播放策略拦截)。
|
||||
4. Alert 可点「查看详情」→ `router.push('#/scripts')` + `openExecDetail(id)`(通过 event bus 或 query)。
|
||||
|
||||
### 与现有 `$snackbar` 兼容
|
||||
|
||||
- 保留 `window.$snackbar` 给普通 toast;脚本进度走独立 queue,避免互相覆盖。
|
||||
|
||||
---
|
||||
|
||||
## P4 — ScriptsPage 接入
|
||||
|
||||
### 涉及文件
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| [frontend/src/pages/ScriptsPage.vue](../../frontend/src/pages/ScriptsPage.vue) | `submitExec` → `queue.track()`;删除/隐藏页内「批量执行状态」卡片 |
|
||||
| [frontend/src/pages/SchedulesPage.vue](../../frontend/src/pages/SchedulesPage.vue) | 若 schedule 触发 exec,同样 track(可选 P4+) |
|
||||
|
||||
### 步骤
|
||||
|
||||
1. `doRun` / `doQuickExec`:POST 成功后 `showRun=false`,调用 `queue.track(id, label, total)`。
|
||||
2. 移除 `trackedExecs` / `pollTimer` / `activeTrackedExecs`(逻辑迁至 composable)。
|
||||
3. WS 断线时 composable 降级 3s 轮询直至终态。
|
||||
|
||||
---
|
||||
|
||||
## P5 — 验证与交付
|
||||
|
||||
```bash
|
||||
pytest tests/test_script_execution_async.py tests/test_telegram_script.py -q
|
||||
cd frontend && npx vite build
|
||||
bash scripts/local_verify.sh
|
||||
```
|
||||
|
||||
### 手动验收
|
||||
|
||||
1. 生产 10 台脚本:顶部进度 + 完成 Alert + 声音。
|
||||
2. 366 台(或 staging):Telegram 汇总与失败明细。
|
||||
3. 关闭 Telegram 通知设置后无推送。
|
||||
|
||||
### 文档
|
||||
|
||||
- `docs/changelog/2026-06-08-script-exec-global-progress-telegram.md`
|
||||
- `docs/audit/2026-06-08-script-exec-global-progress-telegram.md`
|
||||
- 更新 [AI-HANDOFF-2026-06-08.md](../../project/AI-HANDOFF-2026-06-08.md)
|
||||
|
||||
### 回滚
|
||||
|
||||
- 后端:revert commit;进行中的 execution 靠 Redis live 继续,无 schema 迁移。
|
||||
- 前端:revert + vite build deploy。
|
||||
|
||||
---
|
||||
|
||||
## 风险与对策
|
||||
|
||||
| 风险 | 对策 |
|
||||
|------|------|
|
||||
| 366 台 WS 消息过多 | 节流:每批或每 5 台推送一次 |
|
||||
| 浏览器拦截自动播放 | 首次用户手势后解锁;Alert 仍可见 |
|
||||
| 异步 exec 重复提交 | 可选:同脚本 running 时 confirm(P4+) |
|
||||
| Telegram 过长 | 截断 + 链接 execution id |
|
||||
| 多 Tab 重复声音 | `localStorage` leader election 或仅 focused tab 播放 |
|
||||
|
||||
---
|
||||
|
||||
## 建议实施顺序(给 Agent)
|
||||
|
||||
```
|
||||
P0 异步 API → P1 WS → P2 Telegram → P3 全局 UI → P4 ScriptsPage 接入 → P5 验证部署
|
||||
```
|
||||
|
||||
**请先确认设计再写代码**(perfect-implementation 纪律)。
|
||||
@@ -0,0 +1,148 @@
|
||||
# 设计说明 — 脚本库全局执行进度 + Telegram 结果通知
|
||||
|
||||
**日期**: 2026-06-08
|
||||
**基线**: `main@3f995a7`
|
||||
**参考 UI**: [VSnackbarQueue Promise](https://vuetifyjs.com/zh-Hans/components/snackbar-queue/#promise) · [VProgressLinear chunks](https://vuetifyjs.com/zh-Hans/components/progress-linear/#chunks) · [VAlert 可关闭](https://vuetifyjs.com/zh-Hans/components/alerts/#section-7c7b578b)
|
||||
|
||||
## 背景与目标
|
||||
|
||||
运维在脚本库对 300+ 台执行命令时,需要:
|
||||
|
||||
1. **点击执行后立即返回**,不阻塞对话框/页面,可在其他页面继续操作。
|
||||
2. **全局顶部**展示执行进度(流动状进度条 +「已进行 3 台,剩余 363 台」)。
|
||||
3. **执行结束**时:浏览器提醒 + 短促提示音;可点击关闭,**1 秒渐隐**。
|
||||
4. **Telegram** 推送汇总:成功 N / 失败 M;失败项含**服务器名称、退出码、原因摘要**(可多条)。
|
||||
|
||||
## 现状差距
|
||||
|
||||
| 能力 | 现状 | 缺口 |
|
||||
|------|------|------|
|
||||
| 后台执行 | `POST /api/scripts/exec` **同步 await** 全部批次(366 台可阻塞数十秒~数分钟) | 须改为「创建 execution → 立即返回 → 后台 dispatch」 |
|
||||
| 进度 UI | ScriptsPage 底部「批量执行状态」+ 10s 轮询 | 须全局 Snackbar Queue + 细粒度进度 |
|
||||
| 实时推送 | Push 有 `/ws/sync`;脚本**无** WS | 须新增 `script_progress` 广播或复用 alerts WS |
|
||||
| Telegram | 仅有 `send_telegram_sync_complete` | 须新增 `send_telegram_script_complete` + 设置项 |
|
||||
| 完成提醒 | 终态仅 `snackbar(warning)` | 须 Alert + 声音 + 渐隐动画 |
|
||||
|
||||
## 方案对比
|
||||
|
||||
### A. 后台执行 + WebSocket 进度(推荐)
|
||||
|
||||
```
|
||||
POST /exec → 202 + execution_id
|
||||
↓
|
||||
asyncio.create_task(run_batches)
|
||||
↓ 每完成一台/一批
|
||||
broadcast_script_progress → /ws/alerts 或 /ws/scripts
|
||||
↓ 终态
|
||||
send_telegram_script_complete + WS terminal event
|
||||
```
|
||||
|
||||
**优点**:进度实时、与 Push 模式一致、Snackbar 可流畅更新。
|
||||
**缺点**:需改后端执行模型 + WS 协议。
|
||||
|
||||
### B. 仅前端全局轮询
|
||||
|
||||
API 仍同步,或改为后台但前端 2s 轮询 `GET /executions/{id}`。
|
||||
|
||||
**优点**:改动小。
|
||||
**缺点**:366 台时 API 仍可能长时间不返回;轮询粗、流量大;不符合「先返回再执行」。
|
||||
|
||||
**选定 A**。
|
||||
|
||||
## 接口与数据模型
|
||||
|
||||
### API 变更
|
||||
|
||||
| 端点 | 变更 |
|
||||
|------|------|
|
||||
| `POST /api/scripts/exec` | 创建 execution + `init_live` 后**立即返回** `{ id, status: "running", progress: "0/N" }`;dispatch 在 `asyncio.create_task` |
|
||||
| `POST /api/scripts/executions/{id}/retry` | 同上异步模式 |
|
||||
| `GET /api/scripts/executions/{id}` | 不变(轮询兜底) |
|
||||
|
||||
**兼容**:长任务(`long_task`)逻辑保持;短任务也走同一异步路径。
|
||||
|
||||
### WebSocket 消息(扩展 `/ws/alerts` 或新建 `/ws/scripts`)
|
||||
|
||||
推荐 **扩展 `/ws/alerts`**(App 已连接),新增 type:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "script_progress",
|
||||
"execution_id": 6,
|
||||
"label": "磁盘检查",
|
||||
"status": "running",
|
||||
"done": 3,
|
||||
"success": 3,
|
||||
"failed": 0,
|
||||
"total": 366,
|
||||
"remaining": 363
|
||||
}
|
||||
```
|
||||
|
||||
终态:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "script_complete",
|
||||
"execution_id": 6,
|
||||
"status": "partial",
|
||||
"success": 363,
|
||||
"failed": 3,
|
||||
"total": 366,
|
||||
"failed_preview": [
|
||||
{ "server_id": 1, "name": "srv-a", "exit_code": 1, "summary": "Connection timed out" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`done` = 已有终态结果的台数(含失败);`success` 沿用 `execution_progress_summary` 语义(失败不计入 success)。
|
||||
|
||||
### Telegram
|
||||
|
||||
新增 `send_telegram_script_complete()`,参照 `send_telegram_sync_complete`:
|
||||
|
||||
- 设置项:`NOTIFY_SCRIPT_COMPLETE`(默认 `true`,Settings 可关)
|
||||
- 正文:脚本名/命令摘要、操作人、成功/失败/总计、耗时
|
||||
- **失败明细**:每台一行 — `名称 · exit码 · 原因摘要`(HTML escape + sanitize)
|
||||
- 超过 Telegram 4096 字符:首批 20 条 +「…及其他 N 台,详见 Nexus #/scripts 执行 #id」
|
||||
- 触发点:`script_execution_store.apply_update(..., terminal=True)` 或 dedicated `on_execution_terminal` hook
|
||||
|
||||
### 前端全局 UI
|
||||
|
||||
| 组件 | 用途 |
|
||||
|------|------|
|
||||
| `VSnackbarQueue` | 全局顶部(`location="top"`),每任务一条 queue item |
|
||||
| `VProgressLinear` `buffer-value` + `indeterminate` 交替 | 「流动状」进度;`model-value = done/total*100` |
|
||||
| `VAlert` closable + `@click:close` + CSS transition 1s | 完成提醒条(成功/部分失败/全失败配色) |
|
||||
| `Audio` / `new Audio('/app/notify.mp3')` | 终态播放一次(用户可浏览器静音) |
|
||||
|
||||
**Snackbar 文案示例**(进行中):
|
||||
|
||||
> 磁盘检查 · 已进行 3 台,剩余 363 台
|
||||
|
||||
**Promise 模式**:queue 项在 `script_complete` 时 resolve,切换为 Alert 或关闭 snackbar 并弹出 Alert。
|
||||
|
||||
**状态管理**:新建 `useScriptExecutionQueue`(Pinia 或 composable singleton),ScriptsPage / SchedulesPage 提交后只调用 `queue.track(executionId, label, total)`,**移除**页面内 `trackedExecs` 面板(或保留「详情」链接)。
|
||||
|
||||
## 安全与性能
|
||||
|
||||
- WS 消息仅 JWT 已连接管理员可见;不含完整 stderr(Telegram 同样截断)。
|
||||
- 后台 task 须捕获异常写 execution `failed`,避免 silent hang。
|
||||
- 366 台 Telegram 分条/截断,防 flood。
|
||||
- 单 worker:`create_task` 绑定 primary worker 约定(与现有 background tasks 一致)。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] 选 366 台点执行 → **1s 内**关闭对话框,顶部出现进度 Snackbar。
|
||||
- [ ] 切换至 Dashboard/其他页,进度条仍更新。
|
||||
- [ ] 文案显示「已进行 X 台,剩余 Y 台」,X+Y=总数(进行中);Y 随完成递减。
|
||||
- [ ] 全部结束:短促提示音 + 顶部 Alert「成功 363 台,失败 3 台」;点击后 **1s 渐隐**。
|
||||
- [ ] Telegram 收到汇总 + 3 台失败名称与原因。
|
||||
- [ ] Settings 关闭 `NOTIFY_SCRIPT_COMPLETE` 时不发 Telegram。
|
||||
- [ ] 长任务 / retry / 临时命令 行为一致。
|
||||
|
||||
## 非目标(本期不做)
|
||||
|
||||
- 多 execution 合并为一条 Telegram(每台 execution 一条)。
|
||||
- 浏览器 Push Notification API(仅站内 Alert + 声音)。
|
||||
- 修改 `success/total` 进度语义。
|
||||
@@ -20,7 +20,7 @@
|
||||
9. [后端 API 全览](#9-后端-api-全览)
|
||||
10. [核心业务模块详解](#10-核心业务模块详解)
|
||||
11. [前端 SPA 架构](#11-前端-spa-架构)
|
||||
12. [14 个业务页面功能说明](#12-14-个业务页面功能说明)
|
||||
12. [15 个业务页面功能说明](#12-15-个业务页面功能说明)
|
||||
13. [Composables 与状态管理](#13-composables-与状态管理)
|
||||
14. [WebSocket 与 WebSSH](#14-websocket-与-webssh)
|
||||
15. [Agent 与子机协议](#15-agent-与子机协议)
|
||||
@@ -511,7 +511,7 @@ main.ts → App.vue(侧栏/搜索)→ router-view
|
||||
|
||||
---
|
||||
|
||||
## 12. 14 个业务页面功能说明
|
||||
## 12. 15 个业务页面功能说明
|
||||
|
||||
> 路由均位于 `#/` 下;除 Login 外需 JWT。
|
||||
|
||||
@@ -591,11 +591,21 @@ main.ts → App.vue(侧栏/搜索)→ router-view
|
||||
| 库内执行 | `POST /scripts/exec`(`script_id` 或 `command` 二选一) |
|
||||
| 临时命令 | 页内 quick exec 面板 |
|
||||
| 长任务 / DB 凭据 | `long_task`、`credential_id`;`GET /scripts/exec-config` |
|
||||
| 执行状态 | 10s 轮询 `GET /scripts/executions/{id}` |
|
||||
| 提交执行 | `POST /scripts/exec` **立即返回** `running`(后台批次) |
|
||||
| 全局进度 | `App.vue` + `useScriptExecutionQueue`(WS `script_progress` / `script_complete`) |
|
||||
| 历史 | `GET /scripts/executions?script_id=` |
|
||||
| 停止 / 重试 / 卡住 | `POST .../stop`、`.../retry`、`.../mark-stuck` |
|
||||
|
||||
### 12.7 CredentialsPage `/credentials`
|
||||
### 12.7 ScriptRunsPage `/script-runs` · `/script-runs/:id`
|
||||
|
||||
| 功能 | API |
|
||||
|------|-----|
|
||||
| 执行看板列表 | `GET /scripts/executions`(状态筛选 running/completed/partial/failed) |
|
||||
| 单次明细 | `GET /scripts/executions/{id}` · 失败分组 · 复制清单 · 跳转终端 |
|
||||
| 重试 / 停止 | 同 ScriptsPage |
|
||||
| 入口 | 侧栏「脚本看板」;Telegram 深链 `#/script-runs/{id}` |
|
||||
|
||||
### 12.8 CredentialsPage `/credentials`
|
||||
|
||||
三 Tab:密码预设、SSH 密钥、DB 凭据;编辑时空密码表示不修改。
|
||||
|
||||
@@ -607,7 +617,7 @@ main.ts → App.vue(侧栏/搜索)→ router-view
|
||||
|
||||
`username` 供 `credential_poller` 在 `add-by-ip` / retry 时与预设密码或私钥组合尝试 SSH。
|
||||
|
||||
### 12.8 SchedulesPage `/schedules`
|
||||
### 12.9 SchedulesPage `/schedules`
|
||||
|
||||
| 功能 | API / 行为 |
|
||||
|------|------------|
|
||||
@@ -619,11 +629,11 @@ main.ts → App.vue(侧栏/搜索)→ router-view
|
||||
| 立即执行 | push → `POST /sync/files`;script → `POST /scripts/exec` |
|
||||
| 后台 | `schedule_runner` 60s 扫描到期项 |
|
||||
|
||||
### 12.9 RetriesPage `/retries`
|
||||
### 12.10 RetriesPage `/retries`
|
||||
|
||||
失败推送重试队列;`POST /retries/{id}/retry`。
|
||||
|
||||
### 12.10 CommandsPage `/commands`
|
||||
### 12.11 CommandsPage `/commands`
|
||||
|
||||
| 视图 | API | 筛选 |
|
||||
|------|-----|------|
|
||||
@@ -631,19 +641,19 @@ main.ts → App.vue(侧栏/搜索)→ router-view
|
||||
| 会话视图 | `GET /assets/ssh-sessions` | server_id |
|
||||
| 推送日志 | `GET /servers/logs` | server_id、status、sync_mode、trigger_type |
|
||||
|
||||
### 12.11 AlertsPage `/alerts`
|
||||
### 12.12 AlertsPage `/alerts`
|
||||
|
||||
告警统计 + 分页;按 type/status 过滤。
|
||||
|
||||
### 12.12 AuditPage `/audit`
|
||||
### 12.13 AuditPage `/audit`
|
||||
|
||||
操作审计;action/user/date 过滤;`normalizeAuditList`。
|
||||
|
||||
### 12.13 SettingsPage `/settings`
|
||||
### 12.14 SettingsPage `/settings`
|
||||
|
||||
系统名/阈值/连接池、Telegram、改密、TOTP、API Key reveal、IP 白名单、终端快捷命令设置。
|
||||
|
||||
### 12.14 LoginPage `/login`
|
||||
### 12.15 LoginPage `/login`
|
||||
|
||||
JWT 登录、TOTP、429 锁定、开放重定向防护(`//` 拒绝)、bing 壁纸背景。
|
||||
|
||||
@@ -678,8 +688,9 @@ JWT 登录、TOTP、429 锁定、开放重定向防护(`//` 拒绝)、bing
|
||||
### 14.1 告警 WS `/ws/alerts`
|
||||
|
||||
- 连接: `buildWebSocketUrl('/ws/alerts', { token: accessToken })`
|
||||
- 消息类型: alert / recovery / system
|
||||
- App.vue 或 Dashboard 订阅
|
||||
- 消息类型: alert / recovery / system / **script_progress** / **script_complete**
|
||||
- App.vue:`useWebSocket` + `useScriptExecutionQueue`(全局进度条与完成 Alert)
|
||||
- WS 断线时队列 3s 轮询直至终态
|
||||
|
||||
### 14.2 推送 WS `/ws/sync`
|
||||
|
||||
@@ -879,7 +890,7 @@ bash deploy/deploy-production.sh
|
||||
| **C** | Push 页 composable 与推送时序 |
|
||||
| **D** | Terminal / WebSSH 调用链 |
|
||||
| **E** | schedules/retries/audit/scripts/agent/search API 明细 |
|
||||
| **F** | 14 页 ↔ composable ↔ 组件对照表 |
|
||||
| **F** | 15 页 ↔ composable ↔ 组件对照表 |
|
||||
| **G** | L4 验收 + Playwright 15 用例对照 |
|
||||
| **H** | HTTP 错误码与前端处理 |
|
||||
| **I** | 扩展开发检查清单 |
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# 脚本执行看板 — 本地验证报告(2026-06-08)
|
||||
|
||||
## 环境
|
||||
|
||||
- 主机:`127.0.0.1:8600`(`bash scripts/start-dev.sh`)
|
||||
- MySQL / Redis:Docker `nexus-mysql-1` / `nexus-redis-1`
|
||||
- 凭据:`.env` 中 `NEXUS_TEST_ADMIN_*` 已与本地 `admins` 表对齐(用户提供的本地密码)
|
||||
|
||||
## 结果摘要
|
||||
|
||||
| 检查项 | 结果 |
|
||||
|--------|------|
|
||||
| `bash scripts/local_verify.sh`(完整) | **通过** |
|
||||
| smoke:MySQL schema + 47 unit tests | 通过 |
|
||||
| `tests/test_api.py` | **26/26** |
|
||||
| ruff F | 通过 |
|
||||
| 脚本相关 pytest(15 条) | 通过 |
|
||||
|
||||
## local_verify 明细
|
||||
|
||||
```
|
||||
═══ All local checks passed ═══
|
||||
```
|
||||
|
||||
- `GET /health` → 200
|
||||
- `POST /api/auth/login` → 200(JWT 获取成功)
|
||||
- Scripts CRUD:`POST/GET/DELETE /api/scripts/` 均通过
|
||||
- 其余 Server / Schedules / Settings / Audit / Retries / Sync 端点均通过
|
||||
|
||||
## 脚本专项 pytest
|
||||
|
||||
```bash
|
||||
.venv/bin/pytest tests/test_script_execution_progress_stats.py \
|
||||
tests/test_script_execution_progress.py \
|
||||
tests/test_exec_detail_grouping.py \
|
||||
tests/test_script_dispatch.py -q
|
||||
# 15 passed
|
||||
```
|
||||
|
||||
## 异步执行(设计验收)
|
||||
|
||||
`test_api` 已覆盖 Scripts 创建/列表/删除;结合 P0 实现,`POST /api/scripts/exec` 在代码审查路径上为「创建记录后立即返回 `running` + 后台 `asyncio` 任务」。
|
||||
|
||||
**建议终验(浏览器,需用户):**
|
||||
|
||||
1. 脚本库选多台子机执行 → 1s 内关对话框,顶栏进度条更新
|
||||
2. `#/script-runs/{id}` 查看失败明细与分组
|
||||
3. 完成时 Alert + 提示音;Telegram 仅汇总(若已配置 bot)
|
||||
|
||||
## 未在本报告执行的项
|
||||
|
||||
- 生产 `deploy/pre_deploy_check.sh` + 部署
|
||||
- 366 台规模压测
|
||||
- Playwright 浏览器自动化
|
||||
+134
-3
@@ -227,8 +227,70 @@
|
||||
</template>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<!-- ── 脚本执行全局进度 ── -->
|
||||
<div
|
||||
v-if="auth.isLoggedIn && runningItems.length"
|
||||
class="nexus-script-exec-bar"
|
||||
>
|
||||
<v-sheet
|
||||
v-for="item in runningItems"
|
||||
:key="item.executionId"
|
||||
class="nexus-script-exec-bar__item px-4 py-2"
|
||||
color="surface"
|
||||
border
|
||||
>
|
||||
<div class="d-flex align-center flex-wrap ga-2 mb-1">
|
||||
<span class="text-body-2 font-weight-medium text-truncate">{{ item.label }}</span>
|
||||
<v-chip size="x-small" variant="tonal" color="primary" label>#{{ item.executionId }}</v-chip>
|
||||
<v-spacer />
|
||||
<span class="text-caption text-medium-emphasis">
|
||||
已进行 {{ item.done }} 台,剩余 {{ item.remaining }} 台
|
||||
<span class="ml-2">({{ item.progress }})</span>
|
||||
</span>
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:model-value="item.total ? Math.round((item.done / item.total) * 100) : 0"
|
||||
color="primary"
|
||||
height="6"
|
||||
rounded
|
||||
striped
|
||||
stream
|
||||
/>
|
||||
</v-sheet>
|
||||
</div>
|
||||
|
||||
<!-- ── 脚本执行完成提示 ── -->
|
||||
<div v-if="auth.isLoggedIn" class="nexus-script-exec-alerts">
|
||||
<v-alert
|
||||
v-for="alert in completeAlerts"
|
||||
:key="alert.executionId"
|
||||
:class="{ 'nexus-script-exec-alert--fading': alert.fading }"
|
||||
:type="completeAlertType(alert.status)"
|
||||
variant="tonal"
|
||||
closable
|
||||
density="compact"
|
||||
class="mb-2"
|
||||
@click:close="dismissCompleteAlert(alert.executionId)"
|
||||
>
|
||||
<div class="d-flex align-center flex-wrap ga-2">
|
||||
<span>
|
||||
「{{ alert.label }}」执行结束:
|
||||
成功 {{ alert.success }} / 失败 {{ alert.failed }} / 共 {{ alert.total }} 台
|
||||
</span>
|
||||
<v-btn
|
||||
size="small"
|
||||
variant="text"
|
||||
color="primary"
|
||||
@click="goToScriptRun(alert.executionId)"
|
||||
>
|
||||
查看详情
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-alert>
|
||||
</div>
|
||||
|
||||
<!-- ── Main Content ── -->
|
||||
<v-main :class="mainLayoutClass">
|
||||
<v-main :class="mainLayoutClass" :style="scriptExecBarOffset">
|
||||
<router-view :key="route.fullPath" />
|
||||
</v-main>
|
||||
|
||||
@@ -240,11 +302,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch, onUnmounted, provide, computed } from 'vue'
|
||||
import { ref, reactive, watch, onUnmounted, onMounted, provide, computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useTheme, useDisplay } from 'vuetify'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useWebSocket } from '@/composables/useWebSocket'
|
||||
import {
|
||||
useScriptExecutionQueue,
|
||||
onScriptExecutionComplete,
|
||||
} from '@/composables/useScriptExecutionQueue'
|
||||
import { useScriptCompleteSound } from '@/composables/useScriptCompleteSound'
|
||||
import { api, http } from '@/api'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -252,10 +319,32 @@ const router = useRouter()
|
||||
const theme = useTheme()
|
||||
const auth = useAuthStore()
|
||||
const ws = useWebSocket()
|
||||
const { runningItems, completeAlerts, dismissCompleteAlert } = useScriptExecutionQueue()
|
||||
const { playCompleteSound, syncFromServer: syncScriptSoundFromServer } = useScriptCompleteSound()
|
||||
|
||||
function completeAlertType(status: string): 'success' | 'warning' | 'error' | 'info' {
|
||||
if (status === 'completed') return 'success'
|
||||
if (status === 'failed') return 'error'
|
||||
if (status === 'partial') return 'warning'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
function goToScriptRun(executionId: number) {
|
||||
dismissCompleteAlert(executionId)
|
||||
router.push(`/script-runs/${executionId}`).catch(() => {})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onScriptExecutionComplete(() => playCompleteSound())
|
||||
})
|
||||
|
||||
// Auto-connect WebSocket when logged in
|
||||
watch(() => auth.isLoggedIn, (loggedIn) => {
|
||||
if (loggedIn) { ws.connect(); syncThemeFromServer() }
|
||||
if (loggedIn) {
|
||||
ws.connect()
|
||||
syncThemeFromServer()
|
||||
syncScriptSoundFromServer()
|
||||
}
|
||||
else ws.disconnect()
|
||||
}, { immediate: true })
|
||||
|
||||
@@ -292,6 +381,15 @@ const mainLayoutClass = computed(() => ({
|
||||
'nexus-page-main': route.path !== '/terminal' && route.path !== '/login',
|
||||
}))
|
||||
|
||||
const scriptExecBarOffset = computed(() => {
|
||||
const n = runningItems.value.length
|
||||
if (!n || !auth.isLoggedIn) return undefined
|
||||
const extra = n * 52
|
||||
return {
|
||||
paddingTop: `calc(var(--v-layout-top, 64px) + ${extra}px)`,
|
||||
}
|
||||
})
|
||||
|
||||
const opsItems = [
|
||||
{ to: '/', title: '仪表盘', icon: 'mdi-view-dashboard-outline' },
|
||||
{ to: '/servers', title: '服务器', icon: 'mdi-server' },
|
||||
@@ -299,6 +397,7 @@ const opsItems = [
|
||||
{ to: '/files', title: '文件管理', icon: 'mdi-folder-outline' },
|
||||
{ to: '/push', title: '推送', icon: 'mdi-upload-outline' },
|
||||
{ to: '/scripts', title: '脚本库', icon: 'mdi-code-braces' },
|
||||
{ to: '/script-runs', title: '脚本看板', icon: 'mdi-clipboard-play-outline' },
|
||||
{ to: '/credentials',title: '凭据', icon: 'mdi-key-outline' },
|
||||
{ to: '/schedules', title: '调度', icon: 'mdi-clock-outline' },
|
||||
{ to: '/retries', title: '重试队列', icon: 'mdi-refresh' },
|
||||
@@ -453,4 +552,36 @@ interface SearchResults {
|
||||
.nexus-nav-drawer :deep(.v-navigation-drawer__content) {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.nexus-script-exec-bar {
|
||||
position: fixed;
|
||||
top: var(--v-layout-top, 64px);
|
||||
left: var(--v-layout-left, 0);
|
||||
right: 0;
|
||||
z-index: 1005;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.nexus-script-exec-bar__item {
|
||||
pointer-events: auto;
|
||||
border-bottom: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
|
||||
}
|
||||
|
||||
.nexus-script-exec-alerts {
|
||||
position: fixed;
|
||||
top: calc(var(--v-layout-top, 64px) + 8px);
|
||||
right: 16px;
|
||||
z-index: 1006;
|
||||
max-width: min(420px, calc(100vw - 32px));
|
||||
pointer-events: none;
|
||||
}
|
||||
.nexus-script-exec-alerts .v-alert {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.nexus-script-exec-alert--fading {
|
||||
opacity: 0;
|
||||
transition: opacity 1s ease;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { ref } from 'vue'
|
||||
import { api } from '@/api'
|
||||
import {
|
||||
DEFAULT_SCRIPT_COMPLETE_SOUND,
|
||||
normalizeScriptCompleteSound,
|
||||
playScriptCompleteSoundById,
|
||||
type ScriptCompleteSoundId,
|
||||
} from '@/utils/scriptCompleteSound'
|
||||
|
||||
const LS_KEY = 'nexus_script_exec_complete_sound'
|
||||
const soundMode = ref<ScriptCompleteSoundId>(readCached())
|
||||
|
||||
function readCached(): ScriptCompleteSoundId {
|
||||
try {
|
||||
const raw = localStorage.getItem(LS_KEY)
|
||||
if (raw) return normalizeScriptCompleteSound(raw)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
return DEFAULT_SCRIPT_COMPLETE_SOUND
|
||||
}
|
||||
|
||||
function persistCache(mode: ScriptCompleteSoundId) {
|
||||
try {
|
||||
localStorage.setItem(LS_KEY, mode)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
export function useScriptCompleteSound() {
|
||||
async function syncFromServer() {
|
||||
try {
|
||||
const data = await api<{ value: string }>('/settings/script_exec_complete_sound')
|
||||
const mode = normalizeScriptCompleteSound(data.value)
|
||||
soundMode.value = mode
|
||||
persistCache(mode)
|
||||
} catch {
|
||||
soundMode.value = readCached()
|
||||
}
|
||||
}
|
||||
|
||||
async function saveToServer(mode: ScriptCompleteSoundId) {
|
||||
const normalized = normalizeScriptCompleteSound(mode)
|
||||
await api('/settings/script_exec_complete_sound', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ value: normalized }),
|
||||
})
|
||||
soundMode.value = normalized
|
||||
persistCache(normalized)
|
||||
}
|
||||
|
||||
function playCompleteSound() {
|
||||
playScriptCompleteSoundById(soundMode.value)
|
||||
}
|
||||
|
||||
function previewSound(mode?: ScriptCompleteSoundId) {
|
||||
playScriptCompleteSoundById(mode ?? soundMode.value)
|
||||
}
|
||||
|
||||
return {
|
||||
soundMode,
|
||||
syncFromServer,
|
||||
saveToServer,
|
||||
playCompleteSound,
|
||||
previewSound,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* Global script execution progress queue — fed by /ws/alerts script_* messages
|
||||
* with 3s polling fallback when WebSocket is disconnected.
|
||||
*/
|
||||
import { ref, computed, onUnmounted } from 'vue'
|
||||
import { http } from '@/api'
|
||||
import { isExecTerminalStatus } from '@/utils/scriptExecutionLabels'
|
||||
|
||||
export interface ScriptExecQueueItem {
|
||||
executionId: number
|
||||
label: string
|
||||
status: string
|
||||
total: number
|
||||
done: number
|
||||
remaining: number
|
||||
success: number
|
||||
failed: number
|
||||
progress: string
|
||||
longTask: boolean
|
||||
operator?: string
|
||||
}
|
||||
|
||||
export interface ScriptExecCompleteAlert {
|
||||
executionId: number
|
||||
label: string
|
||||
status: string
|
||||
success: number
|
||||
failed: number
|
||||
total: number
|
||||
fading: boolean
|
||||
}
|
||||
|
||||
const POLL_MS = 3000
|
||||
const activeItems = ref<Map<number, ScriptExecQueueItem>>(new Map())
|
||||
const completeAlerts = ref<ScriptExecCompleteAlert[]>([])
|
||||
const wsConnected = ref(true)
|
||||
|
||||
let pollTimer: ReturnType<typeof setInterval> | null = null
|
||||
const completeListeners = new Set<(alert: ScriptExecCompleteAlert) => void>()
|
||||
|
||||
function mapToItem(msg: Record<string, unknown>): ScriptExecQueueItem {
|
||||
const total = Number(msg.total) || 0
|
||||
const done = Number(msg.done) || 0
|
||||
const remaining = Number(msg.remaining) ?? Math.max(0, total - done)
|
||||
return {
|
||||
executionId: Number(msg.execution_id),
|
||||
label: String(msg.label || `执行 #${msg.execution_id}`),
|
||||
status: String(msg.status || 'running'),
|
||||
total,
|
||||
done,
|
||||
remaining,
|
||||
success: Number(msg.success) || 0,
|
||||
failed: Number(msg.failed) || 0,
|
||||
progress: String(msg.progress || `${done}/${total}`),
|
||||
longTask: Boolean(msg.long_task),
|
||||
operator: msg.operator ? String(msg.operator) : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
function upsertItem(item: ScriptExecQueueItem) {
|
||||
const next = new Map(activeItems.value)
|
||||
if (isExecTerminalStatus(item.status)) {
|
||||
next.delete(item.executionId)
|
||||
} else {
|
||||
next.set(item.executionId, item)
|
||||
}
|
||||
activeItems.value = next
|
||||
}
|
||||
|
||||
function pushCompleteAlert(item: ScriptExecQueueItem) {
|
||||
const alert: ScriptExecCompleteAlert = {
|
||||
executionId: item.executionId,
|
||||
label: item.label,
|
||||
status: item.status,
|
||||
success: item.success,
|
||||
failed: item.failed,
|
||||
total: item.total,
|
||||
fading: false,
|
||||
}
|
||||
completeAlerts.value = [alert, ...completeAlerts.value].slice(0, 5)
|
||||
for (const fn of completeListeners) {
|
||||
try {
|
||||
fn(alert)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
completeAlerts.value = completeAlerts.value.map(a =>
|
||||
a.executionId === alert.executionId ? { ...a, fading: true } : a,
|
||||
)
|
||||
setTimeout(() => {
|
||||
completeAlerts.value = completeAlerts.value.filter(
|
||||
a => a.executionId !== alert.executionId,
|
||||
)
|
||||
}, 1000)
|
||||
}, 8000)
|
||||
}
|
||||
|
||||
function ensurePolling() {
|
||||
if (pollTimer) return
|
||||
pollTimer = setInterval(() => {
|
||||
if (wsConnected.value) return
|
||||
void pollActive()
|
||||
}, POLL_MS)
|
||||
}
|
||||
|
||||
function stopPollingIfIdle() {
|
||||
if (activeItems.value.size > 0) return
|
||||
if (pollTimer) {
|
||||
clearInterval(pollTimer)
|
||||
pollTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
async function pollActive() {
|
||||
const ids = [...activeItems.value.keys()]
|
||||
if (!ids.length) {
|
||||
stopPollingIfIdle()
|
||||
return
|
||||
}
|
||||
for (const id of ids) {
|
||||
try {
|
||||
const res = await http.get<{
|
||||
id: number
|
||||
status: string
|
||||
progress?: string
|
||||
long_task?: boolean
|
||||
label?: string
|
||||
server_ids?: string
|
||||
results?: Record<string, unknown>
|
||||
}>(`/scripts/executions/${id}`)
|
||||
const prev = activeItems.value.get(id)
|
||||
const label = res.label || prev?.label || `执行 #${id}`
|
||||
let total = prev?.total || 0
|
||||
if (res.server_ids) {
|
||||
try {
|
||||
const parsed = JSON.parse(res.server_ids) as unknown
|
||||
if (Array.isArray(parsed)) total = parsed.length
|
||||
} catch {
|
||||
/* keep */
|
||||
}
|
||||
}
|
||||
const progress = res.progress || prev?.progress || `0/${total}`
|
||||
const parts = progress.split('/')
|
||||
const success = Number(parts[0]) || 0
|
||||
const totalFromProgress = Number(parts[1]) || total
|
||||
const item: ScriptExecQueueItem = {
|
||||
executionId: id,
|
||||
label,
|
||||
status: res.status,
|
||||
total: totalFromProgress || total,
|
||||
done: success,
|
||||
remaining: Math.max(0, (totalFromProgress || total) - success),
|
||||
success,
|
||||
failed: 0,
|
||||
progress,
|
||||
longTask: Boolean(res.long_task ?? prev?.longTask),
|
||||
operator: prev?.operator,
|
||||
}
|
||||
if (isExecTerminalStatus(res.status)) {
|
||||
upsertItem(item)
|
||||
pushCompleteAlert({ ...item, status: res.status })
|
||||
} else {
|
||||
upsertItem(item)
|
||||
}
|
||||
} catch {
|
||||
/* keep last known */
|
||||
}
|
||||
}
|
||||
stopPollingIfIdle()
|
||||
}
|
||||
|
||||
export function handleScriptWsMessage(msg: Record<string, unknown>) {
|
||||
const item = mapToItem(msg)
|
||||
if (!item.executionId) return
|
||||
if (msg.type === 'script_complete' || isExecTerminalStatus(item.status)) {
|
||||
upsertItem(item)
|
||||
pushCompleteAlert(item)
|
||||
stopPollingIfIdle()
|
||||
return
|
||||
}
|
||||
upsertItem(item)
|
||||
ensurePolling()
|
||||
}
|
||||
|
||||
export function setScriptQueueWsConnected(connected: boolean) {
|
||||
wsConnected.value = connected
|
||||
if (!connected && activeItems.value.size > 0) {
|
||||
ensurePolling()
|
||||
}
|
||||
}
|
||||
|
||||
export function registerScriptExecution(
|
||||
executionId: number,
|
||||
label: string,
|
||||
totalServers: number,
|
||||
longTask = false,
|
||||
progress?: string,
|
||||
status = 'running',
|
||||
) {
|
||||
if (isExecTerminalStatus(status)) return
|
||||
const item: ScriptExecQueueItem = {
|
||||
executionId,
|
||||
label,
|
||||
status,
|
||||
total: totalServers,
|
||||
done: 0,
|
||||
remaining: totalServers,
|
||||
success: 0,
|
||||
failed: 0,
|
||||
progress: progress || `0/${totalServers}`,
|
||||
longTask,
|
||||
}
|
||||
upsertItem(item)
|
||||
ensurePolling()
|
||||
}
|
||||
|
||||
export function onScriptExecutionComplete(listener: (alert: ScriptExecCompleteAlert) => void) {
|
||||
completeListeners.add(listener)
|
||||
return () => completeListeners.delete(listener)
|
||||
}
|
||||
|
||||
export function dismissCompleteAlert(executionId: number) {
|
||||
completeAlerts.value = completeAlerts.value.map(a =>
|
||||
a.executionId === executionId ? { ...a, fading: true } : a,
|
||||
)
|
||||
setTimeout(() => {
|
||||
completeAlerts.value = completeAlerts.value.filter(a => a.executionId !== executionId)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
export function useScriptExecutionQueue() {
|
||||
const runningItems = computed(() =>
|
||||
[...activeItems.value.values()].sort((a, b) => b.executionId - a.executionId),
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
/* singleton — do not stop global poll on component unmount */
|
||||
})
|
||||
|
||||
return {
|
||||
runningItems,
|
||||
completeAlerts: computed(() => completeAlerts.value),
|
||||
registerScriptExecution,
|
||||
dismissCompleteAlert,
|
||||
onScriptExecutionComplete,
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,16 @@
|
||||
* alert → { type: "alert", server_id, server_name, alert_type, alert_value }
|
||||
* recovery → { type: "recovery", server_id, server_name, metric, value }
|
||||
* system → { type: "system", event_type, message }
|
||||
* script_progress / script_complete → global script execution queue (useScriptExecutionQueue)
|
||||
* Push progress uses a separate channel: /ws/sync (see usePushProgress)
|
||||
* ping → { type: "ping" } → client must respond with text "pong"
|
||||
*/
|
||||
import { ref, computed } from 'vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import {
|
||||
handleScriptWsMessage,
|
||||
setScriptQueueWsConnected,
|
||||
} from '@/composables/useScriptExecutionQueue'
|
||||
|
||||
const ws = ref<WebSocket | null>(null)
|
||||
const connected = ref(false)
|
||||
@@ -64,6 +69,7 @@ export function useWebSocket() {
|
||||
socket.onopen = () => {
|
||||
connected.value = true
|
||||
reconnectAttempts.value = 0
|
||||
setScriptQueueWsConnected(true)
|
||||
}
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
@@ -79,6 +85,11 @@ export function useWebSocket() {
|
||||
return
|
||||
}
|
||||
|
||||
if (msg.type === 'script_progress' || msg.type === 'script_complete') {
|
||||
handleScriptWsMessage(msg)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle alert/recovery/system
|
||||
if (msg.type === 'alert' || msg.type === 'recovery') {
|
||||
const alert: WsAlert = {
|
||||
@@ -109,6 +120,7 @@ export function useWebSocket() {
|
||||
|
||||
socket.onclose = (event) => {
|
||||
connected.value = false
|
||||
setScriptQueueWsConnected(false)
|
||||
ws.value = null
|
||||
// Don't reconnect on auth failure — token expired
|
||||
if (event.code === 4001 || event.code === 4401) {
|
||||
@@ -136,6 +148,7 @@ export function useWebSocket() {
|
||||
}
|
||||
connected.value = false
|
||||
reconnectAttempts.value = 0
|
||||
setScriptQueueWsConnected(false)
|
||||
}
|
||||
|
||||
function scheduleReconnect() {
|
||||
|
||||
@@ -0,0 +1,592 @@
|
||||
<template>
|
||||
<v-container fluid class="pa-4">
|
||||
<template v-if="detailId">
|
||||
<div class="d-flex align-center flex-wrap ga-2 mb-4">
|
||||
<v-btn variant="text" prepend-icon="mdi-arrow-left" @click="router.push('/script-runs')">
|
||||
返回列表
|
||||
</v-btn>
|
||||
<h1 class="text-h6">执行详情 #{{ detailId }}</h1>
|
||||
<v-chip
|
||||
v-if="execDetail"
|
||||
:color="execStatusColor(execDetail.status)"
|
||||
size="small"
|
||||
variant="tonal"
|
||||
label
|
||||
>
|
||||
{{ execStatusLabel(execDetail.status) }}
|
||||
</v-chip>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
v-if="execDetail?.long_task"
|
||||
size="small"
|
||||
variant="tonal"
|
||||
:loading="detailLoading"
|
||||
@click="loadExecDetail(true)"
|
||||
>
|
||||
拉取日志
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="execDetail && (execDetail.status === 'running' || execDetail.status === 'partial')"
|
||||
size="small"
|
||||
variant="text"
|
||||
color="error"
|
||||
@click="stopCurrent"
|
||||
>
|
||||
停止
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="execDetail && (execDetail.status === 'failed' || execDetail.status === 'partial')"
|
||||
size="small"
|
||||
variant="tonal"
|
||||
color="primary"
|
||||
@click="retryCurrent"
|
||||
>
|
||||
重试失败项
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-card v-if="detailLoading && !execDetail" elevation="0" border rounded="lg" class="pa-8 text-center">
|
||||
<v-progress-circular indeterminate color="primary" />
|
||||
</v-card>
|
||||
|
||||
<v-card v-else-if="execDetail" elevation="0" border rounded="lg">
|
||||
<v-card-text>
|
||||
<div class="text-caption text-medium-emphasis mb-2">
|
||||
{{ formatProgress(execDetail.progress) }} · 操作人 {{ execDetail.operator || '—' }}
|
||||
</div>
|
||||
<pre class="text-body-2 mb-3 pa-3 rounded bg-surface-variant overflow-auto" style="max-height: 100px">{{ execDetail.command }}</pre>
|
||||
|
||||
<div v-if="execDetailStats.total" class="d-flex flex-wrap ga-2 mb-3">
|
||||
<v-chip size="small" variant="tonal" label>共 {{ execDetailStats.total }} 台</v-chip>
|
||||
<v-chip v-if="execDetailStats.success" color="success" size="small" variant="tonal" label>
|
||||
成功 {{ execDetailStats.success }}
|
||||
</v-chip>
|
||||
<v-chip v-if="execDetailStats.failed" color="error" size="small" variant="tonal" label>
|
||||
失败 {{ execDetailStats.failed }}
|
||||
</v-chip>
|
||||
<v-chip v-if="execDetailStats.pending" color="info" size="small" variant="tonal" label>
|
||||
进行中 {{ execDetailStats.pending }}
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<v-alert
|
||||
v-if="execDetailFailedNames.length"
|
||||
type="error"
|
||||
variant="tonal"
|
||||
density="compact"
|
||||
class="mb-3"
|
||||
:title="`失败 ${execDetailFailedNames.length} 台`"
|
||||
>
|
||||
<div class="text-body-2">{{ execDetailFailedNames.join('、') }}</div>
|
||||
</v-alert>
|
||||
|
||||
<div class="d-flex align-center flex-wrap ga-2 mb-3">
|
||||
<span class="text-caption text-medium-emphasis">筛选</span>
|
||||
<v-btn-toggle v-model="detailRowFilter" mandatory density="compact" color="primary" variant="outlined" divided>
|
||||
<v-btn value="all" size="small">全部</v-btn>
|
||||
<v-btn value="failed" size="small">仅失败</v-btn>
|
||||
<v-btn value="success" size="small">仅成功</v-btn>
|
||||
</v-btn-toggle>
|
||||
<template v-if="execDetailStats.failed > 0">
|
||||
<v-divider vertical class="mx-1" />
|
||||
<span class="text-caption text-medium-emphasis">视图</span>
|
||||
<v-btn-toggle v-model="detailViewMode" mandatory density="compact" color="primary" variant="outlined" divided>
|
||||
<v-btn value="list" size="small">列表</v-btn>
|
||||
<v-btn value="group" size="small">按错误分组</v-btn>
|
||||
</v-btn-toggle>
|
||||
<v-btn size="small" variant="tonal" prepend-icon="mdi-content-copy" @click="copyFailedList">
|
||||
复制失败清单
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="detailViewMode === 'group'"
|
||||
size="small"
|
||||
variant="tonal"
|
||||
prepend-icon="mdi-content-copy"
|
||||
@click="copyFailureGroupSummary"
|
||||
>
|
||||
复制分组摘要
|
||||
</v-btn>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<v-table v-if="detailViewMode === 'list'" density="compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>服务器</th>
|
||||
<th>状态</th>
|
||||
<th style="width: 72px">退出码</th>
|
||||
<th>错误摘要</th>
|
||||
<th style="width: 120px" class="text-end">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="row in filteredExecDetailRows"
|
||||
:key="row.serverId"
|
||||
:class="{ 'exec-detail-row--failed': row.failed }"
|
||||
class="cursor-pointer"
|
||||
@click="toggleDetailRow(row.serverId)"
|
||||
>
|
||||
<td>
|
||||
<div class="font-weight-medium">{{ row.serverName }}</div>
|
||||
<div v-if="row.serverDomain" class="text-caption text-medium-emphasis">{{ row.serverDomain }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<v-chip
|
||||
:color="execServerStatusColor(row.status, row.phase, row.exitCode)"
|
||||
size="x-small"
|
||||
variant="tonal"
|
||||
label
|
||||
>
|
||||
{{ execServerStatusLabel(row.status, row.phase) }}
|
||||
</v-chip>
|
||||
</td>
|
||||
<td :class="{ 'text-error font-weight-medium': row.failed }">{{ row.exitCode ?? '—' }}</td>
|
||||
<td class="text-truncate" style="max-width: 280px" :title="row.summary">{{ row.summary }}</td>
|
||||
<td class="text-end" @click.stop>
|
||||
<v-btn
|
||||
size="x-small"
|
||||
variant="text"
|
||||
icon="mdi-console"
|
||||
title="打开终端"
|
||||
@click="openServerTerminal(row.serverIdNum)"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="filteredExecDetailRows.length === 0">
|
||||
<td colspan="5" class="text-center text-medium-emphasis py-4">当前筛选无记录</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
|
||||
<v-expansion-panels
|
||||
v-else-if="execDetailFailureGroups.length"
|
||||
v-model="expandedFailureGroups"
|
||||
multiple
|
||||
variant="accordion"
|
||||
>
|
||||
<v-expansion-panel v-for="(group, idx) in execDetailFailureGroups" :key="`${idx}-${group.errorKey}`">
|
||||
<v-expansion-panel-title>
|
||||
<div class="d-flex align-center ga-2 flex-grow-1 min-width-0">
|
||||
<span class="text-body-2 text-truncate" :title="group.errorKey">{{ group.errorKey }}</span>
|
||||
<v-chip color="error" size="x-small" variant="tonal" label>{{ group.count }} 台</v-chip>
|
||||
</div>
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<div v-for="row in group.rows" :key="row.serverId" class="text-body-2 py-1">
|
||||
{{ row.serverName }}
|
||||
<span v-if="row.serverDomain" class="text-medium-emphasis">({{ row.serverDomain }})</span>
|
||||
</div>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="d-flex align-center flex-wrap ga-2 mb-4">
|
||||
<h1 class="text-h6">脚本执行看板</h1>
|
||||
<v-spacer />
|
||||
<v-btn variant="tonal" prepend-icon="mdi-refresh" :loading="listLoading" @click="loadList">
|
||||
刷新
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-card elevation="0" border rounded="lg">
|
||||
<v-card-text>
|
||||
<v-row dense class="mb-2">
|
||||
<v-col cols="12" sm="4">
|
||||
<v-select
|
||||
v-model="statusFilter"
|
||||
:items="statusFilterOptions"
|
||||
label="状态筛选"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
hide-details
|
||||
clearable
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-data-table-server
|
||||
:headers="listHeaders"
|
||||
:items="executions"
|
||||
:items-length="total"
|
||||
:loading="listLoading"
|
||||
:items-per-page="perPage"
|
||||
:page="page"
|
||||
@update:page="onPageChange"
|
||||
@update:items-per-page="onPerPageChange"
|
||||
>
|
||||
<template #item.label="{ item }">
|
||||
{{ execLabel(item) }}
|
||||
</template>
|
||||
<template #item.status="{ item }">
|
||||
<v-chip :color="execStatusColor(item.status)" size="x-small" variant="tonal" label>
|
||||
{{ execStatusLabel(item.status) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<template #item.progress="{ item }">
|
||||
{{ formatProgress(item.progress) }}
|
||||
</template>
|
||||
<template #item.server_count="{ item }">
|
||||
{{ parseServerCount(item.server_ids) }}
|
||||
</template>
|
||||
<template #item.actions="{ item }">
|
||||
<v-btn size="x-small" variant="text" color="primary" @click="openDetail(item.id)">
|
||||
详情
|
||||
</v-btn>
|
||||
</template>
|
||||
<template #no-data>
|
||||
<div class="text-center text-medium-emphasis py-6">暂无执行记录</div>
|
||||
</template>
|
||||
</v-data-table-server>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { http } from '@/api'
|
||||
import { useSnackbar } from '@/composables/useSnackbar'
|
||||
import { registerScriptExecution } from '@/composables/useScriptExecutionQueue'
|
||||
import { useServerList } from '@/composables/useServerList'
|
||||
import { formatApiError } from '@/utils/apiError'
|
||||
import type { ScriptExecItem } from '@/types/api'
|
||||
import { DATA_TABLE_ITEMS_PER_PAGE_OPTIONS, headersWithoutSort } from '@/constants/dataTable'
|
||||
import { fetchLimitOffset } from '@/utils/paginatedFetch'
|
||||
import {
|
||||
execStatusLabel,
|
||||
execStatusColor,
|
||||
execServerStatusLabel,
|
||||
execServerStatusColor,
|
||||
isExecServerFailed,
|
||||
isExecServerPending,
|
||||
summarizeExecServerResults,
|
||||
} from '@/utils/scriptExecutionLabels'
|
||||
import { groupFailedExecRows } from '@/utils/execDetailGrouping'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const snackbar = useSnackbar()
|
||||
const { servers: serverList, loadServers } = useServerList()
|
||||
|
||||
const detailId = computed(() => {
|
||||
const raw = route.params.id
|
||||
if (!raw) return null
|
||||
const n = Number(raw)
|
||||
return Number.isFinite(n) ? n : null
|
||||
})
|
||||
|
||||
const listLoading = ref(false)
|
||||
const executions = ref<ScriptExecItem[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const perPage = ref(20)
|
||||
const statusFilter = ref<string | null>(null)
|
||||
|
||||
const statusFilterOptions = [
|
||||
{ title: '执行中', value: 'running' },
|
||||
{ title: '已完成', value: 'completed' },
|
||||
{ title: '部分成功', value: 'partial' },
|
||||
{ title: '失败', value: 'failed' },
|
||||
{ title: '已取消', value: 'cancelled' },
|
||||
]
|
||||
|
||||
const listHeaders = headersWithoutSort([
|
||||
{ title: 'ID', key: 'id', width: 72 },
|
||||
{ title: '脚本/命令', key: 'label' },
|
||||
{ title: '状态', key: 'status', width: 100 },
|
||||
{ title: '进度', key: 'progress', width: 88 },
|
||||
{ title: '台数', key: 'server_count', width: 72 },
|
||||
{ title: '操作人', key: 'operator', width: 88 },
|
||||
{ title: '开始时间', key: 'started_at', width: 168 },
|
||||
{ title: '操作', key: 'actions', width: 88 },
|
||||
])
|
||||
|
||||
const execDetail = ref<ScriptExecItem | null>(null)
|
||||
const detailLoading = ref(false)
|
||||
const detailRowFilter = ref<'all' | 'failed' | 'success'>('all')
|
||||
const detailViewMode = ref<'list' | 'group'>('list')
|
||||
const expandedRowId = ref<string | null>(null)
|
||||
const expandedFailureGroups = ref<number[]>([])
|
||||
|
||||
let detailPollTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
interface ExecDetailRow {
|
||||
serverId: string
|
||||
serverIdNum: number
|
||||
serverName: string
|
||||
serverDomain: string
|
||||
status: string
|
||||
phase?: string
|
||||
exitCode: number | null
|
||||
summary: string
|
||||
fullOutput: string
|
||||
failed: boolean
|
||||
pending: boolean
|
||||
}
|
||||
|
||||
const serverBriefById = computed(() => new Map(serverList.value.map(s => [s.id, s])))
|
||||
|
||||
function formatProgress(p: string | undefined) {
|
||||
return p || '—'
|
||||
}
|
||||
|
||||
function parseServerCount(serverIds: string | undefined): number {
|
||||
if (!serverIds) return 0
|
||||
try {
|
||||
const arr = JSON.parse(serverIds) as unknown
|
||||
return Array.isArray(arr) ? arr.length : 0
|
||||
} catch {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
function execLabel(item: ScriptExecItem): string {
|
||||
if (item.label) return item.label
|
||||
const cmd = (item.command || '').replace(/^\[long_task\] /, '')
|
||||
if (cmd.length > 48) return cmd.slice(0, 48) + '…'
|
||||
return cmd || `执行 #${item.id}`
|
||||
}
|
||||
|
||||
function buildExecOutput(entry: ScriptExecItem['results'][string]): { summary: string; full: string } {
|
||||
const stderr = (entry.stderr || '').trim()
|
||||
const stdout = (entry.stdout || '').trim()
|
||||
const message = (entry.message || '').trim()
|
||||
const full = stderr || stdout || message || '—'
|
||||
const summary = (stderr || message || stdout || '').slice(0, 200) || '—'
|
||||
return { summary, full }
|
||||
}
|
||||
|
||||
const execDetailRows = computed<ExecDetailRow[]>(() => {
|
||||
if (!execDetail.value?.results) return []
|
||||
const briefs = serverBriefById.value
|
||||
return Object.entries(execDetail.value.results)
|
||||
.filter(([k]) => k !== '_meta' && /^\d+$/.test(k))
|
||||
.map(([serverId, entry]) => {
|
||||
const idNum = Number(serverId)
|
||||
const brief = briefs.get(idNum)
|
||||
const status = String(entry.status || '')
|
||||
const phase = entry.phase ? String(entry.phase) : undefined
|
||||
const exitCode = entry.exit_code ?? null
|
||||
const output = buildExecOutput(entry)
|
||||
return {
|
||||
serverId,
|
||||
serverIdNum: idNum,
|
||||
serverName: brief?.name || `服务器 #${serverId}`,
|
||||
serverDomain: brief?.domain || '',
|
||||
status,
|
||||
phase,
|
||||
exitCode,
|
||||
summary: output.summary,
|
||||
fullOutput: output.full,
|
||||
failed: isExecServerFailed(status, phase, exitCode),
|
||||
pending: isExecServerPending(phase),
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const filteredExecDetailRows = computed(() => {
|
||||
if (detailRowFilter.value === 'failed') return execDetailRows.value.filter(r => r.failed)
|
||||
if (detailRowFilter.value === 'success') return execDetailRows.value.filter(r => !r.failed && !r.pending)
|
||||
return execDetailRows.value
|
||||
})
|
||||
|
||||
const execDetailStats = computed(() =>
|
||||
summarizeExecServerResults(execDetail.value?.results, execDetail.value?.server_ids),
|
||||
)
|
||||
|
||||
const execDetailFailedNames = computed(() =>
|
||||
execDetailRows.value.filter(r => r.failed).map(r => r.serverName),
|
||||
)
|
||||
|
||||
const execDetailFailureGroups = computed(() =>
|
||||
groupFailedExecRows(execDetailRows.value.filter(r => r.failed)),
|
||||
)
|
||||
|
||||
async function loadList() {
|
||||
listLoading.value = true
|
||||
try {
|
||||
const res = await fetchLimitOffset<ScriptExecItem>(
|
||||
async (limit, offset) => {
|
||||
const params: Record<string, unknown> = { limit, offset }
|
||||
if (statusFilter.value) params.status = statusFilter.value
|
||||
const data = await http.get<{ items: ScriptExecItem[]; total: number }>('/scripts/executions', params)
|
||||
return { items: data.items || [], total: data.total || 0 }
|
||||
},
|
||||
page.value,
|
||||
perPage.value,
|
||||
)
|
||||
executions.value = res.items
|
||||
total.value = res.total
|
||||
} catch (e: unknown) {
|
||||
executions.value = []
|
||||
total.value = 0
|
||||
snackbar(formatApiError(e, '加载失败'), 'error')
|
||||
} finally {
|
||||
listLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onPageChange(p: number) {
|
||||
page.value = p
|
||||
loadList()
|
||||
}
|
||||
|
||||
function onPerPageChange(n: number) {
|
||||
perPage.value = n
|
||||
page.value = 1
|
||||
loadList()
|
||||
}
|
||||
|
||||
function openDetail(id: number) {
|
||||
router.push(`/script-runs/${id}`)
|
||||
}
|
||||
|
||||
async function loadExecDetail(fetchLogs = false) {
|
||||
if (!detailId.value) return
|
||||
detailLoading.value = true
|
||||
try {
|
||||
const q = fetchLogs ? '?fetch_logs=true' : ''
|
||||
execDetail.value = await http.get<ScriptExecItem>(`/scripts/executions/${detailId.value}${q}`)
|
||||
if (execDetail.value?.status === 'running') {
|
||||
startDetailPoll()
|
||||
} else {
|
||||
stopDetailPoll()
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
snackbar(formatApiError(e, '加载详情失败'), 'error')
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function startDetailPoll() {
|
||||
if (detailPollTimer) return
|
||||
detailPollTimer = setInterval(() => {
|
||||
void loadExecDetail(false)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
function stopDetailPoll() {
|
||||
if (detailPollTimer) {
|
||||
clearInterval(detailPollTimer)
|
||||
detailPollTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDetailRow(serverId: string) {
|
||||
expandedRowId.value = expandedRowId.value === serverId ? null : serverId
|
||||
}
|
||||
|
||||
function openServerTerminal(serverId: number) {
|
||||
router.push({ path: '/terminal', query: { server: String(serverId) } })
|
||||
}
|
||||
|
||||
async function stopCurrent() {
|
||||
if (!detailId.value) return
|
||||
try {
|
||||
await http.post(`/scripts/executions/${detailId.value}/stop`)
|
||||
await loadExecDetail(false)
|
||||
snackbar('已发送停止指令')
|
||||
} catch (e: unknown) {
|
||||
snackbar(formatApiError(e, '停止失败'), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
async function retryCurrent() {
|
||||
if (!detailId.value) return
|
||||
try {
|
||||
const res = await http.post<ScriptExecItem>(`/scripts/executions/${detailId.value}/retry`, {})
|
||||
const count = parseServerCount(res.server_ids)
|
||||
registerScriptExecution(
|
||||
res.id,
|
||||
`${execLabel(res)}(重试)`,
|
||||
count,
|
||||
Boolean(res.long_task),
|
||||
res.progress,
|
||||
res.status,
|
||||
)
|
||||
snackbar(`重试已提交 #${res.id}`)
|
||||
router.push(`/script-runs/${res.id}`)
|
||||
} catch (e: unknown) {
|
||||
snackbar(formatApiError(e, '重试失败'), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
function formatFailedRowLine(row: ExecDetailRow): string {
|
||||
const domain = row.serverDomain ? ` (${row.serverDomain})` : ''
|
||||
const code = row.exitCode != null ? ` · exit ${row.exitCode}` : ''
|
||||
const line = row.summary.split('\n')[0]?.trim() || row.summary
|
||||
const summary = line && line !== '—' ? ` · ${line.slice(0, 120)}` : ''
|
||||
return `- ${row.serverName}${domain}${code}${summary}`
|
||||
}
|
||||
|
||||
async function copyTextToClipboard(text: string, successMsg: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
snackbar(successMsg)
|
||||
} catch {
|
||||
snackbar('复制失败', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
async function copyFailedList() {
|
||||
const failed = execDetailRows.value.filter(r => r.failed)
|
||||
if (!failed.length) return
|
||||
const lines = [
|
||||
`执行 #${detailId.value} · 失败 ${failed.length} 台`,
|
||||
...failed.map(formatFailedRowLine),
|
||||
]
|
||||
await copyTextToClipboard(lines.join('\n'), '已复制失败清单')
|
||||
}
|
||||
|
||||
async function copyFailureGroupSummary() {
|
||||
const groups = execDetailFailureGroups.value
|
||||
if (!groups.length) return
|
||||
const lines = [`执行 #${detailId.value} · 失败分组 ${groups.length} 类`]
|
||||
for (const g of groups) {
|
||||
lines.push(`\n[${g.count} 台] ${g.errorKey}`)
|
||||
for (const row of g.rows.slice(0, 20)) {
|
||||
lines.push(` - ${row.serverName}`)
|
||||
}
|
||||
if (g.rows.length > 20) lines.push(` …及其他 ${g.rows.length - 20} 台`)
|
||||
}
|
||||
await copyTextToClipboard(lines.join('\n'), '已复制分组摘要')
|
||||
}
|
||||
|
||||
watch(statusFilter, () => {
|
||||
page.value = 1
|
||||
loadList()
|
||||
})
|
||||
|
||||
watch(detailId, (id) => {
|
||||
stopDetailPoll()
|
||||
execDetail.value = null
|
||||
if (id) {
|
||||
detailRowFilter.value = 'all'
|
||||
detailViewMode.value = 'list'
|
||||
void loadExecDetail(false)
|
||||
} else {
|
||||
loadList()
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
onMounted(() => {
|
||||
loadServers()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopDetailPoll()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.exec-detail-row--failed {
|
||||
background: rgba(var(--v-theme-error), 0.06);
|
||||
}
|
||||
</style>
|
||||
@@ -284,67 +284,6 @@
|
||||
</v-card>
|
||||
</div>
|
||||
|
||||
<!-- Execution Status Panel -->
|
||||
<v-card v-if="activeTrackedExecs.length > 0" elevation="0" border rounded="lg" class="mt-4">
|
||||
<v-card-title class="d-flex align-center text-subtitle-1">
|
||||
<v-icon class="mr-2">mdi-progress-clock</v-icon>
|
||||
批量执行状态
|
||||
<v-spacer />
|
||||
<v-btn size="small" variant="text" @click="trackedExecs = []">清除</v-btn>
|
||||
</v-card-title>
|
||||
<v-divider />
|
||||
<v-card-text>
|
||||
<v-list density="compact">
|
||||
<v-list-item v-for="exec in activeTrackedExecs" :key="exec.id" :title="exec.name">
|
||||
<template #subtitle>
|
||||
<span class="text-caption">
|
||||
#{{ exec.id }} · {{ exec.progress || '—' }} · {{ exec.totalServers }} 台
|
||||
<span v-if="exec.longTask" class="ml-1">· 长任务</span>
|
||||
</span>
|
||||
</template>
|
||||
<template #append>
|
||||
<div class="d-flex align-center flex-wrap ga-1 justify-end">
|
||||
<v-chip :color="execStatusColor(exec.status)" size="x-small" variant="tonal" label>
|
||||
{{ execStatusLabel(exec.status) }}
|
||||
</v-chip>
|
||||
<v-btn size="x-small" variant="text" @click="openExecDetail(exec.id)">详情</v-btn>
|
||||
<v-btn size="x-small" variant="text" @click="refreshExecStatus(exec)" :loading="exec.refreshing">
|
||||
刷新
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="exec.status === 'running'"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
color="warning"
|
||||
@click="markExecStuck(exec)"
|
||||
>
|
||||
卡住
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="exec.status === 'running' || exec.status === 'partial'"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
color="error"
|
||||
@click="stopExec(exec)"
|
||||
>
|
||||
停止
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="exec.status === 'failed' || exec.status === 'partial'"
|
||||
size="x-small"
|
||||
variant="text"
|
||||
color="primary"
|
||||
@click="retryExec(exec)"
|
||||
>
|
||||
重试
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- Editor Dialog -->
|
||||
<v-dialog v-model="showEditor" max-width="900" scrollable>
|
||||
<v-card border>
|
||||
@@ -683,7 +622,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { ref, computed, watch, onMounted, nextTick } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import ScriptContentEditor from '@/components/ScriptContentEditor.vue'
|
||||
import ServerCategoryPicker from '@/components/servers/ServerCategoryPicker.vue'
|
||||
@@ -706,6 +645,7 @@ import {
|
||||
summarizeExecServerResults,
|
||||
} from '@/utils/scriptExecutionLabels'
|
||||
import { groupFailedExecRows } from '@/utils/execDetailGrouping'
|
||||
import { registerScriptExecution } from '@/composables/useScriptExecutionQueue'
|
||||
|
||||
const dataTablePageOptions = [...DATA_TABLE_ITEMS_PER_PAGE_OPTIONS]
|
||||
const snackbar = useSnackbar()
|
||||
@@ -725,16 +665,6 @@ interface ExecConfig {
|
||||
max_timeout: number
|
||||
}
|
||||
|
||||
interface TrackedExec {
|
||||
id: number
|
||||
name: string
|
||||
status: string
|
||||
totalServers: number
|
||||
progress: string
|
||||
longTask: boolean
|
||||
refreshing: boolean
|
||||
}
|
||||
|
||||
interface ExecDetailRow {
|
||||
serverId: string
|
||||
serverIdNum: number
|
||||
@@ -792,14 +722,6 @@ const quickLongTask = ref(false)
|
||||
const quickCredentialId = ref<number | null>(null)
|
||||
const quickExecuting = ref(false)
|
||||
|
||||
const trackedExecs = ref<TrackedExec[]>([])
|
||||
let pollTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
/** 批量执行状态面板仅展示进行中任务(running) */
|
||||
const activeTrackedExecs = computed(() =>
|
||||
trackedExecs.value.filter(e => e.status === 'running'),
|
||||
)
|
||||
|
||||
const showExecDetail = ref(false)
|
||||
const execDetail = ref<ScriptExecItem | null>(null)
|
||||
const detailLoading = ref(false)
|
||||
@@ -1143,7 +1065,14 @@ function runScript(script: ScriptItem) {
|
||||
async function submitExec(payload: Record<string, unknown>, label: string, serverCount: number) {
|
||||
const res = await http.post<ScriptExecItem & { execution_id?: number }>('/scripts/exec', payload)
|
||||
const id = executionId(res)
|
||||
addTrackedExecution(label, id, serverCount, Boolean(payload.long_task), res.progress, res.status)
|
||||
registerScriptExecution(
|
||||
id,
|
||||
label,
|
||||
serverCount,
|
||||
Boolean(payload.long_task),
|
||||
res.progress,
|
||||
res.status,
|
||||
)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -1216,109 +1145,6 @@ async function doQuickExec() {
|
||||
}
|
||||
}
|
||||
|
||||
function pruneTerminalTrackedExecs() {
|
||||
trackedExecs.value = trackedExecs.value.filter(e => !isExecTerminalStatus(e.status))
|
||||
}
|
||||
|
||||
function addTrackedExecution(
|
||||
name: string,
|
||||
execId: number,
|
||||
totalServers: number,
|
||||
longTask: boolean,
|
||||
progress?: string,
|
||||
status?: string,
|
||||
) {
|
||||
const initialStatus = status || 'running'
|
||||
if (isExecTerminalStatus(initialStatus)) return
|
||||
if (trackedExecs.value.some(e => e.id === execId)) return
|
||||
trackedExecs.value.unshift({
|
||||
id: execId,
|
||||
name,
|
||||
status: initialStatus,
|
||||
totalServers,
|
||||
progress: progress || `0/${totalServers}`,
|
||||
longTask,
|
||||
refreshing: false,
|
||||
})
|
||||
startPolling()
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
if (pollTimer) return
|
||||
pollTimer = setInterval(async () => {
|
||||
const active = trackedExecs.value.filter(e => e.status === 'running')
|
||||
if (active.length === 0) {
|
||||
pruneTerminalTrackedExecs()
|
||||
stopPolling()
|
||||
return
|
||||
}
|
||||
for (const exec of active) await refreshExecStatus(exec)
|
||||
}, 10_000)
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (pollTimer) {
|
||||
clearInterval(pollTimer)
|
||||
pollTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshExecStatus(exec: TrackedExec) {
|
||||
exec.refreshing = true
|
||||
try {
|
||||
const res = await http.get<ScriptExecItem>(`/scripts/executions/${exec.id}`)
|
||||
exec.status = res.status
|
||||
exec.progress = formatProgress(res.progress)
|
||||
exec.longTask = Boolean(res.long_task)
|
||||
if (isExecTerminalStatus(res.status)) {
|
||||
const name = exec.name
|
||||
const failed = res.status === 'failed' || res.status === 'partial'
|
||||
trackedExecs.value = trackedExecs.value.filter(e => e.id !== exec.id)
|
||||
refreshExecutionHistory()
|
||||
if (failed) {
|
||||
snackbar(`「${name}」已结束,请查看下方执行历史`, 'warning')
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* keep last known */
|
||||
} finally {
|
||||
exec.refreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
async function stopExec(exec: TrackedExec) {
|
||||
try {
|
||||
await http.post(`/scripts/executions/${exec.id}/stop`)
|
||||
await refreshExecStatus(exec)
|
||||
snackbar('已发送停止指令')
|
||||
} catch (e: unknown) {
|
||||
snackbar(formatApiError(e, '停止失败'), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
async function markExecStuck(exec: TrackedExec) {
|
||||
try {
|
||||
await http.post(`/scripts/executions/${exec.id}/mark-stuck`, { detail: '用户在脚本库标记可能卡住' })
|
||||
snackbar('已标记')
|
||||
} catch (e: unknown) {
|
||||
snackbar(formatApiError(e, '标记失败'), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
async function retryExec(exec: TrackedExec) {
|
||||
try {
|
||||
const res = await http.post<ScriptExecItem & { parent_execution_id?: number }>(
|
||||
`/scripts/executions/${exec.id}/retry`,
|
||||
{},
|
||||
)
|
||||
const newId = executionId(res)
|
||||
addTrackedExecution(`${exec.name}(重试)`, newId, exec.totalServers, exec.longTask, res.progress, res.status)
|
||||
snackbar(`已创建重试任务 #${newId}`)
|
||||
} catch (e: unknown) {
|
||||
snackbar(formatApiError(e, '重试失败'), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
async function stopHistoryExec(item: ScriptExecItem) {
|
||||
try {
|
||||
await http.post(`/scripts/executions/${item.id}/stop`)
|
||||
@@ -1332,7 +1158,14 @@ async function stopHistoryExec(item: ScriptExecItem) {
|
||||
async function retryHistoryExec(item: ScriptExecItem) {
|
||||
try {
|
||||
const res = await http.post<ScriptExecItem>(`/scripts/executions/${item.id}/retry`, {})
|
||||
addTrackedExecution(`${execLabel(item)}(重试)`, executionId(res), parseServerCount(item.server_ids) || parseServerCount(res.server_ids), Boolean(item.long_task), res.progress, res.status)
|
||||
registerScriptExecution(
|
||||
executionId(res),
|
||||
`${execLabel(item)}(重试)`,
|
||||
parseServerCount(item.server_ids) || parseServerCount(res.server_ids),
|
||||
Boolean(item.long_task),
|
||||
res.progress,
|
||||
res.status,
|
||||
)
|
||||
refreshExecutionHistory()
|
||||
snackbar('重试已提交')
|
||||
} catch (e: unknown) {
|
||||
@@ -1446,9 +1279,6 @@ onMounted(() => {
|
||||
refreshExecutionHistory()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopPolling()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -47,6 +47,42 @@
|
||||
|
||||
<TerminalQuickCommandsSettings />
|
||||
|
||||
<v-card elevation="0" border rounded="lg" class="mb-4">
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2">mdi-bell-ring-outline</v-icon>
|
||||
界面提示
|
||||
</v-card-title>
|
||||
<v-divider />
|
||||
<v-card-text>
|
||||
<p class="text-caption text-medium-emphasis mb-3">
|
||||
脚本批量执行全部结束时,在浏览器播放提示音(仅当前登录的管理员会话,与 Telegram 推送无关)。
|
||||
</p>
|
||||
<v-select
|
||||
v-model="scriptExecCompleteSound"
|
||||
:items="scriptCompleteSoundOptions"
|
||||
item-title="title"
|
||||
item-value="value"
|
||||
label="脚本执行完成提示音"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
hide-details
|
||||
class="mb-3"
|
||||
:loading="scriptSoundSaving"
|
||||
:disabled="scriptSoundSaving"
|
||||
@update:model-value="onScriptExecSoundChange"
|
||||
/>
|
||||
<v-btn
|
||||
size="small"
|
||||
variant="tonal"
|
||||
prepend-icon="mdi-volume-high"
|
||||
:disabled="scriptExecCompleteSound === 'off' || scriptSoundSaving"
|
||||
@click="previewScriptExecSound"
|
||||
>
|
||||
试听
|
||||
</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- Telegram Settings -->
|
||||
<v-card elevation="0" border rounded="lg">
|
||||
<v-card-title class="d-flex align-center">
|
||||
@@ -378,11 +414,23 @@ import type {
|
||||
IpAllowlistSaveRequest,
|
||||
} from '@/types/api'
|
||||
import { NOTIFY_TOGGLE_ITEMS } from '@/types/api'
|
||||
import {
|
||||
SCRIPT_COMPLETE_SOUND_OPTIONS,
|
||||
DEFAULT_SCRIPT_COMPLETE_SOUND,
|
||||
normalizeScriptCompleteSound,
|
||||
type ScriptCompleteSoundId,
|
||||
} from '@/utils/scriptCompleteSound'
|
||||
import { useScriptCompleteSound } from '@/composables/useScriptCompleteSound'
|
||||
|
||||
const snackbar = useSnackbar()
|
||||
const { saveToServer: saveScriptSoundToServer, previewSound } = useScriptCompleteSound()
|
||||
|
||||
const auth = useAuthStore()
|
||||
|
||||
const scriptCompleteSoundOptions = SCRIPT_COMPLETE_SOUND_OPTIONS
|
||||
const scriptExecCompleteSound = ref<ScriptCompleteSoundId>(DEFAULT_SCRIPT_COMPLETE_SOUND)
|
||||
const scriptSoundSaving = ref(false)
|
||||
|
||||
// ── Settings ──
|
||||
const settingsLoading = ref(false)
|
||||
const settings = ref({
|
||||
@@ -468,6 +516,10 @@ async function loadSettings() {
|
||||
telegramTokenDraft.value = ''
|
||||
continue
|
||||
}
|
||||
if (key === 'script_exec_complete_sound') {
|
||||
scriptExecCompleteSound.value = normalizeScriptCompleteSound(item.value)
|
||||
continue
|
||||
}
|
||||
if (key && NOTIFY_TOGGLE_ITEMS.some(n => n.key === key)) {
|
||||
notifyToggles.value[key] = parseNotifyEnabled(item.value)
|
||||
continue
|
||||
@@ -598,6 +650,26 @@ async function saveTelegramConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
async function onScriptExecSoundChange(value: ScriptCompleteSoundId | null) {
|
||||
if (!value || scriptSoundSaving.value) return
|
||||
const prev = scriptExecCompleteSound.value
|
||||
scriptExecCompleteSound.value = value
|
||||
scriptSoundSaving.value = true
|
||||
try {
|
||||
await saveScriptSoundToServer(value)
|
||||
snackbar('提示音设置已保存')
|
||||
} catch (e: unknown) {
|
||||
scriptExecCompleteSound.value = prev
|
||||
snackbar(e instanceof Error ? e.message : '保存失败', 'error')
|
||||
} finally {
|
||||
scriptSoundSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function previewScriptExecSound() {
|
||||
previewSound(scriptExecCompleteSound.value)
|
||||
}
|
||||
|
||||
async function toggleNotify(key: string, enabled: boolean) {
|
||||
const prev = notifyToggles.value[key]
|
||||
notifyToggles.value[key] = enabled
|
||||
|
||||
@@ -8,6 +8,8 @@ const routes = [
|
||||
{ path: '/files', name: 'Files', component: () => import('@/pages/FilesPage.vue') },
|
||||
{ path: '/push', name: 'Push', component: () => import('@/pages/PushPage.vue') },
|
||||
{ path: '/scripts', name: 'Scripts', component: () => import('@/pages/ScriptsPage.vue') },
|
||||
{ path: '/script-runs', name: 'ScriptRuns', component: () => import('@/pages/ScriptRunsPage.vue') },
|
||||
{ path: '/script-runs/:id', name: 'ScriptRunDetail', component: () => import('@/pages/ScriptRunsPage.vue') },
|
||||
{ path: '/credentials', name: 'Credentials',component: () => import('@/pages/CredentialsPage.vue') },
|
||||
{ path: '/schedules', name: 'Schedules', component: () => import('@/pages/SchedulesPage.vue') },
|
||||
{ path: '/retries', name: 'Retries', component: () => import('@/pages/RetriesPage.vue') },
|
||||
|
||||
@@ -79,6 +79,7 @@ export const NOTIFY_TOGGLE_ITEMS: NotifyToggleItem[] = [
|
||||
{ key: 'notify_system_redis', label: 'Redis 连接异常 / 恢复', desc: 'Redis 不可达或恢复时推送', group: '系统' },
|
||||
{ key: 'notify_system_mysql', label: 'MySQL 连接异常 / 恢复', desc: 'MySQL 不可达或恢复时推送', group: '系统' },
|
||||
{ key: 'notify_restart', label: 'Nexus 后端重启', desc: 'Python 服务崩溃重启成功/失败时推送', group: '系统' },
|
||||
{ key: 'notify_script_complete', label: '脚本执行完成', desc: '脚本批量执行结束时推送汇总与看板链接(不含失败明细)', group: '运维' },
|
||||
]
|
||||
|
||||
/** IP Allowlist response */
|
||||
@@ -399,8 +400,9 @@ export interface ScriptExecItem {
|
||||
completed_at: string | null
|
||||
long_task?: boolean
|
||||
progress?: string
|
||||
label?: string
|
||||
credential_id?: number | null
|
||||
source: string
|
||||
source?: string
|
||||
}
|
||||
|
||||
/** Alert history stats from /api/alert-history/stats */
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/** Script execution complete sound presets (Web Audio API). */
|
||||
|
||||
export type ScriptCompleteSoundId = 'off' | 'beep_short' | 'beep_double' | 'chime_soft'
|
||||
|
||||
export const SCRIPT_COMPLETE_SOUND_OPTIONS: Array<{ value: ScriptCompleteSoundId; title: string }> = [
|
||||
{ value: 'off', title: '关闭' },
|
||||
{ value: 'beep_short', title: '短促单音' },
|
||||
{ value: 'beep_double', title: '双音提示(默认)' },
|
||||
{ value: 'chime_soft', title: '柔和三音' },
|
||||
]
|
||||
|
||||
export const DEFAULT_SCRIPT_COMPLETE_SOUND: ScriptCompleteSoundId = 'beep_double'
|
||||
|
||||
const VALID_IDS = new Set(SCRIPT_COMPLETE_SOUND_OPTIONS.map(o => o.value))
|
||||
|
||||
export function normalizeScriptCompleteSound(raw: unknown): ScriptCompleteSoundId {
|
||||
const s = String(raw ?? '').trim().toLowerCase()
|
||||
if (VALID_IDS.has(s as ScriptCompleteSoundId)) {
|
||||
return s as ScriptCompleteSoundId
|
||||
}
|
||||
return DEFAULT_SCRIPT_COMPLETE_SOUND
|
||||
}
|
||||
|
||||
function tone(
|
||||
ctx: AudioContext,
|
||||
freq: number,
|
||||
startAt: number,
|
||||
duration: number,
|
||||
gain = 0.08,
|
||||
) {
|
||||
const osc = ctx.createOscillator()
|
||||
const g = ctx.createGain()
|
||||
osc.type = 'sine'
|
||||
osc.frequency.value = freq
|
||||
g.gain.value = gain
|
||||
osc.connect(g)
|
||||
g.connect(ctx.destination)
|
||||
osc.start(startAt)
|
||||
osc.stop(startAt + duration)
|
||||
}
|
||||
|
||||
export function playScriptCompleteSoundById(mode: ScriptCompleteSoundId): void {
|
||||
if (mode === 'off') return
|
||||
try {
|
||||
const ctx = new AudioContext()
|
||||
const t0 = ctx.currentTime
|
||||
if (mode === 'beep_short') {
|
||||
tone(ctx, 880, t0, 0.12)
|
||||
return
|
||||
}
|
||||
if (mode === 'beep_double') {
|
||||
tone(ctx, 880, t0, 0.15)
|
||||
tone(ctx, 1174, t0 + 0.12, 0.2)
|
||||
return
|
||||
}
|
||||
if (mode === 'chime_soft') {
|
||||
tone(ctx, 523.25, t0, 0.1, 0.06)
|
||||
tone(ctx, 659.25, t0 + 0.1, 0.1, 0.06)
|
||||
tone(ctx, 783.99, t0 + 0.2, 0.14, 0.06)
|
||||
}
|
||||
} catch {
|
||||
/* non-critical */
|
||||
}
|
||||
}
|
||||
@@ -50,10 +50,17 @@ MUTABLE_KEYS: set[str] = {
|
||||
"notify_alert_cpu", "notify_alert_mem", "notify_alert_disk",
|
||||
"notify_recovery", "notify_time_drift",
|
||||
"notify_system_redis", "notify_system_mysql", "notify_restart",
|
||||
"notify_script_complete",
|
||||
"script_exec_complete_sound", # 脚本执行完成浏览器提示音 (off|beep_short|beep_double|chime_soft)
|
||||
"editor_theme", # Monaco editor theme preference
|
||||
"theme", # 全站主题 (dark/light)
|
||||
}
|
||||
|
||||
SETTING_ENUM_VALIDATORS: dict[str, frozenset[str]] = {
|
||||
"script_exec_complete_sound": frozenset({"off", "beep_short", "beep_double", "chime_soft"}),
|
||||
"theme": frozenset({"dark", "light"}),
|
||||
}
|
||||
|
||||
# S-02: Per-key validation rules: (type, min, max) for int settings
|
||||
SETTING_VALIDATORS: dict[str, tuple] = {
|
||||
"db_pool_size": (int, 1, 1000),
|
||||
@@ -339,6 +346,14 @@ async def set_setting(
|
||||
raise HTTPException(status_code=400, detail=f"'{key}' 不能为空")
|
||||
return _format_setting_response(key, current, None)
|
||||
|
||||
if key in SETTING_ENUM_VALIDATORS:
|
||||
allowed = SETTING_ENUM_VALIDATORS[key]
|
||||
if val_str not in allowed:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"'{key}' 必须是: {', '.join(sorted(allowed))}",
|
||||
)
|
||||
|
||||
# S-02: Validate value type and range for int settings
|
||||
if key in SETTING_VALIDATORS:
|
||||
expected_type, min_val, max_val = SETTING_VALIDATORS[key]
|
||||
|
||||
@@ -523,6 +523,18 @@ async def broadcast_system_event(event_type: str, message: str):
|
||||
await _dispatch_ws_message(msg)
|
||||
|
||||
|
||||
async def broadcast_script_progress(payload: dict):
|
||||
"""Broadcast script execution batch progress to /ws/alerts subscribers."""
|
||||
msg = {"type": "script_progress", **payload}
|
||||
await _dispatch_ws_message(msg)
|
||||
|
||||
|
||||
async def broadcast_script_complete(payload: dict):
|
||||
"""Broadcast script execution terminal state to /ws/alerts subscribers."""
|
||||
msg = {"type": "script_complete", **payload}
|
||||
await _dispatch_ws_message(msg)
|
||||
|
||||
|
||||
async def broadcast_sync_progress(
|
||||
batch_id: str, server_id: int, server_name: str,
|
||||
status: str, completed: int, failed: int, total: int,
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
"""WebSocket + Telegram notifications for script execution progress."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
from server.config import settings
|
||||
from server.domain.repositories import ScriptExecutionRepository, ScriptRepository
|
||||
from server.infrastructure.redis import script_execution_store as ses
|
||||
|
||||
logger = logging.getLogger("nexus.script_execution_notify")
|
||||
|
||||
|
||||
def _script_board_url(execution_id: int) -> str:
|
||||
base = (settings.API_BASE_URL or "").strip().rstrip("/")
|
||||
if not base:
|
||||
return f"/app/#/script-runs/{execution_id}"
|
||||
return f"{base}/app/#/script-runs/{execution_id}"
|
||||
|
||||
|
||||
async def _resolve_label(
|
||||
live: dict[str, Any],
|
||||
script_repo: Optional[ScriptRepository] = None,
|
||||
) -> str:
|
||||
label = (live.get("label") or "").strip()
|
||||
if label:
|
||||
return label
|
||||
script_id = live.get("script_id")
|
||||
if script_id and script_repo:
|
||||
script = await script_repo.get_by_id(int(script_id))
|
||||
if script and script.name:
|
||||
return script.name
|
||||
cmd = str(live.get("command") or "")
|
||||
if cmd.startswith("[long_task] "):
|
||||
cmd = cmd[len("[long_task] "):]
|
||||
if len(cmd) > 48:
|
||||
return cmd[:48] + "…"
|
||||
return cmd or f"执行 #{live.get('id')}"
|
||||
|
||||
|
||||
def _ws_payload(live: dict[str, Any], label: str) -> dict[str, Any]:
|
||||
results = live.get("results") or {}
|
||||
server_ids = live.get("server_ids") or []
|
||||
stats = ses.execution_progress_stats(results, server_ids)
|
||||
return {
|
||||
"execution_id": int(live["id"]),
|
||||
"label": label,
|
||||
"status": live.get("status") or "running",
|
||||
"long_task": bool(live.get("long_task")),
|
||||
"operator": live.get("operator") or "",
|
||||
"script_id": live.get("script_id"),
|
||||
**stats,
|
||||
}
|
||||
|
||||
|
||||
async def notify_script_progress(
|
||||
live: dict[str, Any],
|
||||
*,
|
||||
script_repo: Optional[ScriptRepository] = None,
|
||||
) -> None:
|
||||
"""Broadcast running progress (throttle at caller for per-server updates)."""
|
||||
try:
|
||||
from server.api.websocket import broadcast_script_progress
|
||||
|
||||
label = await _resolve_label(live, script_repo)
|
||||
await broadcast_script_progress(_ws_payload(live, label))
|
||||
except Exception as e:
|
||||
logger.warning("script progress notify failed: %s", e)
|
||||
|
||||
|
||||
async def notify_script_complete(
|
||||
live: dict[str, Any],
|
||||
*,
|
||||
script_repo: Optional[ScriptRepository] = None,
|
||||
) -> None:
|
||||
"""Broadcast terminal state + optional Telegram summary."""
|
||||
status = live.get("status") or ""
|
||||
if status not in ses.TERMINAL_STATUSES:
|
||||
return
|
||||
try:
|
||||
from server.api.websocket import broadcast_script_complete
|
||||
|
||||
label = await _resolve_label(live, script_repo)
|
||||
payload = _ws_payload(live, label)
|
||||
await broadcast_script_complete(payload)
|
||||
|
||||
from server.infrastructure.telegram import send_telegram_script_summary
|
||||
|
||||
results = live.get("results") or {}
|
||||
server_ids = live.get("server_ids") or []
|
||||
stats = ses.execution_progress_stats(results, server_ids)
|
||||
await send_telegram_script_summary(
|
||||
execution_id=int(live["id"]),
|
||||
script_label=label,
|
||||
operator=str(live.get("operator") or "admin"),
|
||||
status=status,
|
||||
success_count=int(stats["success"]),
|
||||
failed_count=int(stats["failed"]),
|
||||
total=int(stats["total"]),
|
||||
board_url=_script_board_url(int(live["id"])),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning("script complete notify failed: %s", e)
|
||||
|
||||
|
||||
async def notify_script_complete_for_execution(
|
||||
execution_repo: ScriptExecutionRepository,
|
||||
execution_id: int,
|
||||
*,
|
||||
script_repo: Optional[ScriptRepository] = None,
|
||||
) -> None:
|
||||
view = await ses.get_execution_view(execution_repo, execution_id)
|
||||
if not view:
|
||||
return
|
||||
raw_ids = view.get("server_ids")
|
||||
if isinstance(raw_ids, str):
|
||||
try:
|
||||
server_ids = json.loads(raw_ids or "[]")
|
||||
except json.JSONDecodeError:
|
||||
server_ids = []
|
||||
else:
|
||||
server_ids = raw_ids or []
|
||||
live = {
|
||||
"id": view["id"],
|
||||
"script_id": view.get("script_id"),
|
||||
"command": view.get("command"),
|
||||
"server_ids": server_ids,
|
||||
"status": view.get("status"),
|
||||
"results": view.get("results") or {},
|
||||
"operator": view.get("operator"),
|
||||
"long_task": view.get("long_task", False),
|
||||
"label": view.get("label"),
|
||||
}
|
||||
await notify_script_complete(live, script_repo=script_repo)
|
||||
|
||||
@@ -88,7 +88,7 @@ async def apply_script_job_callback(
|
||||
status = _aggregate_execution_status(server_ids, results)
|
||||
terminal = status in ses.TERMINAL_STATUSES
|
||||
|
||||
await ses.apply_update(
|
||||
live = await ses.apply_update(
|
||||
execution_repo,
|
||||
execution_id,
|
||||
status=status,
|
||||
@@ -102,6 +102,15 @@ async def apply_script_job_callback(
|
||||
terminal=terminal,
|
||||
)
|
||||
|
||||
if terminal:
|
||||
from server.application.services.script_execution_notify import notify_script_complete
|
||||
|
||||
await notify_script_complete(live)
|
||||
else:
|
||||
from server.application.services.script_execution_notify import notify_script_progress
|
||||
|
||||
await notify_script_progress(live)
|
||||
|
||||
return {
|
||||
"execution_id": execution_id,
|
||||
"server_id": server_id,
|
||||
|
||||
@@ -20,6 +20,8 @@ from server.domain.repositories import (
|
||||
|
||||
logger = logging.getLogger("nexus.script_service")
|
||||
|
||||
_BG_EXEC_TASKS: set[asyncio.Task] = set()
|
||||
|
||||
_NEXUS_JOB_LOG_RE = re.compile(r"log=(/var/log/nexus-job/[^\s\"']+)")
|
||||
_NEXUS_JOB_PID_RE = re.compile(r"pid=(\d+)")
|
||||
|
||||
@@ -95,15 +97,9 @@ class ScriptService:
|
||||
# Resolve DB credential variables (if provided)
|
||||
resolved_command = await self._substitute_db_vars(command, credential_id)
|
||||
|
||||
from server.application.services.script_jobs import (
|
||||
build_long_task_command,
|
||||
master_callback_url,
|
||||
register_script_job,
|
||||
)
|
||||
from server.application.services.script_jobs import master_callback_url
|
||||
|
||||
callback_url = None
|
||||
if long_task:
|
||||
callback_url = master_callback_url()
|
||||
callback_url = master_callback_url() if long_task else None
|
||||
|
||||
# Create execution record — use sanitized command (password redacted)
|
||||
sanitized_command = await self._substitute_db_vars(command, credential_id, redact=True)
|
||||
@@ -118,7 +114,10 @@ class ScriptService:
|
||||
operator=operator,
|
||||
)
|
||||
execution = await self.execution_repo.create(execution)
|
||||
await ses.init_live_from_execution(execution, long_task=long_task)
|
||||
label = await self._execution_label(script_id, command)
|
||||
live = await ses.init_live_from_execution(execution, long_task=long_task)
|
||||
live["label"] = label
|
||||
await ses.save_live(live)
|
||||
await self._audit(
|
||||
"execute_started", "script_execution", execution.id,
|
||||
f"开始执行 {len(server_ids)} 台"
|
||||
@@ -126,6 +125,132 @@ class ScriptService:
|
||||
operator=operator,
|
||||
)
|
||||
|
||||
self._schedule_background_execution(
|
||||
execution_id=execution.id,
|
||||
resolved_command=resolved_command,
|
||||
server_ids=list(server_ids),
|
||||
timeout=timeout,
|
||||
operator=operator,
|
||||
long_task=long_task,
|
||||
callback_url=callback_url,
|
||||
label=label,
|
||||
)
|
||||
return await self.execution_repo.get_by_id(execution.id)
|
||||
|
||||
@staticmethod
|
||||
def _schedule_background_execution(**kwargs: Any) -> None:
|
||||
task = asyncio.create_task(ScriptService._run_background_execution(**kwargs))
|
||||
_BG_EXEC_TASKS.add(task)
|
||||
task.add_done_callback(_BG_EXEC_TASKS.discard)
|
||||
|
||||
@staticmethod
|
||||
async def _run_background_execution(
|
||||
*,
|
||||
execution_id: int,
|
||||
resolved_command: str,
|
||||
server_ids: List[int],
|
||||
timeout: int,
|
||||
operator: str,
|
||||
long_task: bool,
|
||||
callback_url: Optional[str],
|
||||
label: str,
|
||||
) -> None:
|
||||
from server.infrastructure.database.session import AsyncSessionLocal
|
||||
from server.infrastructure.database.script_repo import (
|
||||
ScriptRepositoryImpl,
|
||||
ScriptExecutionRepositoryImpl,
|
||||
)
|
||||
from server.infrastructure.database.db_credential_repo import DbCredentialRepositoryImpl
|
||||
from server.infrastructure.database.server_repo import ServerRepositoryImpl
|
||||
from server.infrastructure.database.audit_log_repo import AuditLogRepositoryImpl
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
service = ScriptService(
|
||||
script_repo=ScriptRepositoryImpl(session),
|
||||
execution_repo=ScriptExecutionRepositoryImpl(session),
|
||||
credential_repo=DbCredentialRepositoryImpl(session),
|
||||
server_repo=ServerRepositoryImpl(session),
|
||||
audit_repo=AuditLogRepositoryImpl(session),
|
||||
)
|
||||
try:
|
||||
await service._run_execution_batches(
|
||||
execution_id=execution_id,
|
||||
resolved_command=resolved_command,
|
||||
server_ids=server_ids,
|
||||
timeout=timeout,
|
||||
operator=operator,
|
||||
long_task=long_task,
|
||||
callback_url=callback_url,
|
||||
label=label,
|
||||
)
|
||||
await session.commit()
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"Background script execution failed (execution_id=%s)", execution_id,
|
||||
)
|
||||
await session.rollback()
|
||||
try:
|
||||
await service._mark_execution_catastrophic_failure(
|
||||
execution_id, operator, label,
|
||||
)
|
||||
await session.commit()
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"Failed to mark catastrophic failure for execution_id=%s",
|
||||
execution_id,
|
||||
)
|
||||
await session.rollback()
|
||||
|
||||
async def _mark_execution_catastrophic_failure(
|
||||
self,
|
||||
execution_id: int,
|
||||
operator: str,
|
||||
label: str,
|
||||
) -> None:
|
||||
from server.application.services.script_execution_notify import notify_script_complete
|
||||
|
||||
view = await ses.get_execution_view(self.execution_repo, execution_id)
|
||||
results = dict((view or {}).get("results") or {})
|
||||
live = await ses.apply_update(
|
||||
self.execution_repo,
|
||||
execution_id,
|
||||
status="failed",
|
||||
results=results,
|
||||
event_action="execution_failed",
|
||||
event_detail="后台执行异常终止",
|
||||
operator=operator,
|
||||
terminal=True,
|
||||
)
|
||||
live["label"] = label
|
||||
await ses.save_live(live)
|
||||
await notify_script_complete(live, script_repo=self.script_repo)
|
||||
await self._audit(
|
||||
"execute_command", "script_execution", execution_id,
|
||||
"后台执行异常终止",
|
||||
operator=operator,
|
||||
)
|
||||
|
||||
async def _run_execution_batches(
|
||||
self,
|
||||
*,
|
||||
execution_id: int,
|
||||
resolved_command: str,
|
||||
server_ids: List[int],
|
||||
timeout: int,
|
||||
operator: str,
|
||||
long_task: bool,
|
||||
callback_url: Optional[str],
|
||||
label: str,
|
||||
) -> None:
|
||||
from server.application.services.script_jobs import (
|
||||
build_long_task_command,
|
||||
register_script_job,
|
||||
)
|
||||
from server.application.services.script_execution_notify import (
|
||||
notify_script_progress,
|
||||
notify_script_complete,
|
||||
)
|
||||
|
||||
batch_size = max(1, settings.SCRIPT_EXEC_BATCH_SIZE)
|
||||
results: Dict[str, dict] = {}
|
||||
batches = [
|
||||
@@ -139,14 +264,14 @@ class ScriptService:
|
||||
batch_idx,
|
||||
len(batches),
|
||||
len(batch),
|
||||
execution.id,
|
||||
execution_id,
|
||||
)
|
||||
per_server_commands: Optional[Dict[int, str]] = None
|
||||
job_ids_by_server: Dict[int, str] = {}
|
||||
if long_task:
|
||||
per_server_commands = {}
|
||||
for sid in batch:
|
||||
job_id, secret = await register_script_job(execution.id, sid)
|
||||
job_id, secret = await register_script_job(execution_id, sid)
|
||||
job_ids_by_server[sid] = job_id
|
||||
agent_key = await self._agent_api_key_for_server(sid)
|
||||
per_server_commands[sid] = build_long_task_command(
|
||||
@@ -156,15 +281,15 @@ class ScriptService:
|
||||
batch,
|
||||
resolved_command,
|
||||
timeout,
|
||||
execution_id=execution.id,
|
||||
execution_id=execution_id,
|
||||
per_server_commands=per_server_commands,
|
||||
long_task=long_task,
|
||||
job_ids_by_server=job_ids_by_server or None,
|
||||
)
|
||||
results.update(batch_results)
|
||||
await ses.apply_update(
|
||||
live = await ses.apply_update(
|
||||
self.execution_repo,
|
||||
execution.id,
|
||||
execution_id,
|
||||
status="running",
|
||||
results=results,
|
||||
event_action="batch_progress",
|
||||
@@ -173,8 +298,10 @@ class ScriptService:
|
||||
terminal=False,
|
||||
long_task=long_task,
|
||||
)
|
||||
live["label"] = label
|
||||
await ses.save_live(live)
|
||||
await notify_script_progress(live, script_repo=self.script_repo)
|
||||
|
||||
# Determine overall status
|
||||
if long_task:
|
||||
start_failed = sum(
|
||||
1 for r in results.values() if r.get("exit_code", -1) != 0
|
||||
@@ -196,9 +323,9 @@ class ScriptService:
|
||||
)
|
||||
|
||||
terminal = status in ses.TERMINAL_STATUSES
|
||||
await ses.apply_update(
|
||||
live = await ses.apply_update(
|
||||
self.execution_repo,
|
||||
execution.id,
|
||||
execution_id,
|
||||
status=status,
|
||||
results=results,
|
||||
event_action="execution_finished",
|
||||
@@ -207,13 +334,26 @@ class ScriptService:
|
||||
terminal=terminal,
|
||||
long_task=long_task,
|
||||
)
|
||||
live["label"] = label
|
||||
await ses.save_live(live)
|
||||
|
||||
await self._audit(
|
||||
"execute_command", "script_execution", execution.id,
|
||||
"execute_command", "script_execution", execution_id,
|
||||
f"执行于 {len(server_ids)} 台: {status} ({failed_count} 台失败)",
|
||||
operator=operator,
|
||||
)
|
||||
return await self.execution_repo.get_by_id(execution.id)
|
||||
if terminal:
|
||||
await notify_script_complete(live, script_repo=self.script_repo)
|
||||
|
||||
async def _execution_label(self, script_id: Optional[int], command: str) -> str:
|
||||
if script_id:
|
||||
script = await self.script_repo.get_by_id(script_id)
|
||||
if script and script.name:
|
||||
return script.name
|
||||
text = (command or "").strip()
|
||||
if len(text) > 48:
|
||||
return text[:48] + "…"
|
||||
return text or "临时命令"
|
||||
|
||||
async def get_execution(self, id: int) -> Optional[ScriptExecution]:
|
||||
return await self.execution_repo.get_by_id(id)
|
||||
@@ -285,6 +425,7 @@ class ScriptService:
|
||||
"completed_at": view.get("completed_at"),
|
||||
"long_task": view.get("long_task", False),
|
||||
"progress": view.get("progress"),
|
||||
"label": view.get("label"),
|
||||
"source": view.get("source"),
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ class Settings(BaseSettings):
|
||||
NOTIFY_SYSTEM_REDIS: str = "true" # Redis 连接异常/恢复
|
||||
NOTIFY_SYSTEM_MYSQL: str = "true" # MySQL 连接异常/恢复
|
||||
NOTIFY_RESTART: str = "true" # Nexus 后端重启通知
|
||||
NOTIFY_SCRIPT_COMPLETE: str = "true" # 脚本执行完成汇总(含看板链接)
|
||||
|
||||
# extra=ignore: .env 中与 MCP 共存的 MYSQL_* / NEXUS_API_BASE_URL 等不进入 Settings
|
||||
model_config = SettingsConfigDict(
|
||||
@@ -146,6 +147,7 @@ class Settings(BaseSettings):
|
||||
"notify_system_redis": "NOTIFY_SYSTEM_REDIS",
|
||||
"notify_system_mysql": "NOTIFY_SYSTEM_MYSQL",
|
||||
"notify_restart": "NOTIFY_RESTART",
|
||||
"notify_script_complete": "NOTIFY_SCRIPT_COMPLETE",
|
||||
}
|
||||
|
||||
# Settings that require int conversion from DB string values
|
||||
|
||||
@@ -55,6 +55,47 @@ def _is_server_exec_success(entry: Any) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _is_server_exec_done(entry: Any) -> bool:
|
||||
"""True when a server has a non-pending result (counts toward done/remaining)."""
|
||||
if not isinstance(entry, dict) or not entry:
|
||||
return False
|
||||
phase = str(entry.get("phase") or "").strip()
|
||||
if phase == "pending":
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def execution_progress_stats(
|
||||
results: dict[str, Any],
|
||||
server_ids: Optional[list] = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Counts for UI / WebSocket: done, remaining, success, failed, total, progress."""
|
||||
ids = server_ids
|
||||
if ids is None:
|
||||
ids = [int(k) for k in results if k != "_meta" and str(k).isdigit()]
|
||||
total = len(ids)
|
||||
done = 0
|
||||
success = 0
|
||||
failed_done = 0
|
||||
for sid in ids:
|
||||
entry = results.get(str(sid))
|
||||
if not _is_server_exec_done(entry):
|
||||
continue
|
||||
done += 1
|
||||
if _is_server_exec_success(entry):
|
||||
success += 1
|
||||
else:
|
||||
failed_done += 1
|
||||
return {
|
||||
"total": total,
|
||||
"done": done,
|
||||
"remaining": max(0, total - done),
|
||||
"success": success,
|
||||
"failed": failed_done,
|
||||
"progress": execution_progress_summary(results, ids),
|
||||
}
|
||||
|
||||
|
||||
def execution_progress_summary(
|
||||
results: dict[str, Any],
|
||||
server_ids: Optional[list] = None,
|
||||
@@ -278,6 +319,7 @@ async def get_execution_view(
|
||||
"long_task": live.get("long_task", False),
|
||||
"progress": execution_progress_summary(results, live.get("server_ids")),
|
||||
"credential_id": live.get("credential_id"),
|
||||
"label": live.get("label"),
|
||||
"source": "redis",
|
||||
}
|
||||
|
||||
|
||||
@@ -277,3 +277,43 @@ async def send_telegram_sync_complete(
|
||||
lines.append(f"时间:{_now_cn()}")
|
||||
|
||||
return await send_telegram("\n".join(lines))
|
||||
|
||||
|
||||
async def send_telegram_script_summary(
|
||||
execution_id: int,
|
||||
script_label: str,
|
||||
operator: str,
|
||||
status: str,
|
||||
success_count: int,
|
||||
failed_count: int,
|
||||
total: int,
|
||||
board_url: str,
|
||||
) -> bool:
|
||||
"""Send script execution summary (no per-server failure list) via Telegram."""
|
||||
if not _notify_enabled(getattr(settings, "NOTIFY_SCRIPT_COMPLETE", "true")):
|
||||
return False
|
||||
|
||||
safe_label = html.escape(sanitize_external_message(script_label, max_len=80))
|
||||
safe_operator = html.escape(operator)
|
||||
safe_url = html.escape(board_url)
|
||||
|
||||
if status == "completed":
|
||||
icon = "🟢"
|
||||
title = "脚本执行完成"
|
||||
elif status == "failed":
|
||||
icon = "🔴"
|
||||
title = "脚本执行失败"
|
||||
else:
|
||||
icon = "🟡"
|
||||
title = "脚本执行部分失败"
|
||||
|
||||
lines = [
|
||||
f"{icon} <b>【{title}】</b>",
|
||||
f"平台:{_system_name()}",
|
||||
f"脚本:{safe_label} · 执行 #{execution_id}",
|
||||
f"统计:成功 {success_count} 台 / 失败 {failed_count} 台 / 总计 {total} 台",
|
||||
f"操作人:{safe_operator}",
|
||||
f"详情:<a href=\"{safe_url}\">打开执行看板</a>",
|
||||
f"时间:{_now_cn()}",
|
||||
]
|
||||
return await send_telegram("\n".join(lines))
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Tests for script execution progress stats (done/remaining vs success/total)."""
|
||||
|
||||
from server.infrastructure.redis.script_execution_store import execution_progress_stats
|
||||
|
||||
|
||||
def test_progress_stats_empty_results():
|
||||
stats = execution_progress_stats({}, [1, 2, 3])
|
||||
assert stats["total"] == 3
|
||||
assert stats["done"] == 0
|
||||
assert stats["remaining"] == 3
|
||||
assert stats["success"] == 0
|
||||
assert stats["progress"] == "0/3"
|
||||
|
||||
|
||||
def test_progress_stats_mixed_success_and_failure():
|
||||
results = {
|
||||
"1": {"exit_code": 0, "status": "success"},
|
||||
"2": {"exit_code": 1, "status": "failed"},
|
||||
"3": {"phase": "pending"},
|
||||
}
|
||||
stats = execution_progress_stats(results, [1, 2, 3])
|
||||
assert stats["done"] == 2
|
||||
assert stats["remaining"] == 1
|
||||
assert stats["success"] == 1
|
||||
assert stats["failed"] == 1
|
||||
assert stats["progress"] == "1/3"
|
||||
|
||||
|
||||
def test_progress_stats_long_task_pending_not_done():
|
||||
results = {
|
||||
"10": {"phase": "pending", "exit_code": 0},
|
||||
"11": {"phase": "done", "exit_code": 0, "status": "completed"},
|
||||
}
|
||||
stats = execution_progress_stats(results, [10, 11])
|
||||
assert stats["done"] == 1
|
||||
assert stats["remaining"] == 1
|
||||
assert stats["success"] == 1
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,Er as t,F as n,N as r,Nr as i,Ur as a,Z as o,Zn as s,_r as c,ar as l,er as u,hr as d,k as f,or as p,rr as m,sr as h,tr as g,w as _,yr as v,zr as y,zt as b}from"./index-HeC-ymfd.js";import{o as x,t as S}from"./VContainer-Bqb4OsjX.js";import{n as C}from"./dataTable-esDY4cOk.js";import{t as w}from"./VChip-C-HpH-oj.js";import{t as T}from"./VSelect-CEXOntEU.js";import{t as E}from"./VDataTableServer-_AO1gvgs.js";import{n as D,t as O}from"./VRow-w4SO-kR5.js";import{t as k}from"./VSpacer-C31iLuvS.js";var A={class:`font-weight-medium`},j={class:`text-body-2`},M={class:`text-caption text-medium-emphasis`},N=h({__name:`AlertsPage`,setup(h){let N=x(),P=i(!1),F=i([]),I=i(0),L=i(1),R=i(null),z=i(null),B=i([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),V=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],H=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],U=C([{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}]);async function W(){try{let e=await b.get(`/alert-history/stats`);B.value[0].value=String(e.today||0),B.value[1].value=String(e.active||0),B.value[2].value=String(e.recovered||0),B.value[3].value=e.top_server||`—`}catch{N(`加载统计失败`,`error`)}}async function G(){P.value=!0;try{let e=await b.get(`/alert-history/`,{page:L.value,per_page:20,type:R.value||void 0,status:z.value||void 0});F.value=e.items||[],I.value=e.total||0}catch{F.value=[]}finally{P.value=!1}}function K(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function q(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{W(),G()}),(i,d)=>(c(),g(S,{fluid:``,class:`pa-6`},{default:t(()=>[p(O,{class:`mb-4`},{default:t(()=>[(c(!0),m(s,null,v(B.value,n=>(c(),g(D,{cols:`12`,sm:`6`,lg:`3`,key:n.label},{default:t(()=>[p(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:t(()=>[p(f,null,{append:t(()=>[p(o,{color:n.color,size:`30`},{default:t(()=>[l(a(n.icon),1)]),_:2},1032,[`color`])]),default:t(()=>[p(e,{class:`text-body-small`},{default:t(()=>[l(a(n.label),1)]),_:2},1024),p(e,null,{default:t(()=>[l(a(n.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),p(r,{elevation:`0`,border:``,rounded:`lg`},{default:t(()=>[p(n,{class:`d-flex align-center`},{default:t(()=>[d[5]||=l(` 告警历史 `,-1),p(k),p(T,{modelValue:R.value,"onUpdate:modelValue":[d[0]||=e=>R.value=e,d[1]||=e=>{L.value=1,G()}],items:V,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),p(T,{modelValue:z.value,"onUpdate:modelValue":[d[2]||=e=>z.value=e,d[3]||=e=>{L.value=1,G()}],items:H,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),p(E,{items:F.value,headers:y(U),"items-length":I.value,loading:P.value,page:L.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{L.value=e,G()}},{"item.alert_type":t(({item:e})=>[p(w,{color:K(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:t(()=>[p(o,{size:`12`},{default:t(()=>[l(a(q(e.alert_type)),1)]),_:2},1024)]),default:t(()=>[l(` `+a(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":t(({item:e})=>[u(`span`,A,a(e.server_name),1)]),"item.value":t(({item:e})=>[u(`span`,j,a(e.value),1)]),"item.is_recovery":t(({item:e})=>[e.is_recovery?(c(),g(w,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[6]||=[l(`已恢复`,-1)]]),_:1})):(c(),g(w,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[7]||=[l(`告警中`,-1)]]),_:1}))]),"item.created_at":t(({item:e})=>[u(`span`,M,a(e.created_at),1)]),"no-data":t(()=>[...d[8]||=[u(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{N as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as e,t,v as n}from"./VSelect-BBtNL0eJ.js";import{n as r,t as i}from"./VRow-sbIKY3fI.js";import{A as a,B as o,Cr as s,F as c,Gr as l,Mr as u,N as d,R as f,T as p,Tr as m,Yr as h,dr as g,fr as _,j as v,lr as y,or as b,pr as x,qt as S,rr as C,sr as w,ut as T,xr as E,zr as D}from"./index-DXg_pn7T.js";import{n as O}from"./dataTable-esDY4cOk.js";import{t as k}from"./VDataTableServer-CAOCfrD0.js";var A={class:`font-weight-medium`},j={class:`text-body-2`},M={class:`text-caption text-medium-emphasis`},N=x({__name:`AlertsPage`,setup(x){let N=e(),P=D(!1),F=D([]),I=D(0),L=D(1),R=D(null),z=D(null),B=D([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),V=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],H=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],U=O([{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}]);async function W(){try{let e=await S.get(`/alert-history/stats`);B.value[0].value=String(e.today||0),B.value[1].value=String(e.active||0),B.value[2].value=String(e.recovered||0),B.value[3].value=e.top_server||`—`}catch{N(`加载统计失败`,`error`)}}async function G(){P.value=!0;try{let e=await S.get(`/alert-history/`,{page:L.value,per_page:20,type:R.value||void 0,status:z.value||void 0});F.value=e.items||[],I.value=e.total||0}catch{F.value=[]}finally{P.value=!1}}function K(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function q(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return E(()=>{W(),G()}),(e,x)=>(s(),w(n,{fluid:``,class:`pa-6`},{default:u(()=>[_(i,{class:`mb-4`},{default:u(()=>[(s(!0),y(C,null,m(B.value,e=>(s(),w(r,{cols:`12`,sm:`6`,lg:`3`,key:e.label},{default:u(()=>[_(p,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:u(()=>[_(a,null,{append:u(()=>[_(T,{color:e.color,size:`30`},{default:u(()=>[g(h(e.icon),1)]),_:2},1032,[`color`])]),default:u(()=>[_(v,{class:`text-body-small`},{default:u(()=>[g(h(e.label),1)]),_:2},1024),_(v,null,{default:u(()=>[g(h(e.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),_(f,{elevation:`0`,border:``,rounded:`lg`},{default:u(()=>[_(o,{class:`d-flex align-center`},{default:u(()=>[x[5]||=g(` 告警历史 `,-1),_(d),_(t,{modelValue:R.value,"onUpdate:modelValue":[x[0]||=e=>R.value=e,x[1]||=e=>{L.value=1,G()}],items:V,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),_(t,{modelValue:z.value,"onUpdate:modelValue":[x[2]||=e=>z.value=e,x[3]||=e=>{L.value=1,G()}],items:H,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),_(k,{items:F.value,headers:l(U),"items-length":I.value,loading:P.value,page:L.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":x[4]||=e=>{L.value=e,G()}},{"item.alert_type":u(({item:e})=>[_(c,{color:K(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:u(()=>[_(T,{size:`12`},{default:u(()=>[g(h(q(e.alert_type)),1)]),_:2},1024)]),default:u(()=>[g(` `+h(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":u(({item:e})=>[b(`span`,A,h(e.server_name),1)]),"item.value":u(({item:e})=>[b(`span`,j,h(e.value),1)]),"item.is_recovery":u(({item:e})=>[e.is_recovery?(s(),w(c,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:u(()=>[...x[6]||=[g(`已恢复`,-1)]]),_:1})):(s(),w(c,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:u(()=>[...x[7]||=[g(`告警中`,-1)]]),_:1}))]),"item.created_at":u(({item:e})=>[b(`span`,M,h(e.created_at),1)]),"no-data":u(()=>[...x[8]||=[b(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{N as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{$n as e,A as t,Ar as n,Br as r,Cr as i,F as a,N as o,Rt as s,X as c,Xn as l,_r as u,ar as d,er as f,hr as p,ir as m,k as h,nr as g,or as _,pr as v,w as y}from"./index-Da-oPxMv.js";import{o as b,t as x}from"./VContainer-Dp-8rduj.js";import{t as S}from"./VChip-uNN5BiCx.js";import{t as C}from"./VSelect-B1u55nVH.js";import{t as w}from"./VDataTableServer-BLAQDDBi.js";import{n as T,t as E}from"./VRow-Rr6vqjSj.js";import{t as D}from"./VSpacer-ZMhNXvUl.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=_({__name:`AlertsPage`,setup(_){let j=b(),M=n(!1),N=n([]),P=n(0),F=n(1),I=n(null),L=n(null),R=n([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await s.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await s.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return v(()=>{H(),U()}),(n,s)=>(p(),f(x,{fluid:``,class:`pa-6`},{default:i(()=>[d(E,{class:`mb-4`},{default:i(()=>[(p(!0),g(l,null,u(R.value,e=>(p(),f(T,{cols:`12`,sm:`6`,lg:`3`,key:e.label},{default:i(()=>[d(y,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:i(()=>[d(h,null,{append:i(()=>[d(c,{color:e.color,size:`30`},{default:i(()=>[m(r(e.icon),1)]),_:2},1032,[`color`])]),default:i(()=>[d(t,{class:`text-body-small`},{default:i(()=>[m(r(e.label),1)]),_:2},1024),d(t,null,{default:i(()=>[m(r(e.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),d(o,{elevation:`0`,border:``,rounded:`lg`},{default:i(()=>[d(a,{class:`d-flex align-center`},{default:i(()=>[s[5]||=m(` 告警历史 `,-1),d(D),d(C,{modelValue:I.value,"onUpdate:modelValue":[s[0]||=e=>I.value=e,s[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),d(C,{modelValue:L.value,"onUpdate:modelValue":[s[2]||=e=>L.value=e,s[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),d(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":s[4]||=e=>{F.value=e,U()}},{"item.alert_type":i(({item:e})=>[d(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:i(()=>[d(c,{size:`12`},{default:i(()=>[m(r(G(e.alert_type)),1)]),_:2},1024)]),default:i(()=>[m(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":i(({item:t})=>[e(`span`,O,r(t.server_name),1)]),"item.value":i(({item:t})=>[e(`span`,k,r(t.value),1)]),"item.is_recovery":i(({item:e})=>[e.is_recovery?(p(),f(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:i(()=>[...s[6]||=[m(`已恢复`,-1)]]),_:1})):(p(),f(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:i(()=>[...s[7]||=[m(`告警中`,-1)]]),_:1}))]),"item.created_at":i(({item:t})=>[e(`span`,A,r(t.created_at),1)]),"no-data":i(()=>[...s[8]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,Er as t,F as n,N as r,Nr as i,Ur as a,Z as o,Zn as s,_r as c,ar as l,er as u,hr as d,k as f,or as p,rr as m,sr as h,tr as g,w as _,yr as v,zr as y,zt as b}from"./index-Hwt84p4T.js";import{o as x,t as S}from"./VContainer-DVdhFFDK.js";import{n as C}from"./dataTable-esDY4cOk.js";import{t as w}from"./VChip-BOlm6rKc.js";import{t as T}from"./VSelect-Ce9_iAcL.js";import{t as E}from"./VDataTableServer-Ch0R1ufY.js";import{n as D,t as O}from"./VRow-CW1Gj3Lm.js";import{t as k}from"./VSpacer-Dz9VwXyE.js";var A={class:`font-weight-medium`},j={class:`text-body-2`},M={class:`text-caption text-medium-emphasis`},N=h({__name:`AlertsPage`,setup(h){let N=x(),P=i(!1),F=i([]),I=i(0),L=i(1),R=i(null),z=i(null),B=i([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),V=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],H=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],U=C([{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}]);async function W(){try{let e=await b.get(`/alert-history/stats`);B.value[0].value=String(e.today||0),B.value[1].value=String(e.active||0),B.value[2].value=String(e.recovered||0),B.value[3].value=e.top_server||`—`}catch{N(`加载统计失败`,`error`)}}async function G(){P.value=!0;try{let e=await b.get(`/alert-history/`,{page:L.value,per_page:20,type:R.value||void 0,status:z.value||void 0});F.value=e.items||[],I.value=e.total||0}catch{F.value=[]}finally{P.value=!1}}function K(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function q(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{W(),G()}),(i,d)=>(c(),g(S,{fluid:``,class:`pa-6`},{default:t(()=>[p(O,{class:`mb-4`},{default:t(()=>[(c(!0),m(s,null,v(B.value,n=>(c(),g(D,{cols:`12`,sm:`6`,lg:`3`,key:n.label},{default:t(()=>[p(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:t(()=>[p(f,null,{append:t(()=>[p(o,{color:n.color,size:`30`},{default:t(()=>[l(a(n.icon),1)]),_:2},1032,[`color`])]),default:t(()=>[p(e,{class:`text-body-small`},{default:t(()=>[l(a(n.label),1)]),_:2},1024),p(e,null,{default:t(()=>[l(a(n.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),p(r,{elevation:`0`,border:``,rounded:`lg`},{default:t(()=>[p(n,{class:`d-flex align-center`},{default:t(()=>[d[5]||=l(` 告警历史 `,-1),p(k),p(T,{modelValue:R.value,"onUpdate:modelValue":[d[0]||=e=>R.value=e,d[1]||=e=>{L.value=1,G()}],items:V,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),p(T,{modelValue:z.value,"onUpdate:modelValue":[d[2]||=e=>z.value=e,d[3]||=e=>{L.value=1,G()}],items:H,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),p(E,{items:F.value,headers:y(U),"items-length":I.value,loading:P.value,page:L.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{L.value=e,G()}},{"item.alert_type":t(({item:e})=>[p(w,{color:K(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:t(()=>[p(o,{size:`12`},{default:t(()=>[l(a(q(e.alert_type)),1)]),_:2},1024)]),default:t(()=>[l(` `+a(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":t(({item:e})=>[u(`span`,A,a(e.server_name),1)]),"item.value":t(({item:e})=>[u(`span`,j,a(e.value),1)]),"item.is_recovery":t(({item:e})=>[e.is_recovery?(c(),g(w,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[6]||=[l(`已恢复`,-1)]]),_:1})):(c(),g(w,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[7]||=[l(`告警中`,-1)]]),_:1}))]),"item.created_at":t(({item:e})=>[u(`span`,M,a(e.created_at),1)]),"no-data":t(()=>[...d[8]||=[u(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{N as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,F as t,N as n,Vr as r,Z as i,Zn as a,ar as o,er as s,gr as c,jr as l,k as u,mr as d,or as f,rr as p,sr as m,tr as h,vr as g,w as _,wr as v,zt as y}from"./index-DhXUzl5h.js";import{o as b,t as x}from"./VContainer-CrR_anyV.js";import{t as S}from"./VChip-Bj2Bn9Ad.js";import{t as C}from"./VSelect-B8ti2NSE.js";import{t as w}from"./VDataTableServer-DGuK8add.js";import{n as T,t as E}from"./VRow-C2jem5w2.js";import{t as D}from"./VSpacer-DhDsDhMR.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=m({__name:`AlertsPage`,setup(m){let j=b(),M=l(!1),N=l([]),P=l(0),F=l(1),I=l(null),L=l(null),R=l([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(l,d)=>(c(),h(x,{fluid:``,class:`pa-6`},{default:v(()=>[f(E,{class:`mb-4`},{default:v(()=>[(c(!0),p(a,null,g(R.value,t=>(c(),h(T,{cols:`12`,sm:`6`,lg:`3`,key:t.label},{default:v(()=>[f(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:v(()=>[f(u,null,{append:v(()=>[f(i,{color:t.color,size:`30`},{default:v(()=>[o(r(t.icon),1)]),_:2},1032,[`color`])]),default:v(()=>[f(e,{class:`text-body-small`},{default:v(()=>[o(r(t.label),1)]),_:2},1024),f(e,null,{default:v(()=>[o(r(t.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),f(n,{elevation:`0`,border:``,rounded:`lg`},{default:v(()=>[f(t,{class:`d-flex align-center`},{default:v(()=>[d[5]||=o(` 告警历史 `,-1),f(D),f(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),f(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),f(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":v(({item:e})=>[f(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:v(()=>[f(i,{size:`12`},{default:v(()=>[o(r(G(e.alert_type)),1)]),_:2},1024)]),default:v(()=>[o(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":v(({item:e})=>[s(`span`,O,r(e.server_name),1)]),"item.value":v(({item:e})=>[s(`span`,k,r(e.value),1)]),"item.is_recovery":v(({item:e})=>[e.is_recovery?(c(),h(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[6]||=[o(`已恢复`,-1)]]),_:1})):(c(),h(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[7]||=[o(`告警中`,-1)]]),_:1}))]),"item.created_at":v(({item:e})=>[s(`span`,A,r(e.created_at),1)]),"no-data":v(()=>[...d[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,F as t,N as n,Vr as r,Z as i,Zn as a,ar as o,er as s,gr as c,jr as l,k as u,mr as d,or as f,rr as p,sr as m,tr as h,vr as g,w as _,wr as v,zt as y}from"./index-gfOrpdmy.js";import{o as b,t as x}from"./VContainer-DoAVnyqr.js";import{t as S}from"./VChip-sW1zi7V6.js";import{t as C}from"./VSelect-KxRF8W0i.js";import{t as w}from"./VDataTableServer-LfNDokVe.js";import{n as T,t as E}from"./VRow-AEPVE_-F.js";import{t as D}from"./VSpacer-ChMx8Hym.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=m({__name:`AlertsPage`,setup(m){let j=b(),M=l(!1),N=l([]),P=l(0),F=l(1),I=l(null),L=l(null),R=l([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(l,d)=>(c(),h(x,{fluid:``,class:`pa-6`},{default:v(()=>[f(E,{class:`mb-4`},{default:v(()=>[(c(!0),p(a,null,g(R.value,t=>(c(),h(T,{cols:`12`,sm:`6`,lg:`3`,key:t.label},{default:v(()=>[f(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:v(()=>[f(u,null,{append:v(()=>[f(i,{color:t.color,size:`30`},{default:v(()=>[o(r(t.icon),1)]),_:2},1032,[`color`])]),default:v(()=>[f(e,{class:`text-body-small`},{default:v(()=>[o(r(t.label),1)]),_:2},1024),f(e,null,{default:v(()=>[o(r(t.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),f(n,{elevation:`0`,border:``,rounded:`lg`},{default:v(()=>[f(t,{class:`d-flex align-center`},{default:v(()=>[d[5]||=o(` 告警历史 `,-1),f(D),f(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),f(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),f(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":v(({item:e})=>[f(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:v(()=>[f(i,{size:`12`},{default:v(()=>[o(r(G(e.alert_type)),1)]),_:2},1024)]),default:v(()=>[o(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":v(({item:e})=>[s(`span`,O,r(e.server_name),1)]),"item.value":v(({item:e})=>[s(`span`,k,r(e.value),1)]),"item.is_recovery":v(({item:e})=>[e.is_recovery?(c(),h(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[6]||=[o(`已恢复`,-1)]]),_:1})):(c(),h(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[7]||=[o(`告警中`,-1)]]),_:1}))]),"item.created_at":v(({item:e})=>[s(`span`,A,r(e.created_at),1)]),"no-data":v(()=>[...d[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,F as t,N as n,Vr as r,Z as i,Zn as a,ar as o,er as s,gr as c,jr as l,k as u,mr as d,or as f,rr as p,sr as m,tr as h,vr as g,w as _,wr as v,zt as y}from"./index-ia8w6hh3.js";import{o as b,t as x}from"./VContainer-Cg-YgCG2.js";import{t as S}from"./VChip-gd2nTpu3.js";import{t as C}from"./VSelect-DesLFoAg.js";import{t as w}from"./VDataTableServer-D4zQCwj2.js";import{n as T,t as E}from"./VRow-DRmOLWn-.js";import{t as D}from"./VSpacer-BtntPGL0.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=m({__name:`AlertsPage`,setup(m){let j=b(),M=l(!1),N=l([]),P=l(0),F=l(1),I=l(null),L=l(null),R=l([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(l,d)=>(c(),h(x,{fluid:``,class:`pa-6`},{default:v(()=>[f(E,{class:`mb-4`},{default:v(()=>[(c(!0),p(a,null,g(R.value,t=>(c(),h(T,{cols:`12`,sm:`6`,lg:`3`,key:t.label},{default:v(()=>[f(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:v(()=>[f(u,null,{append:v(()=>[f(i,{color:t.color,size:`30`},{default:v(()=>[o(r(t.icon),1)]),_:2},1032,[`color`])]),default:v(()=>[f(e,{class:`text-body-small`},{default:v(()=>[o(r(t.label),1)]),_:2},1024),f(e,null,{default:v(()=>[o(r(t.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),f(n,{elevation:`0`,border:``,rounded:`lg`},{default:v(()=>[f(t,{class:`d-flex align-center`},{default:v(()=>[d[5]||=o(` 告警历史 `,-1),f(D),f(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),f(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),f(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":v(({item:e})=>[f(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:v(()=>[f(i,{size:`12`},{default:v(()=>[o(r(G(e.alert_type)),1)]),_:2},1024)]),default:v(()=>[o(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":v(({item:e})=>[s(`span`,O,r(e.server_name),1)]),"item.value":v(({item:e})=>[s(`span`,k,r(e.value),1)]),"item.is_recovery":v(({item:e})=>[e.is_recovery?(c(),h(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[6]||=[o(`已恢复`,-1)]]),_:1})):(c(),h(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[7]||=[o(`告警中`,-1)]]),_:1}))]),"item.created_at":v(({item:e})=>[s(`span`,A,r(e.created_at),1)]),"no-data":v(()=>[...d[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,Er as t,F as n,N as r,Nr as i,Ur as a,Z as o,Zn as s,_r as c,ar as l,er as u,hr as d,k as f,or as p,rr as m,sr as h,tr as g,w as _,yr as v,zr as y,zt as b}from"./index-Dp4iCzbs.js";import{o as x,t as S}from"./VContainer-BpcBPa4e.js";import{n as C}from"./dataTable-esDY4cOk.js";import{t as w}from"./VChip-DBjCm_hy.js";import{t as T}from"./VSelect-B3aZ6TxZ.js";import{t as E}from"./VDataTableServer-5YTAzdGq.js";import{n as D,t as O}from"./VRow-B_I9Fnty.js";import{t as k}from"./VSpacer-Dij81TMC.js";var A={class:`font-weight-medium`},j={class:`text-body-2`},M={class:`text-caption text-medium-emphasis`},N=h({__name:`AlertsPage`,setup(h){let N=x(),P=i(!1),F=i([]),I=i(0),L=i(1),R=i(null),z=i(null),B=i([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),V=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],H=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],U=C([{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}]);async function W(){try{let e=await b.get(`/alert-history/stats`);B.value[0].value=String(e.today||0),B.value[1].value=String(e.active||0),B.value[2].value=String(e.recovered||0),B.value[3].value=e.top_server||`—`}catch{N(`加载统计失败`,`error`)}}async function G(){P.value=!0;try{let e=await b.get(`/alert-history/`,{page:L.value,per_page:20,type:R.value||void 0,status:z.value||void 0});F.value=e.items||[],I.value=e.total||0}catch{F.value=[]}finally{P.value=!1}}function K(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function q(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{W(),G()}),(i,d)=>(c(),g(S,{fluid:``,class:`pa-6`},{default:t(()=>[p(O,{class:`mb-4`},{default:t(()=>[(c(!0),m(s,null,v(B.value,n=>(c(),g(D,{cols:`12`,sm:`6`,lg:`3`,key:n.label},{default:t(()=>[p(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:t(()=>[p(f,null,{append:t(()=>[p(o,{color:n.color,size:`30`},{default:t(()=>[l(a(n.icon),1)]),_:2},1032,[`color`])]),default:t(()=>[p(e,{class:`text-body-small`},{default:t(()=>[l(a(n.label),1)]),_:2},1024),p(e,null,{default:t(()=>[l(a(n.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),p(r,{elevation:`0`,border:``,rounded:`lg`},{default:t(()=>[p(n,{class:`d-flex align-center`},{default:t(()=>[d[5]||=l(` 告警历史 `,-1),p(k),p(T,{modelValue:R.value,"onUpdate:modelValue":[d[0]||=e=>R.value=e,d[1]||=e=>{L.value=1,G()}],items:V,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),p(T,{modelValue:z.value,"onUpdate:modelValue":[d[2]||=e=>z.value=e,d[3]||=e=>{L.value=1,G()}],items:H,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),p(E,{items:F.value,headers:y(U),"items-length":I.value,loading:P.value,page:L.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{L.value=e,G()}},{"item.alert_type":t(({item:e})=>[p(w,{color:K(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:t(()=>[p(o,{size:`12`},{default:t(()=>[l(a(q(e.alert_type)),1)]),_:2},1024)]),default:t(()=>[l(` `+a(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":t(({item:e})=>[u(`span`,A,a(e.server_name),1)]),"item.value":t(({item:e})=>[u(`span`,j,a(e.value),1)]),"item.is_recovery":t(({item:e})=>[e.is_recovery?(c(),g(w,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[6]||=[l(`已恢复`,-1)]]),_:1})):(c(),g(w,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[7]||=[l(`告警中`,-1)]]),_:1}))]),"item.created_at":t(({item:e})=>[u(`span`,M,a(e.created_at),1)]),"no-data":t(()=>[...d[8]||=[u(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{N as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{a as e,o as t}from"./VSelectionControl-EvpbLA8N.js";import{n,t as r}from"./VRow-ECYmMAi9.js";import{$n as i,A as a,B as o,F as s,Fr as c,Gr as l,N as u,Or as d,R as f,T as p,Ut as m,Vr as h,_r as g,ar as _,cr as v,j as y,lr as b,nr as x,rr as S,sr as C,ut as w,xr as T,yr as E}from"./index-B-qGIXx8.js";import{n as D}from"./dataTable-esDY4cOk.js";import{t as O}from"./VSelect-D0D-yfrH.js";import{t as k}from"./VDataTableServer-DCjhqS-Y.js";var A={class:`font-weight-medium`},j={class:`text-body-2`},M={class:`text-caption text-medium-emphasis`},N=b({__name:`AlertsPage`,setup(b){let N=e(),P=c(!1),F=c([]),I=c(0),L=c(1),R=c(null),z=c(null),B=c([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),V=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],H=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],U=D([{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}]);async function W(){try{let e=await m.get(`/alert-history/stats`);B.value[0].value=String(e.today||0),B.value[1].value=String(e.active||0),B.value[2].value=String(e.recovered||0),B.value[3].value=e.top_server||`—`}catch{N(`加载统计失败`,`error`)}}async function G(){P.value=!0;try{let e=await m.get(`/alert-history/`,{page:L.value,per_page:20,type:R.value||void 0,status:z.value||void 0});F.value=e.items||[],I.value=e.total||0}catch{F.value=[]}finally{P.value=!1}}function K(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function q(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return g(()=>{W(),G()}),(e,c)=>(E(),S(t,{fluid:``,class:`pa-6`},{default:d(()=>[v(r,{class:`mb-4`},{default:d(()=>[(E(!0),_(i,null,T(B.value,e=>(E(),S(n,{cols:`12`,sm:`6`,lg:`3`,key:e.label},{default:d(()=>[v(p,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:d(()=>[v(a,null,{append:d(()=>[v(w,{color:e.color,size:`30`},{default:d(()=>[C(l(e.icon),1)]),_:2},1032,[`color`])]),default:d(()=>[v(y,{class:`text-body-small`},{default:d(()=>[C(l(e.label),1)]),_:2},1024),v(y,null,{default:d(()=>[C(l(e.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),v(f,{elevation:`0`,border:``,rounded:`lg`},{default:d(()=>[v(o,{class:`d-flex align-center`},{default:d(()=>[c[5]||=C(` 告警历史 `,-1),v(u),v(O,{modelValue:R.value,"onUpdate:modelValue":[c[0]||=e=>R.value=e,c[1]||=e=>{L.value=1,G()}],items:V,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),v(O,{modelValue:z.value,"onUpdate:modelValue":[c[2]||=e=>z.value=e,c[3]||=e=>{L.value=1,G()}],items:H,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),v(k,{items:F.value,headers:h(U),"items-length":I.value,loading:P.value,page:L.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":c[4]||=e=>{L.value=e,G()}},{"item.alert_type":d(({item:e})=>[v(s,{color:K(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:d(()=>[v(w,{size:`12`},{default:d(()=>[C(l(q(e.alert_type)),1)]),_:2},1024)]),default:d(()=>[C(` `+l(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":d(({item:e})=>[x(`span`,A,l(e.server_name),1)]),"item.value":d(({item:e})=>[x(`span`,j,l(e.value),1)]),"item.is_recovery":d(({item:e})=>[e.is_recovery?(E(),S(s,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:d(()=>[...c[6]||=[C(`已恢复`,-1)]]),_:1})):(E(),S(s,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:d(()=>[...c[7]||=[C(`告警中`,-1)]]),_:1}))]),"item.created_at":d(({item:e})=>[x(`span`,M,l(e.created_at),1)]),"no-data":d(()=>[...c[8]||=[x(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{N as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,Er as t,F as n,N as r,Nr as i,Ur as a,Z as o,Zn as s,_r as c,ar as l,er as u,hr as d,k as f,or as p,rr as m,sr as h,tr as g,w as _,yr as v,zt as y}from"./index-DoaDMa1C.js";import{o as b,t as x}from"./VContainer-BQRFgmV8.js";import{t as S}from"./VChip-w-VUeBu6.js";import{t as C}from"./VSelect-lvvpsnJa.js";import{t as w}from"./VDataTableServer-CqSShzYP.js";import{n as T,t as E}from"./VRow-CQuw1Sq-.js";import{t as D}from"./VSpacer-DctU2jOI.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=h({__name:`AlertsPage`,setup(h){let j=b(),M=i(!1),N=i([]),P=i(0),F=i(1),I=i(null),L=i(null),R=i([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(i,d)=>(c(),g(x,{fluid:``,class:`pa-6`},{default:t(()=>[p(E,{class:`mb-4`},{default:t(()=>[(c(!0),m(s,null,v(R.value,n=>(c(),g(T,{cols:`12`,sm:`6`,lg:`3`,key:n.label},{default:t(()=>[p(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:t(()=>[p(f,null,{append:t(()=>[p(o,{color:n.color,size:`30`},{default:t(()=>[l(a(n.icon),1)]),_:2},1032,[`color`])]),default:t(()=>[p(e,{class:`text-body-small`},{default:t(()=>[l(a(n.label),1)]),_:2},1024),p(e,null,{default:t(()=>[l(a(n.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),p(r,{elevation:`0`,border:``,rounded:`lg`},{default:t(()=>[p(n,{class:`d-flex align-center`},{default:t(()=>[d[5]||=l(` 告警历史 `,-1),p(D),p(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),p(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),p(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":t(({item:e})=>[p(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:t(()=>[p(o,{size:`12`},{default:t(()=>[l(a(G(e.alert_type)),1)]),_:2},1024)]),default:t(()=>[l(` `+a(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":t(({item:e})=>[u(`span`,O,a(e.server_name),1)]),"item.value":t(({item:e})=>[u(`span`,k,a(e.value),1)]),"item.is_recovery":t(({item:e})=>[e.is_recovery?(c(),g(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[6]||=[l(`已恢复`,-1)]]),_:1})):(c(),g(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[7]||=[l(`告警中`,-1)]]),_:1}))]),"item.created_at":t(({item:e})=>[u(`span`,A,a(e.created_at),1)]),"no-data":t(()=>[...d[8]||=[u(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,Er as t,F as n,N as r,Nr as i,Ur as a,Z as o,Zn as s,_r as c,ar as l,er as u,hr as d,k as f,or as p,rr as m,sr as h,tr as g,w as _,yr as v,zt as y}from"./index-DefLgbfN.js";import{o as b,t as x}from"./VContainer-Cfs-evp_.js";import{t as S}from"./VChip-B776T65H.js";import{t as C}from"./VSelect-CI0l_G9L.js";import{t as w}from"./VDataTableServer-BVvotZW1.js";import{n as T,t as E}from"./VRow-DkDIFkCo.js";import{t as D}from"./VSpacer-K-VIfO9Y.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=h({__name:`AlertsPage`,setup(h){let j=b(),M=i(!1),N=i([]),P=i(0),F=i(1),I=i(null),L=i(null),R=i([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(i,d)=>(c(),g(x,{fluid:``,class:`pa-6`},{default:t(()=>[p(E,{class:`mb-4`},{default:t(()=>[(c(!0),m(s,null,v(R.value,n=>(c(),g(T,{cols:`12`,sm:`6`,lg:`3`,key:n.label},{default:t(()=>[p(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:t(()=>[p(f,null,{append:t(()=>[p(o,{color:n.color,size:`30`},{default:t(()=>[l(a(n.icon),1)]),_:2},1032,[`color`])]),default:t(()=>[p(e,{class:`text-body-small`},{default:t(()=>[l(a(n.label),1)]),_:2},1024),p(e,null,{default:t(()=>[l(a(n.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),p(r,{elevation:`0`,border:``,rounded:`lg`},{default:t(()=>[p(n,{class:`d-flex align-center`},{default:t(()=>[d[5]||=l(` 告警历史 `,-1),p(D),p(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),p(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),p(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":t(({item:e})=>[p(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:t(()=>[p(o,{size:`12`},{default:t(()=>[l(a(G(e.alert_type)),1)]),_:2},1024)]),default:t(()=>[l(` `+a(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":t(({item:e})=>[u(`span`,O,a(e.server_name),1)]),"item.value":t(({item:e})=>[u(`span`,k,a(e.value),1)]),"item.is_recovery":t(({item:e})=>[e.is_recovery?(c(),g(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[6]||=[l(`已恢复`,-1)]]),_:1})):(c(),g(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[7]||=[l(`告警中`,-1)]]),_:1}))]),"item.created_at":t(({item:e})=>[u(`span`,A,a(e.created_at),1)]),"no-data":t(()=>[...d[8]||=[u(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,F as t,N as n,Vr as r,Z as i,Zn as a,ar as o,er as s,gr as c,jr as l,k as u,mr as d,or as f,rr as p,sr as m,tr as h,vr as g,w as _,wr as v,zt as y}from"./index-3SWW3Wj_.js";import{o as b,t as x}from"./VContainer-B79RFM4s.js";import{t as S}from"./VChip-CMAdQZRJ.js";import{t as C}from"./VSelect-CNVSTEnc.js";import{t as w}from"./VDataTableServer-mXWi-ccO.js";import{n as T,t as E}from"./VRow-CEurQEX3.js";import{t as D}from"./VSpacer-Lz_Er0LP.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=m({__name:`AlertsPage`,setup(m){let j=b(),M=l(!1),N=l([]),P=l(0),F=l(1),I=l(null),L=l(null),R=l([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(l,d)=>(c(),h(x,{fluid:``,class:`pa-6`},{default:v(()=>[f(E,{class:`mb-4`},{default:v(()=>[(c(!0),p(a,null,g(R.value,t=>(c(),h(T,{cols:`12`,sm:`6`,lg:`3`,key:t.label},{default:v(()=>[f(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:v(()=>[f(u,null,{append:v(()=>[f(i,{color:t.color,size:`30`},{default:v(()=>[o(r(t.icon),1)]),_:2},1032,[`color`])]),default:v(()=>[f(e,{class:`text-body-small`},{default:v(()=>[o(r(t.label),1)]),_:2},1024),f(e,null,{default:v(()=>[o(r(t.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),f(n,{elevation:`0`,border:``,rounded:`lg`},{default:v(()=>[f(t,{class:`d-flex align-center`},{default:v(()=>[d[5]||=o(` 告警历史 `,-1),f(D),f(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),f(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),f(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":v(({item:e})=>[f(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:v(()=>[f(i,{size:`12`},{default:v(()=>[o(r(G(e.alert_type)),1)]),_:2},1024)]),default:v(()=>[o(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":v(({item:e})=>[s(`span`,O,r(e.server_name),1)]),"item.value":v(({item:e})=>[s(`span`,k,r(e.value),1)]),"item.is_recovery":v(({item:e})=>[e.is_recovery?(c(),h(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[6]||=[o(`已恢复`,-1)]]),_:1})):(c(),h(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[7]||=[o(`告警中`,-1)]]),_:1}))]),"item.created_at":v(({item:e})=>[s(`span`,A,r(e.created_at),1)]),"no-data":v(()=>[...d[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,F as t,N as n,Vr as r,Z as i,Zn as a,ar as o,er as s,gr as c,jr as l,k as u,mr as d,or as f,rr as p,sr as m,tr as h,vr as g,w as _,wr as v,zt as y}from"./index-BoZyi8Cu.js";import{o as b,t as x}from"./VContainer-BT9w_m28.js";import{t as S}from"./VChip-BVR48u8E.js";import{t as C}from"./VSelect-BuZWt-gO.js";import{t as w}from"./VDataTableServer-CjBcrBi4.js";import{n as T,t as E}from"./VRow-DNuBhaBk.js";import{t as D}from"./VSpacer-BHLI8VYt.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=m({__name:`AlertsPage`,setup(m){let j=b(),M=l(!1),N=l([]),P=l(0),F=l(1),I=l(null),L=l(null),R=l([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(l,d)=>(c(),h(x,{fluid:``,class:`pa-6`},{default:v(()=>[f(E,{class:`mb-4`},{default:v(()=>[(c(!0),p(a,null,g(R.value,t=>(c(),h(T,{cols:`12`,sm:`6`,lg:`3`,key:t.label},{default:v(()=>[f(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:v(()=>[f(u,null,{append:v(()=>[f(i,{color:t.color,size:`30`},{default:v(()=>[o(r(t.icon),1)]),_:2},1032,[`color`])]),default:v(()=>[f(e,{class:`text-body-small`},{default:v(()=>[o(r(t.label),1)]),_:2},1024),f(e,null,{default:v(()=>[o(r(t.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),f(n,{elevation:`0`,border:``,rounded:`lg`},{default:v(()=>[f(t,{class:`d-flex align-center`},{default:v(()=>[d[5]||=o(` 告警历史 `,-1),f(D),f(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),f(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),f(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":v(({item:e})=>[f(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:v(()=>[f(i,{size:`12`},{default:v(()=>[o(r(G(e.alert_type)),1)]),_:2},1024)]),default:v(()=>[o(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":v(({item:e})=>[s(`span`,O,r(e.server_name),1)]),"item.value":v(({item:e})=>[s(`span`,k,r(e.value),1)]),"item.is_recovery":v(({item:e})=>[e.is_recovery?(c(),h(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[6]||=[o(`已恢复`,-1)]]),_:1})):(c(),h(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[7]||=[o(`告警中`,-1)]]),_:1}))]),"item.created_at":v(({item:e})=>[s(`span`,A,r(e.created_at),1)]),"no-data":v(()=>[...d[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{a as e,o as t}from"./VSelectionControl-BQRUjX5P.js";import{n,t as r}from"./VRow-C1b4Uu8Q.js";import{$n as i,A as a,B as o,F as s,Fr as c,Gr as l,N as u,Or as d,R as f,T as p,Ut as m,Vr as h,_r as g,ar as _,cr as v,j as y,lr as b,nr as x,rr as S,sr as C,ut as w,xr as T,yr as E}from"./index-DbSyD28P.js";import{n as D}from"./dataTable-esDY4cOk.js";import{t as O}from"./VSelect-glh4Zdhv.js";import{t as k}from"./VDataTableServer-BisDXMgG.js";var A={class:`font-weight-medium`},j={class:`text-body-2`},M={class:`text-caption text-medium-emphasis`},N=b({__name:`AlertsPage`,setup(b){let N=e(),P=c(!1),F=c([]),I=c(0),L=c(1),R=c(null),z=c(null),B=c([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),V=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],H=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],U=D([{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}]);async function W(){try{let e=await m.get(`/alert-history/stats`);B.value[0].value=String(e.today||0),B.value[1].value=String(e.active||0),B.value[2].value=String(e.recovered||0),B.value[3].value=e.top_server||`—`}catch{N(`加载统计失败`,`error`)}}async function G(){P.value=!0;try{let e=await m.get(`/alert-history/`,{page:L.value,per_page:20,type:R.value||void 0,status:z.value||void 0});F.value=e.items||[],I.value=e.total||0}catch{F.value=[]}finally{P.value=!1}}function K(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function q(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return g(()=>{W(),G()}),(e,c)=>(E(),S(t,{fluid:``,class:`pa-6`},{default:d(()=>[v(r,{class:`mb-4`},{default:d(()=>[(E(!0),_(i,null,T(B.value,e=>(E(),S(n,{cols:`12`,sm:`6`,lg:`3`,key:e.label},{default:d(()=>[v(p,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:d(()=>[v(a,null,{append:d(()=>[v(w,{color:e.color,size:`30`},{default:d(()=>[C(l(e.icon),1)]),_:2},1032,[`color`])]),default:d(()=>[v(y,{class:`text-body-small`},{default:d(()=>[C(l(e.label),1)]),_:2},1024),v(y,null,{default:d(()=>[C(l(e.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),v(f,{elevation:`0`,border:``,rounded:`lg`},{default:d(()=>[v(o,{class:`d-flex align-center`},{default:d(()=>[c[5]||=C(` 告警历史 `,-1),v(u),v(O,{modelValue:R.value,"onUpdate:modelValue":[c[0]||=e=>R.value=e,c[1]||=e=>{L.value=1,G()}],items:V,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),v(O,{modelValue:z.value,"onUpdate:modelValue":[c[2]||=e=>z.value=e,c[3]||=e=>{L.value=1,G()}],items:H,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),v(k,{items:F.value,headers:h(U),"items-length":I.value,loading:P.value,page:L.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":c[4]||=e=>{L.value=e,G()}},{"item.alert_type":d(({item:e})=>[v(s,{color:K(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:d(()=>[v(w,{size:`12`},{default:d(()=>[C(l(q(e.alert_type)),1)]),_:2},1024)]),default:d(()=>[C(` `+l(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":d(({item:e})=>[x(`span`,A,l(e.server_name),1)]),"item.value":d(({item:e})=>[x(`span`,j,l(e.value),1)]),"item.is_recovery":d(({item:e})=>[e.is_recovery?(E(),S(s,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:d(()=>[...c[6]||=[C(`已恢复`,-1)]]),_:1})):(E(),S(s,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:d(()=>[...c[7]||=[C(`告警中`,-1)]]),_:1}))]),"item.created_at":d(({item:e})=>[x(`span`,M,l(e.created_at),1)]),"no-data":d(()=>[...c[8]||=[x(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{N as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,F as t,N as n,Vr as r,Z as i,Zn as a,ar as o,er as s,gr as c,jr as l,k as u,mr as d,or as f,rr as p,sr as m,tr as h,vr as g,w as _,wr as v,zt as y}from"./index-DEV2Sc6Q.js";import{o as b,t as x}from"./VContainer-DTFumUOK.js";import{t as S}from"./VChip-DHw3siVg.js";import{t as C}from"./VSelect-Dmadm3KB.js";import{t as w}from"./VDataTableServer-BC4CoMyH.js";import{n as T,t as E}from"./VRow-4Eq778VA.js";import{t as D}from"./VSpacer-sJEakyfC.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=m({__name:`AlertsPage`,setup(m){let j=b(),M=l(!1),N=l([]),P=l(0),F=l(1),I=l(null),L=l(null),R=l([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(l,d)=>(c(),h(x,{fluid:``,class:`pa-6`},{default:v(()=>[f(E,{class:`mb-4`},{default:v(()=>[(c(!0),p(a,null,g(R.value,t=>(c(),h(T,{cols:`12`,sm:`6`,lg:`3`,key:t.label},{default:v(()=>[f(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:v(()=>[f(u,null,{append:v(()=>[f(i,{color:t.color,size:`30`},{default:v(()=>[o(r(t.icon),1)]),_:2},1032,[`color`])]),default:v(()=>[f(e,{class:`text-body-small`},{default:v(()=>[o(r(t.label),1)]),_:2},1024),f(e,null,{default:v(()=>[o(r(t.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),f(n,{elevation:`0`,border:``,rounded:`lg`},{default:v(()=>[f(t,{class:`d-flex align-center`},{default:v(()=>[d[5]||=o(` 告警历史 `,-1),f(D),f(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),f(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),f(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":v(({item:e})=>[f(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:v(()=>[f(i,{size:`12`},{default:v(()=>[o(r(G(e.alert_type)),1)]),_:2},1024)]),default:v(()=>[o(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":v(({item:e})=>[s(`span`,O,r(e.server_name),1)]),"item.value":v(({item:e})=>[s(`span`,k,r(e.value),1)]),"item.is_recovery":v(({item:e})=>[e.is_recovery?(c(),h(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[6]||=[o(`已恢复`,-1)]]),_:1})):(c(),h(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[7]||=[o(`告警中`,-1)]]),_:1}))]),"item.created_at":v(({item:e})=>[s(`span`,A,r(e.created_at),1)]),"no-data":v(()=>[...d[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,Er as t,F as n,N as r,Nr as i,Ur as a,Z as o,Zn as s,_r as c,ar as l,er as u,hr as d,k as f,or as p,rr as m,sr as h,tr as g,w as _,yr as v,zr as y,zt as b}from"./index-x_UzlvQU.js";import{o as x,t as S}from"./VContainer-9KPDvITd.js";import{n as C}from"./dataTable-esDY4cOk.js";import{t as w}from"./VChip-C55Q03jC.js";import{t as T}from"./VSelect-D6JhGiC7.js";import{t as E}from"./VDataTableServer-2HgZ5qDn.js";import{n as D,t as O}from"./VRow-D24DfLiy.js";import{t as k}from"./VSpacer-JMAll94i.js";var A={class:`font-weight-medium`},j={class:`text-body-2`},M={class:`text-caption text-medium-emphasis`},N=h({__name:`AlertsPage`,setup(h){let N=x(),P=i(!1),F=i([]),I=i(0),L=i(1),R=i(null),z=i(null),B=i([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),V=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],H=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],U=C([{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}]);async function W(){try{let e=await b.get(`/alert-history/stats`);B.value[0].value=String(e.today||0),B.value[1].value=String(e.active||0),B.value[2].value=String(e.recovered||0),B.value[3].value=e.top_server||`—`}catch{N(`加载统计失败`,`error`)}}async function G(){P.value=!0;try{let e=await b.get(`/alert-history/`,{page:L.value,per_page:20,type:R.value||void 0,status:z.value||void 0});F.value=e.items||[],I.value=e.total||0}catch{F.value=[]}finally{P.value=!1}}function K(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function q(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{W(),G()}),(i,d)=>(c(),g(S,{fluid:``,class:`pa-6`},{default:t(()=>[p(O,{class:`mb-4`},{default:t(()=>[(c(!0),m(s,null,v(B.value,n=>(c(),g(D,{cols:`12`,sm:`6`,lg:`3`,key:n.label},{default:t(()=>[p(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:t(()=>[p(f,null,{append:t(()=>[p(o,{color:n.color,size:`30`},{default:t(()=>[l(a(n.icon),1)]),_:2},1032,[`color`])]),default:t(()=>[p(e,{class:`text-body-small`},{default:t(()=>[l(a(n.label),1)]),_:2},1024),p(e,null,{default:t(()=>[l(a(n.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),p(r,{elevation:`0`,border:``,rounded:`lg`},{default:t(()=>[p(n,{class:`d-flex align-center`},{default:t(()=>[d[5]||=l(` 告警历史 `,-1),p(k),p(T,{modelValue:R.value,"onUpdate:modelValue":[d[0]||=e=>R.value=e,d[1]||=e=>{L.value=1,G()}],items:V,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),p(T,{modelValue:z.value,"onUpdate:modelValue":[d[2]||=e=>z.value=e,d[3]||=e=>{L.value=1,G()}],items:H,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),p(E,{items:F.value,headers:y(U),"items-length":I.value,loading:P.value,page:L.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{L.value=e,G()}},{"item.alert_type":t(({item:e})=>[p(w,{color:K(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:t(()=>[p(o,{size:`12`},{default:t(()=>[l(a(q(e.alert_type)),1)]),_:2},1024)]),default:t(()=>[l(` `+a(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":t(({item:e})=>[u(`span`,A,a(e.server_name),1)]),"item.value":t(({item:e})=>[u(`span`,j,a(e.value),1)]),"item.is_recovery":t(({item:e})=>[e.is_recovery?(c(),g(w,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[6]||=[l(`已恢复`,-1)]]),_:1})):(c(),g(w,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[7]||=[l(`告警中`,-1)]]),_:1}))]),"item.created_at":t(({item:e})=>[u(`span`,M,a(e.created_at),1)]),"no-data":t(()=>[...d[8]||=[u(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{N as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{$n as e,A as t,Ar as n,Br as r,Cr as i,F as a,N as o,Rt as s,X as c,Xn as l,_r as u,ar as d,er as f,hr as p,ir as m,k as h,nr as g,or as _,pr as v,w as y}from"./index-1vc2lwKY.js";import{o as b,t as x}from"./VContainer-Cyz28tn8.js";import{t as S}from"./VChip-DxlrSIbY.js";import{t as C}from"./VSelect-bmHFJ0Qw.js";import{t as w}from"./VDataTableServer-BgrY7CII.js";import{n as T,t as E}from"./VRow-DWRsOC56.js";import{t as D}from"./VSpacer-DTN9CH1q.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=_({__name:`AlertsPage`,setup(_){let j=b(),M=n(!1),N=n([]),P=n(0),F=n(1),I=n(null),L=n(null),R=n([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await s.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await s.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return v(()=>{H(),U()}),(n,s)=>(p(),f(x,{fluid:``,class:`pa-6`},{default:i(()=>[d(E,{class:`mb-4`},{default:i(()=>[(p(!0),g(l,null,u(R.value,e=>(p(),f(T,{cols:`12`,sm:`6`,lg:`3`,key:e.label},{default:i(()=>[d(y,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:i(()=>[d(h,null,{append:i(()=>[d(c,{color:e.color,size:`30`},{default:i(()=>[m(r(e.icon),1)]),_:2},1032,[`color`])]),default:i(()=>[d(t,{class:`text-body-small`},{default:i(()=>[m(r(e.label),1)]),_:2},1024),d(t,null,{default:i(()=>[m(r(e.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),d(o,{elevation:`0`,border:``,rounded:`lg`},{default:i(()=>[d(a,{class:`d-flex align-center`},{default:i(()=>[s[5]||=m(` 告警历史 `,-1),d(D),d(C,{modelValue:I.value,"onUpdate:modelValue":[s[0]||=e=>I.value=e,s[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),d(C,{modelValue:L.value,"onUpdate:modelValue":[s[2]||=e=>L.value=e,s[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),d(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":s[4]||=e=>{F.value=e,U()}},{"item.alert_type":i(({item:e})=>[d(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:i(()=>[d(c,{size:`12`},{default:i(()=>[m(r(G(e.alert_type)),1)]),_:2},1024)]),default:i(()=>[m(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":i(({item:t})=>[e(`span`,O,r(t.server_name),1)]),"item.value":i(({item:t})=>[e(`span`,k,r(t.value),1)]),"item.is_recovery":i(({item:e})=>[e.is_recovery?(p(),f(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:i(()=>[...s[6]||=[m(`已恢复`,-1)]]),_:1})):(p(),f(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:i(()=>[...s[7]||=[m(`告警中`,-1)]]),_:1}))]),"item.created_at":i(({item:t})=>[e(`span`,A,r(t.created_at),1)]),"no-data":i(()=>[...s[8]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,F as t,N as n,Vr as r,Z as i,Zn as a,ar as o,er as s,gr as c,jr as l,k as u,mr as d,or as f,rr as p,sr as m,tr as h,vr as g,w as _,wr as v,zt as y}from"./index-BOX5ad4G.js";import{o as b,t as x}from"./VContainer-CsduC4GT.js";import{t as S}from"./VChip-D0VaTwoJ.js";import{t as C}from"./VSelect-skTyo71B.js";import{t as w}from"./VDataTableServer-CyZWQ374.js";import{n as T,t as E}from"./VRow-A22kGc_y.js";import{t as D}from"./VSpacer-CysfZoCS.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=m({__name:`AlertsPage`,setup(m){let j=b(),M=l(!1),N=l([]),P=l(0),F=l(1),I=l(null),L=l(null),R=l([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(l,d)=>(c(),h(x,{fluid:``,class:`pa-6`},{default:v(()=>[f(E,{class:`mb-4`},{default:v(()=>[(c(!0),p(a,null,g(R.value,t=>(c(),h(T,{cols:`12`,sm:`6`,lg:`3`,key:t.label},{default:v(()=>[f(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:v(()=>[f(u,null,{append:v(()=>[f(i,{color:t.color,size:`30`},{default:v(()=>[o(r(t.icon),1)]),_:2},1032,[`color`])]),default:v(()=>[f(e,{class:`text-body-small`},{default:v(()=>[o(r(t.label),1)]),_:2},1024),f(e,null,{default:v(()=>[o(r(t.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),f(n,{elevation:`0`,border:``,rounded:`lg`},{default:v(()=>[f(t,{class:`d-flex align-center`},{default:v(()=>[d[5]||=o(` 告警历史 `,-1),f(D),f(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),f(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),f(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":v(({item:e})=>[f(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:v(()=>[f(i,{size:`12`},{default:v(()=>[o(r(G(e.alert_type)),1)]),_:2},1024)]),default:v(()=>[o(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":v(({item:e})=>[s(`span`,O,r(e.server_name),1)]),"item.value":v(({item:e})=>[s(`span`,k,r(e.value),1)]),"item.is_recovery":v(({item:e})=>[e.is_recovery?(c(),h(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[6]||=[o(`已恢复`,-1)]]),_:1})):(c(),h(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[7]||=[o(`告警中`,-1)]]),_:1}))]),"item.created_at":v(({item:e})=>[s(`span`,A,r(e.created_at),1)]),"no-data":v(()=>[...d[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,Er as t,F as n,N as r,Nr as i,Ur as a,Z as o,Zn as s,_r as c,ar as l,er as u,hr as d,k as f,or as p,rr as m,sr as h,tr as g,w as _,yr as v,zr as y,zt as b}from"./index-CITYCKPt.js";import{o as x,t as S}from"./VContainer-D3j80qYo.js";import{n as C}from"./dataTable-esDY4cOk.js";import{t as w}from"./VChip-BZLbsGAB.js";import{t as T}from"./VSelect-BjOTKQYN.js";import{t as E}from"./VDataTableServer-COy02vU2.js";import{n as D,t as O}from"./VRow-BPsTMp9y.js";import{t as k}from"./VSpacer-D1z4iYRg.js";var A={class:`font-weight-medium`},j={class:`text-body-2`},M={class:`text-caption text-medium-emphasis`},N=h({__name:`AlertsPage`,setup(h){let N=x(),P=i(!1),F=i([]),I=i(0),L=i(1),R=i(null),z=i(null),B=i([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),V=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],H=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],U=C([{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}]);async function W(){try{let e=await b.get(`/alert-history/stats`);B.value[0].value=String(e.today||0),B.value[1].value=String(e.active||0),B.value[2].value=String(e.recovered||0),B.value[3].value=e.top_server||`—`}catch{N(`加载统计失败`,`error`)}}async function G(){P.value=!0;try{let e=await b.get(`/alert-history/`,{page:L.value,per_page:20,type:R.value||void 0,status:z.value||void 0});F.value=e.items||[],I.value=e.total||0}catch{F.value=[]}finally{P.value=!1}}function K(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function q(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{W(),G()}),(i,d)=>(c(),g(S,{fluid:``,class:`pa-6`},{default:t(()=>[p(O,{class:`mb-4`},{default:t(()=>[(c(!0),m(s,null,v(B.value,n=>(c(),g(D,{cols:`12`,sm:`6`,lg:`3`,key:n.label},{default:t(()=>[p(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:t(()=>[p(f,null,{append:t(()=>[p(o,{color:n.color,size:`30`},{default:t(()=>[l(a(n.icon),1)]),_:2},1032,[`color`])]),default:t(()=>[p(e,{class:`text-body-small`},{default:t(()=>[l(a(n.label),1)]),_:2},1024),p(e,null,{default:t(()=>[l(a(n.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),p(r,{elevation:`0`,border:``,rounded:`lg`},{default:t(()=>[p(n,{class:`d-flex align-center`},{default:t(()=>[d[5]||=l(` 告警历史 `,-1),p(k),p(T,{modelValue:R.value,"onUpdate:modelValue":[d[0]||=e=>R.value=e,d[1]||=e=>{L.value=1,G()}],items:V,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),p(T,{modelValue:z.value,"onUpdate:modelValue":[d[2]||=e=>z.value=e,d[3]||=e=>{L.value=1,G()}],items:H,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),p(E,{items:F.value,headers:y(U),"items-length":I.value,loading:P.value,page:L.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{L.value=e,G()}},{"item.alert_type":t(({item:e})=>[p(w,{color:K(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:t(()=>[p(o,{size:`12`},{default:t(()=>[l(a(q(e.alert_type)),1)]),_:2},1024)]),default:t(()=>[l(` `+a(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":t(({item:e})=>[u(`span`,A,a(e.server_name),1)]),"item.value":t(({item:e})=>[u(`span`,j,a(e.value),1)]),"item.is_recovery":t(({item:e})=>[e.is_recovery?(c(),g(w,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[6]||=[l(`已恢复`,-1)]]),_:1})):(c(),g(w,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:t(()=>[...d[7]||=[l(`告警中`,-1)]]),_:1}))]),"item.created_at":t(({item:e})=>[u(`span`,M,a(e.created_at),1)]),"no-data":t(()=>[...d[8]||=[u(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{N as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{A as e,F as t,N as n,Vr as r,Z as i,Zn as a,ar as o,er as s,gr as c,jr as l,k as u,mr as d,or as f,rr as p,sr as m,tr as h,vr as g,w as _,wr as v,zt as y}from"./index-8R5Vx1WH.js";import{o as b,t as x}from"./VContainer-ByAXNVXy.js";import{t as S}from"./VChip-CO9HT0ch.js";import{t as C}from"./VSelect-BB7JEkD3.js";import{t as w}from"./VDataTableServer-Bx8d7a_i.js";import{n as T,t as E}from"./VRow-BVJ9K6vV.js";import{t as D}from"./VSpacer-CTDq7Cy_.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=m({__name:`AlertsPage`,setup(m){let j=b(),M=l(!1),N=l([]),P=l(0),F=l(1),I=l(null),L=l(null),R=l([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await y.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await y.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return d(()=>{H(),U()}),(l,d)=>(c(),h(x,{fluid:``,class:`pa-6`},{default:v(()=>[f(E,{class:`mb-4`},{default:v(()=>[(c(!0),p(a,null,g(R.value,t=>(c(),h(T,{cols:`12`,sm:`6`,lg:`3`,key:t.label},{default:v(()=>[f(_,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:v(()=>[f(u,null,{append:v(()=>[f(i,{color:t.color,size:`30`},{default:v(()=>[o(r(t.icon),1)]),_:2},1032,[`color`])]),default:v(()=>[f(e,{class:`text-body-small`},{default:v(()=>[o(r(t.label),1)]),_:2},1024),f(e,null,{default:v(()=>[o(r(t.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),f(n,{elevation:`0`,border:``,rounded:`lg`},{default:v(()=>[f(t,{class:`d-flex align-center`},{default:v(()=>[d[5]||=o(` 告警历史 `,-1),f(D),f(C,{modelValue:I.value,"onUpdate:modelValue":[d[0]||=e=>I.value=e,d[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),f(C,{modelValue:L.value,"onUpdate:modelValue":[d[2]||=e=>L.value=e,d[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),f(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":d[4]||=e=>{F.value=e,U()}},{"item.alert_type":v(({item:e})=>[f(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:v(()=>[f(i,{size:`12`},{default:v(()=>[o(r(G(e.alert_type)),1)]),_:2},1024)]),default:v(()=>[o(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":v(({item:e})=>[s(`span`,O,r(e.server_name),1)]),"item.value":v(({item:e})=>[s(`span`,k,r(e.value),1)]),"item.is_recovery":v(({item:e})=>[e.is_recovery?(c(),h(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[6]||=[o(`已恢复`,-1)]]),_:1})):(c(),h(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:v(()=>[...d[7]||=[o(`告警中`,-1)]]),_:1}))]),"item.created_at":v(({item:e})=>[s(`span`,A,r(e.created_at),1)]),"no-data":v(()=>[...d[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{Er as e,F as t,N as n,Nr as r,Ur as i,_r as a,ar as o,er as s,hr as c,or as l,sr as u,t as d,tr as f,zr as p,zt as m}from"./index-CITYCKPt.js";import{o as h,t as g}from"./VContainer-D3j80qYo.js";import{n as _}from"./dataTable-esDY4cOk.js";import{t as v}from"./VChip-BZLbsGAB.js";import{t as y}from"./VSelect-BjOTKQYN.js";import{t as b}from"./VDataTableServer-COy02vU2.js";import{t as x}from"./VSpacer-D1z4iYRg.js";import{a as S,n as C,r as w,t as T}from"./auditNormalize-bmSA0RyN.js";var E={class:`font-weight-medium`},D={class:`text-body-2`},O={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},k={class:`text-caption text-medium-emphasis`},A={class:`text-caption text-medium-emphasis`},j=u({__name:`AuditPage`,setup(u){let j=h(),M=r(!1),N=r([]),P=r(0),F=r(1),I=r(null),L=r(``),R=r(``),z=C,B=_([{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}]);async function V(){M.value=!0;try{let e=(F.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:I.value||void 0,admin_username:L.value||void 0,date_from:R.value||void 0});N.value=T(t.items||[]),P.value=t.total||0}catch{N.value=[],j(`加载审计日志失败`,`error`)}finally{M.value=!1}}function H(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>V()),(r,c)=>(a(),f(g,{fluid:``,class:`pa-6`},{default:e(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:e(()=>[l(t,{class:`d-flex align-center`},{default:e(()=>[c[7]||=o(` 审计日志 `,-1),l(x),l(y,{modelValue:I.value,"onUpdate:modelValue":[c[0]||=e=>I.value=e,c[1]||=e=>{F.value=1,V()}],items:p(z),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[2]||=e=>L.value=e,c[3]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:R.value,"onUpdate:modelValue":[c[4]||=e=>R.value=e,c[5]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(b,{items:N.value,headers:p(B),"items-length":P.value,loading:M.value,page:F.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{F.value=e,V()}},{"item.action":e(({item:t})=>[l(v,{color:H(t.action),size:`small`,variant:`tonal`,label:``},{default:e(()=>[o(i(p(w)(t.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":e(({item:e})=>[s(`span`,E,i(e.admin_username),1)]),"item.target":e(({item:e})=>[s(`span`,D,i(p(S)(e.resource_type))+i(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":e(({item:e})=>[s(`span`,O,i(e.detail||`—`),1)]),"item.ip_address":e(({item:e})=>[s(`span`,k,i(e.ip_address||`—`),1)]),"item.created_at":e(({item:e})=>[s(`span`,A,i(e.created_at),1)]),"no-data":e(()=>[...c[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,N as n,Vr as r,ar as i,er as a,gr as o,jr as s,mr as c,or as l,sr as u,t as d,tr as f,wr as p,zt as m}from"./index-gfOrpdmy.js";import{o as h,t as g}from"./VContainer-DoAVnyqr.js";import{t as _}from"./VChip-sW1zi7V6.js";import{t as v}from"./VSelect-KxRF8W0i.js";import{t as y}from"./VDataTableServer-LfNDokVe.js";import{t as b}from"./VSpacer-ChMx8Hym.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=s(!1),M=s([]),N=s(0),P=s(1),F=s(null),I=s(``),L=s(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(s,c)=>(o(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[l(e,{class:`d-flex align-center`},{default:p(()=>[c[7]||=i(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:t(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":p(({item:e})=>[l(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:p(()=>[i(r(t(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":p(({item:e})=>[a(`span`,T,r(e.admin_username),1)]),"item.target":p(({item:e})=>[a(`span`,E,r(t(x)(e.resource_type))+r(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":p(({item:e})=>[a(`span`,D,r(e.detail||`—`),1)]),"item.ip_address":p(({item:e})=>[a(`span`,O,r(e.ip_address||`—`),1)]),"item.created_at":p(({item:e})=>[a(`span`,k,r(e.created_at),1)]),"no-data":p(()=>[...c[8]||=[a(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{Er as e,F as t,N as n,Nr as r,Ur as i,_r as a,ar as o,er as s,hr as c,or as l,sr as u,t as d,tr as f,zr as p,zt as m}from"./index-DefLgbfN.js";import{o as h,t as g}from"./VContainer-Cfs-evp_.js";import{t as _}from"./VChip-B776T65H.js";import{t as v}from"./VSelect-CI0l_G9L.js";import{t as y}from"./VDataTableServer-BVvotZW1.js";import{t as b}from"./VSpacer-K-VIfO9Y.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=r(!1),M=r([]),N=r(0),P=r(1),F=r(null),I=r(``),L=r(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(r,c)=>(a(),f(g,{fluid:``,class:`pa-6`},{default:e(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:e(()=>[l(t,{class:`d-flex align-center`},{default:e(()=>[c[7]||=o(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:p(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":e(({item:t})=>[l(_,{color:V(t.action),size:`small`,variant:`tonal`,label:``},{default:e(()=>[o(i(p(C)(t.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":e(({item:e})=>[s(`span`,T,i(e.admin_username),1)]),"item.target":e(({item:e})=>[s(`span`,E,i(p(x)(e.resource_type))+i(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":e(({item:e})=>[s(`span`,D,i(e.detail||`—`),1)]),"item.ip_address":e(({item:e})=>[s(`span`,O,i(e.ip_address||`—`),1)]),"item.created_at":e(({item:e})=>[s(`span`,k,i(e.created_at),1)]),"no-data":e(()=>[...c[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,N as n,Vr as r,ar as i,er as a,gr as o,jr as s,mr as c,or as l,sr as u,t as d,tr as f,wr as p,zt as m}from"./index-8R5Vx1WH.js";import{o as h,t as g}from"./VContainer-ByAXNVXy.js";import{t as _}from"./VChip-CO9HT0ch.js";import{t as v}from"./VSelect-BB7JEkD3.js";import{t as y}from"./VDataTableServer-Bx8d7a_i.js";import{t as b}from"./VSpacer-CTDq7Cy_.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=s(!1),M=s([]),N=s(0),P=s(1),F=s(null),I=s(``),L=s(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(s,c)=>(o(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[l(e,{class:`d-flex align-center`},{default:p(()=>[c[7]||=i(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:t(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":p(({item:e})=>[l(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:p(()=>[i(r(t(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":p(({item:e})=>[a(`span`,T,r(e.admin_username),1)]),"item.target":p(({item:e})=>[a(`span`,E,r(t(x)(e.resource_type))+r(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":p(({item:e})=>[a(`span`,D,r(e.detail||`—`),1)]),"item.ip_address":p(({item:e})=>[a(`span`,O,r(e.ip_address||`—`),1)]),"item.created_at":p(({item:e})=>[a(`span`,k,r(e.created_at),1)]),"no-data":p(()=>[...c[8]||=[a(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,N as n,Vr as r,ar as i,er as a,gr as o,jr as s,mr as c,or as l,sr as u,t as d,tr as f,wr as p,zt as m}from"./index-DEV2Sc6Q.js";import{o as h,t as g}from"./VContainer-DTFumUOK.js";import{t as _}from"./VChip-DHw3siVg.js";import{t as v}from"./VSelect-Dmadm3KB.js";import{t as y}from"./VDataTableServer-BC4CoMyH.js";import{t as b}from"./VSpacer-sJEakyfC.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=s(!1),M=s([]),N=s(0),P=s(1),F=s(null),I=s(``),L=s(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(s,c)=>(o(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[l(e,{class:`d-flex align-center`},{default:p(()=>[c[7]||=i(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:t(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":p(({item:e})=>[l(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:p(()=>[i(r(t(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":p(({item:e})=>[a(`span`,T,r(e.admin_username),1)]),"item.target":p(({item:e})=>[a(`span`,E,r(t(x)(e.resource_type))+r(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":p(({item:e})=>[a(`span`,D,r(e.detail||`—`),1)]),"item.ip_address":p(({item:e})=>[a(`span`,O,r(e.ip_address||`—`),1)]),"item.created_at":p(({item:e})=>[a(`span`,k,r(e.created_at),1)]),"no-data":p(()=>[...c[8]||=[a(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,N as n,Vr as r,ar as i,er as a,gr as o,jr as s,mr as c,or as l,sr as u,t as d,tr as f,wr as p,zt as m}from"./index-ia8w6hh3.js";import{o as h,t as g}from"./VContainer-Cg-YgCG2.js";import{t as _}from"./VChip-gd2nTpu3.js";import{t as v}from"./VSelect-DesLFoAg.js";import{t as y}from"./VDataTableServer-D4zQCwj2.js";import{t as b}from"./VSpacer-BtntPGL0.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=s(!1),M=s([]),N=s(0),P=s(1),F=s(null),I=s(``),L=s(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(s,c)=>(o(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[l(e,{class:`d-flex align-center`},{default:p(()=>[c[7]||=i(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:t(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":p(({item:e})=>[l(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:p(()=>[i(r(t(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":p(({item:e})=>[a(`span`,T,r(e.admin_username),1)]),"item.target":p(({item:e})=>[a(`span`,E,r(t(x)(e.resource_type))+r(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":p(({item:e})=>[a(`span`,D,r(e.detail||`—`),1)]),"item.ip_address":p(({item:e})=>[a(`span`,O,r(e.ip_address||`—`),1)]),"item.created_at":p(({item:e})=>[a(`span`,k,r(e.created_at),1)]),"no-data":p(()=>[...c[8]||=[a(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{Er as e,F as t,N as n,Nr as r,Ur as i,_r as a,ar as o,er as s,hr as c,or as l,sr as u,t as d,tr as f,zr as p,zt as m}from"./index-HeC-ymfd.js";import{o as h,t as g}from"./VContainer-Bqb4OsjX.js";import{n as _}from"./dataTable-esDY4cOk.js";import{t as v}from"./VChip-C-HpH-oj.js";import{t as y}from"./VSelect-CEXOntEU.js";import{t as b}from"./VDataTableServer-_AO1gvgs.js";import{t as x}from"./VSpacer-C31iLuvS.js";import{a as S,n as C,r as w,t as T}from"./auditNormalize-bmSA0RyN.js";var E={class:`font-weight-medium`},D={class:`text-body-2`},O={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},k={class:`text-caption text-medium-emphasis`},A={class:`text-caption text-medium-emphasis`},j=u({__name:`AuditPage`,setup(u){let j=h(),M=r(!1),N=r([]),P=r(0),F=r(1),I=r(null),L=r(``),R=r(``),z=C,B=_([{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}]);async function V(){M.value=!0;try{let e=(F.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:I.value||void 0,admin_username:L.value||void 0,date_from:R.value||void 0});N.value=T(t.items||[]),P.value=t.total||0}catch{N.value=[],j(`加载审计日志失败`,`error`)}finally{M.value=!1}}function H(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>V()),(r,c)=>(a(),f(g,{fluid:``,class:`pa-6`},{default:e(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:e(()=>[l(t,{class:`d-flex align-center`},{default:e(()=>[c[7]||=o(` 审计日志 `,-1),l(x),l(y,{modelValue:I.value,"onUpdate:modelValue":[c[0]||=e=>I.value=e,c[1]||=e=>{F.value=1,V()}],items:p(z),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[2]||=e=>L.value=e,c[3]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:R.value,"onUpdate:modelValue":[c[4]||=e=>R.value=e,c[5]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(b,{items:N.value,headers:p(B),"items-length":P.value,loading:M.value,page:F.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{F.value=e,V()}},{"item.action":e(({item:t})=>[l(v,{color:H(t.action),size:`small`,variant:`tonal`,label:``},{default:e(()=>[o(i(p(w)(t.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":e(({item:e})=>[s(`span`,E,i(e.admin_username),1)]),"item.target":e(({item:e})=>[s(`span`,D,i(p(S)(e.resource_type))+i(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":e(({item:e})=>[s(`span`,O,i(e.detail||`—`),1)]),"item.ip_address":e(({item:e})=>[s(`span`,k,i(e.ip_address||`—`),1)]),"item.created_at":e(({item:e})=>[s(`span`,A,i(e.created_at),1)]),"no-data":e(()=>[...c[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{Er as e,F as t,N as n,Nr as r,Ur as i,_r as a,ar as o,er as s,hr as c,or as l,sr as u,t as d,tr as f,zr as p,zt as m}from"./index-Hwt84p4T.js";import{o as h,t as g}from"./VContainer-DVdhFFDK.js";import{n as _}from"./dataTable-esDY4cOk.js";import{t as v}from"./VChip-BOlm6rKc.js";import{t as y}from"./VSelect-Ce9_iAcL.js";import{t as b}from"./VDataTableServer-Ch0R1ufY.js";import{t as x}from"./VSpacer-Dz9VwXyE.js";import{a as S,n as C,r as w,t as T}from"./auditNormalize-bmSA0RyN.js";var E={class:`font-weight-medium`},D={class:`text-body-2`},O={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},k={class:`text-caption text-medium-emphasis`},A={class:`text-caption text-medium-emphasis`},j=u({__name:`AuditPage`,setup(u){let j=h(),M=r(!1),N=r([]),P=r(0),F=r(1),I=r(null),L=r(``),R=r(``),z=C,B=_([{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}]);async function V(){M.value=!0;try{let e=(F.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:I.value||void 0,admin_username:L.value||void 0,date_from:R.value||void 0});N.value=T(t.items||[]),P.value=t.total||0}catch{N.value=[],j(`加载审计日志失败`,`error`)}finally{M.value=!1}}function H(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>V()),(r,c)=>(a(),f(g,{fluid:``,class:`pa-6`},{default:e(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:e(()=>[l(t,{class:`d-flex align-center`},{default:e(()=>[c[7]||=o(` 审计日志 `,-1),l(x),l(y,{modelValue:I.value,"onUpdate:modelValue":[c[0]||=e=>I.value=e,c[1]||=e=>{F.value=1,V()}],items:p(z),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[2]||=e=>L.value=e,c[3]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:R.value,"onUpdate:modelValue":[c[4]||=e=>R.value=e,c[5]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(b,{items:N.value,headers:p(B),"items-length":P.value,loading:M.value,page:F.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{F.value=e,V()}},{"item.action":e(({item:t})=>[l(v,{color:H(t.action),size:`small`,variant:`tonal`,label:``},{default:e(()=>[o(i(p(w)(t.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":e(({item:e})=>[s(`span`,E,i(e.admin_username),1)]),"item.target":e(({item:e})=>[s(`span`,D,i(p(S)(e.resource_type))+i(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":e(({item:e})=>[s(`span`,O,i(e.detail||`—`),1)]),"item.ip_address":e(({item:e})=>[s(`span`,k,i(e.ip_address||`—`),1)]),"item.created_at":e(({item:e})=>[s(`span`,A,i(e.created_at),1)]),"no-data":e(()=>[...c[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{_ as e,t,v as n}from"./VSelect-BBtNL0eJ.js";import{B as r,Cr as i,F as a,Gr as o,Mr as s,N as c,R as l,Yr as u,dr as d,fr as f,or as p,pr as m,qt as h,sr as g,t as _,xr as v,zr as y}from"./index-DXg_pn7T.js";import{n as b}from"./dataTable-esDY4cOk.js";import{t as x}from"./VDataTableServer-CAOCfrD0.js";import{a as S,n as C,r as w,t as T}from"./auditNormalize-bmSA0RyN.js";var E={class:`font-weight-medium`},D={class:`text-body-2`},O={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},k={class:`text-caption text-medium-emphasis`},A={class:`text-caption text-medium-emphasis`},j=m({__name:`AuditPage`,setup(m){let j=e(),M=y(!1),N=y([]),P=y(0),F=y(1),I=y(null),L=y(``),R=y(``),z=C,B=b([{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}]);async function V(){M.value=!0;try{let e=(F.value-1)*50,t=await h.get(`/audit/`,{limit:50,offset:e,action:I.value||void 0,admin_username:L.value||void 0,date_from:R.value||void 0});N.value=T(t.items||[]),P.value=t.total||0}catch{N.value=[],j(`加载审计日志失败`,`error`)}finally{M.value=!1}}function H(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return v(()=>V()),(e,m)=>(i(),g(n,{fluid:``,class:`pa-6`},{default:s(()=>[f(l,{elevation:`0`,border:``,rounded:`lg`},{default:s(()=>[f(r,{class:`d-flex align-center`},{default:s(()=>[m[7]||=d(` 审计日志 `,-1),f(c),f(t,{modelValue:I.value,"onUpdate:modelValue":[m[0]||=e=>I.value=e,m[1]||=e=>{F.value=1,V()}],items:o(z),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),f(_,{modelValue:L.value,"onUpdate:modelValue":[m[2]||=e=>L.value=e,m[3]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),f(_,{modelValue:R.value,"onUpdate:modelValue":[m[4]||=e=>R.value=e,m[5]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),f(x,{items:N.value,headers:o(B),"items-length":P.value,loading:M.value,page:F.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":m[6]||=e=>{F.value=e,V()}},{"item.action":s(({item:e})=>[f(a,{color:H(e.action),size:`small`,variant:`tonal`,label:``},{default:s(()=>[d(u(o(w)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":s(({item:e})=>[p(`span`,E,u(e.admin_username),1)]),"item.target":s(({item:e})=>[p(`span`,D,u(o(S)(e.resource_type))+u(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":s(({item:e})=>[p(`span`,O,u(e.detail||`—`),1)]),"item.ip_address":s(({item:e})=>[p(`span`,k,u(e.ip_address||`—`),1)]),"item.created_at":s(({item:e})=>[p(`span`,A,u(e.created_at),1)]),"no-data":s(()=>[...m[8]||=[p(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,N as n,Vr as r,ar as i,er as a,gr as o,jr as s,mr as c,or as l,sr as u,t as d,tr as f,wr as p,zt as m}from"./index-3SWW3Wj_.js";import{o as h,t as g}from"./VContainer-B79RFM4s.js";import{t as _}from"./VChip-CMAdQZRJ.js";import{t as v}from"./VSelect-CNVSTEnc.js";import{t as y}from"./VDataTableServer-mXWi-ccO.js";import{t as b}from"./VSpacer-Lz_Er0LP.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=s(!1),M=s([]),N=s(0),P=s(1),F=s(null),I=s(``),L=s(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(s,c)=>(o(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[l(e,{class:`d-flex align-center`},{default:p(()=>[c[7]||=i(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:t(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":p(({item:e})=>[l(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:p(()=>[i(r(t(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":p(({item:e})=>[a(`span`,T,r(e.admin_username),1)]),"item.target":p(({item:e})=>[a(`span`,E,r(t(x)(e.resource_type))+r(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":p(({item:e})=>[a(`span`,D,r(e.detail||`—`),1)]),"item.ip_address":p(({item:e})=>[a(`span`,O,r(e.ip_address||`—`),1)]),"item.created_at":p(({item:e})=>[a(`span`,k,r(e.created_at),1)]),"no-data":p(()=>[...c[8]||=[a(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{Er as e,F as t,N as n,Nr as r,Ur as i,_r as a,ar as o,er as s,hr as c,or as l,sr as u,t as d,tr as f,zr as p,zt as m}from"./index-Dp4iCzbs.js";import{o as h,t as g}from"./VContainer-BpcBPa4e.js";import{n as _}from"./dataTable-esDY4cOk.js";import{t as v}from"./VChip-DBjCm_hy.js";import{t as y}from"./VSelect-B3aZ6TxZ.js";import{t as b}from"./VDataTableServer-5YTAzdGq.js";import{t as x}from"./VSpacer-Dij81TMC.js";import{a as S,n as C,r as w,t as T}from"./auditNormalize-bmSA0RyN.js";var E={class:`font-weight-medium`},D={class:`text-body-2`},O={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},k={class:`text-caption text-medium-emphasis`},A={class:`text-caption text-medium-emphasis`},j=u({__name:`AuditPage`,setup(u){let j=h(),M=r(!1),N=r([]),P=r(0),F=r(1),I=r(null),L=r(``),R=r(``),z=C,B=_([{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}]);async function V(){M.value=!0;try{let e=(F.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:I.value||void 0,admin_username:L.value||void 0,date_from:R.value||void 0});N.value=T(t.items||[]),P.value=t.total||0}catch{N.value=[],j(`加载审计日志失败`,`error`)}finally{M.value=!1}}function H(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>V()),(r,c)=>(a(),f(g,{fluid:``,class:`pa-6`},{default:e(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:e(()=>[l(t,{class:`d-flex align-center`},{default:e(()=>[c[7]||=o(` 审计日志 `,-1),l(x),l(y,{modelValue:I.value,"onUpdate:modelValue":[c[0]||=e=>I.value=e,c[1]||=e=>{F.value=1,V()}],items:p(z),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[2]||=e=>L.value=e,c[3]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:R.value,"onUpdate:modelValue":[c[4]||=e=>R.value=e,c[5]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(b,{items:N.value,headers:p(B),"items-length":P.value,loading:M.value,page:F.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{F.value=e,V()}},{"item.action":e(({item:t})=>[l(v,{color:H(t.action),size:`small`,variant:`tonal`,label:``},{default:e(()=>[o(i(p(w)(t.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":e(({item:e})=>[s(`span`,E,i(e.admin_username),1)]),"item.target":e(({item:e})=>[s(`span`,D,i(p(S)(e.resource_type))+i(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":e(({item:e})=>[s(`span`,O,i(e.detail||`—`),1)]),"item.ip_address":e(({item:e})=>[s(`span`,k,i(e.ip_address||`—`),1)]),"item.created_at":e(({item:e})=>[s(`span`,A,i(e.created_at),1)]),"no-data":e(()=>[...c[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,N as n,Vr as r,ar as i,er as a,gr as o,jr as s,mr as c,or as l,sr as u,t as d,tr as f,wr as p,zt as m}from"./index-DhXUzl5h.js";import{o as h,t as g}from"./VContainer-CrR_anyV.js";import{t as _}from"./VChip-Bj2Bn9Ad.js";import{t as v}from"./VSelect-B8ti2NSE.js";import{t as y}from"./VDataTableServer-DGuK8add.js";import{t as b}from"./VSpacer-DhDsDhMR.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=s(!1),M=s([]),N=s(0),P=s(1),F=s(null),I=s(``),L=s(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(s,c)=>(o(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[l(e,{class:`d-flex align-center`},{default:p(()=>[c[7]||=i(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:t(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":p(({item:e})=>[l(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:p(()=>[i(r(t(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":p(({item:e})=>[a(`span`,T,r(e.admin_username),1)]),"item.target":p(({item:e})=>[a(`span`,E,r(t(x)(e.resource_type))+r(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":p(({item:e})=>[a(`span`,D,r(e.detail||`—`),1)]),"item.ip_address":p(({item:e})=>[a(`span`,O,r(e.ip_address||`—`),1)]),"item.created_at":p(({item:e})=>[a(`span`,k,r(e.created_at),1)]),"no-data":p(()=>[...c[8]||=[a(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{Er as e,F as t,N as n,Nr as r,Ur as i,_r as a,ar as o,er as s,hr as c,or as l,sr as u,t as d,tr as f,zr as p,zt as m}from"./index-x_UzlvQU.js";import{o as h,t as g}from"./VContainer-9KPDvITd.js";import{n as _}from"./dataTable-esDY4cOk.js";import{t as v}from"./VChip-C55Q03jC.js";import{t as y}from"./VSelect-D6JhGiC7.js";import{t as b}from"./VDataTableServer-2HgZ5qDn.js";import{t as x}from"./VSpacer-JMAll94i.js";import{a as S,n as C,r as w,t as T}from"./auditNormalize-bmSA0RyN.js";var E={class:`font-weight-medium`},D={class:`text-body-2`},O={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},k={class:`text-caption text-medium-emphasis`},A={class:`text-caption text-medium-emphasis`},j=u({__name:`AuditPage`,setup(u){let j=h(),M=r(!1),N=r([]),P=r(0),F=r(1),I=r(null),L=r(``),R=r(``),z=C,B=_([{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}]);async function V(){M.value=!0;try{let e=(F.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:I.value||void 0,admin_username:L.value||void 0,date_from:R.value||void 0});N.value=T(t.items||[]),P.value=t.total||0}catch{N.value=[],j(`加载审计日志失败`,`error`)}finally{M.value=!1}}function H(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>V()),(r,c)=>(a(),f(g,{fluid:``,class:`pa-6`},{default:e(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:e(()=>[l(t,{class:`d-flex align-center`},{default:e(()=>[c[7]||=o(` 审计日志 `,-1),l(x),l(y,{modelValue:I.value,"onUpdate:modelValue":[c[0]||=e=>I.value=e,c[1]||=e=>{F.value=1,V()}],items:p(z),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[2]||=e=>L.value=e,c[3]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:R.value,"onUpdate:modelValue":[c[4]||=e=>R.value=e,c[5]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(b,{items:N.value,headers:p(B),"items-length":P.value,loading:M.value,page:F.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{F.value=e,V()}},{"item.action":e(({item:t})=>[l(v,{color:H(t.action),size:`small`,variant:`tonal`,label:``},{default:e(()=>[o(i(p(w)(t.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":e(({item:e})=>[s(`span`,E,i(e.admin_username),1)]),"item.target":e(({item:e})=>[s(`span`,D,i(p(S)(e.resource_type))+i(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":e(({item:e})=>[s(`span`,O,i(e.detail||`—`),1)]),"item.ip_address":e(({item:e})=>[s(`span`,k,i(e.ip_address||`—`),1)]),"item.created_at":e(({item:e})=>[s(`span`,A,i(e.created_at),1)]),"no-data":e(()=>[...c[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{a as e,o as t}from"./VSelectionControl-EvpbLA8N.js";import{B as n,F as r,Fr as i,Gr as a,N as o,Or as s,R as c,Ut as l,Vr as u,_r as d,cr as f,lr as p,nr as m,rr as h,sr as g,t as _,yr as v}from"./index-B-qGIXx8.js";import{n as y}from"./dataTable-esDY4cOk.js";import{t as b}from"./VSelect-D0D-yfrH.js";import{t as x}from"./VDataTableServer-DCjhqS-Y.js";import{a as S,n as C,r as w,t as T}from"./auditNormalize-bmSA0RyN.js";var E={class:`font-weight-medium`},D={class:`text-body-2`},O={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},k={class:`text-caption text-medium-emphasis`},A={class:`text-caption text-medium-emphasis`},j=p({__name:`AuditPage`,setup(p){let j=e(),M=i(!1),N=i([]),P=i(0),F=i(1),I=i(null),L=i(``),R=i(``),z=C,B=y([{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}]);async function V(){M.value=!0;try{let e=(F.value-1)*50,t=await l.get(`/audit/`,{limit:50,offset:e,action:I.value||void 0,admin_username:L.value||void 0,date_from:R.value||void 0});N.value=T(t.items||[]),P.value=t.total||0}catch{N.value=[],j(`加载审计日志失败`,`error`)}finally{M.value=!1}}function H(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return d(()=>V()),(e,i)=>(v(),h(t,{fluid:``,class:`pa-6`},{default:s(()=>[f(c,{elevation:`0`,border:``,rounded:`lg`},{default:s(()=>[f(n,{class:`d-flex align-center`},{default:s(()=>[i[7]||=g(` 审计日志 `,-1),f(o),f(b,{modelValue:I.value,"onUpdate:modelValue":[i[0]||=e=>I.value=e,i[1]||=e=>{F.value=1,V()}],items:u(z),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),f(_,{modelValue:L.value,"onUpdate:modelValue":[i[2]||=e=>L.value=e,i[3]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),f(_,{modelValue:R.value,"onUpdate:modelValue":[i[4]||=e=>R.value=e,i[5]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),f(x,{items:N.value,headers:u(B),"items-length":P.value,loading:M.value,page:F.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":i[6]||=e=>{F.value=e,V()}},{"item.action":s(({item:e})=>[f(r,{color:H(e.action),size:`small`,variant:`tonal`,label:``},{default:s(()=>[g(a(u(w)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":s(({item:e})=>[m(`span`,E,a(e.admin_username),1)]),"item.target":s(({item:e})=>[m(`span`,D,a(u(S)(e.resource_type))+a(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":s(({item:e})=>[m(`span`,O,a(e.detail||`—`),1)]),"item.ip_address":s(({item:e})=>[m(`span`,k,a(e.ip_address||`—`),1)]),"item.created_at":s(({item:e})=>[m(`span`,A,a(e.created_at),1)]),"no-data":s(()=>[...i[8]||=[m(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{$n as e,Ar as t,Br as n,Cr as r,F as i,Ir as a,N as o,Rt as s,ar as c,er as l,hr as u,ir as d,or as f,pr as p,t as m}from"./index-Da-oPxMv.js";import{o as h,t as g}from"./VContainer-Dp-8rduj.js";import{t as _}from"./VChip-uNN5BiCx.js";import{t as v}from"./VSelect-B1u55nVH.js";import{t as y}from"./VDataTableServer-BLAQDDBi.js";import{t as b}from"./VSpacer-ZMhNXvUl.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-BHJPJFP9.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=f({__name:`AuditPage`,setup(f){let A=h(),j=t(!1),M=t([]),N=t(0),P=t(1),F=t(null),I=t(``),L=t(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await s.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return p(()=>B()),(t,s)=>(u(),l(g,{fluid:``,class:`pa-6`},{default:r(()=>[c(o,{elevation:`0`,border:``,rounded:`lg`},{default:r(()=>[c(i,{class:`d-flex align-center`},{default:r(()=>[s[7]||=d(` 审计日志 `,-1),c(b),c(v,{modelValue:F.value,"onUpdate:modelValue":[s[0]||=e=>F.value=e,s[1]||=e=>{P.value=1,B()}],items:a(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),c(m,{modelValue:I.value,"onUpdate:modelValue":[s[2]||=e=>I.value=e,s[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),c(m,{modelValue:L.value,"onUpdate:modelValue":[s[4]||=e=>L.value=e,s[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),c(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":s[6]||=e=>{P.value=e,B()}},{"item.action":r(({item:e})=>[c(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:r(()=>[d(n(a(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":r(({item:t})=>[e(`span`,T,n(t.admin_username),1)]),"item.target":r(({item:t})=>[e(`span`,E,n(a(x)(t.resource_type))+n(t.resource_id?` #${t.resource_id}`:``),1)]),"item.detail":r(({item:t})=>[e(`span`,D,n(t.detail||`—`),1)]),"item.ip_address":r(({item:t})=>[e(`span`,O,n(t.ip_address||`—`),1)]),"item.created_at":r(({item:t})=>[e(`span`,k,n(t.created_at),1)]),"no-data":r(()=>[...s[8]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{$n as e,Ar as t,Br as n,Cr as r,F as i,Ir as a,N as o,Rt as s,ar as c,er as l,hr as u,ir as d,or as f,pr as p,t as m}from"./index-1vc2lwKY.js";import{o as h,t as g}from"./VContainer-Cyz28tn8.js";import{t as _}from"./VChip-DxlrSIbY.js";import{t as v}from"./VSelect-bmHFJ0Qw.js";import{t as y}from"./VDataTableServer-BgrY7CII.js";import{t as b}from"./VSpacer-DTN9CH1q.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=f({__name:`AuditPage`,setup(f){let A=h(),j=t(!1),M=t([]),N=t(0),P=t(1),F=t(null),I=t(``),L=t(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await s.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return p(()=>B()),(t,s)=>(u(),l(g,{fluid:``,class:`pa-6`},{default:r(()=>[c(o,{elevation:`0`,border:``,rounded:`lg`},{default:r(()=>[c(i,{class:`d-flex align-center`},{default:r(()=>[s[7]||=d(` 审计日志 `,-1),c(b),c(v,{modelValue:F.value,"onUpdate:modelValue":[s[0]||=e=>F.value=e,s[1]||=e=>{P.value=1,B()}],items:a(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),c(m,{modelValue:I.value,"onUpdate:modelValue":[s[2]||=e=>I.value=e,s[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),c(m,{modelValue:L.value,"onUpdate:modelValue":[s[4]||=e=>L.value=e,s[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),c(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":s[6]||=e=>{P.value=e,B()}},{"item.action":r(({item:e})=>[c(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:r(()=>[d(n(a(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":r(({item:t})=>[e(`span`,T,n(t.admin_username),1)]),"item.target":r(({item:t})=>[e(`span`,E,n(a(x)(t.resource_type))+n(t.resource_id?` #${t.resource_id}`:``),1)]),"item.detail":r(({item:t})=>[e(`span`,D,n(t.detail||`—`),1)]),"item.ip_address":r(({item:t})=>[e(`span`,O,n(t.ip_address||`—`),1)]),"item.created_at":r(({item:t})=>[e(`span`,k,n(t.created_at),1)]),"no-data":r(()=>[...s[8]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,N as n,Vr as r,ar as i,er as a,gr as o,jr as s,mr as c,or as l,sr as u,t as d,tr as f,wr as p,zt as m}from"./index-BOX5ad4G.js";import{o as h,t as g}from"./VContainer-CsduC4GT.js";import{t as _}from"./VChip-D0VaTwoJ.js";import{t as v}from"./VSelect-skTyo71B.js";import{t as y}from"./VDataTableServer-CyZWQ374.js";import{t as b}from"./VSpacer-CysfZoCS.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=s(!1),M=s([]),N=s(0),P=s(1),F=s(null),I=s(``),L=s(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(s,c)=>(o(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[l(e,{class:`d-flex align-center`},{default:p(()=>[c[7]||=i(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:t(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":p(({item:e})=>[l(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:p(()=>[i(r(t(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":p(({item:e})=>[a(`span`,T,r(e.admin_username),1)]),"item.target":p(({item:e})=>[a(`span`,E,r(t(x)(e.resource_type))+r(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":p(({item:e})=>[a(`span`,D,r(e.detail||`—`),1)]),"item.ip_address":p(({item:e})=>[a(`span`,O,r(e.ip_address||`—`),1)]),"item.created_at":p(({item:e})=>[a(`span`,k,r(e.created_at),1)]),"no-data":p(()=>[...c[8]||=[a(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{a as e,o as t}from"./VSelectionControl-BQRUjX5P.js";import{B as n,F as r,Fr as i,Gr as a,N as o,Or as s,R as c,Ut as l,Vr as u,_r as d,cr as f,lr as p,nr as m,rr as h,sr as g,t as _,yr as v}from"./index-DbSyD28P.js";import{n as y}from"./dataTable-esDY4cOk.js";import{t as b}from"./VSelect-glh4Zdhv.js";import{t as x}from"./VDataTableServer-BisDXMgG.js";import{a as S,n as C,r as w,t as T}from"./auditNormalize-bmSA0RyN.js";var E={class:`font-weight-medium`},D={class:`text-body-2`},O={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},k={class:`text-caption text-medium-emphasis`},A={class:`text-caption text-medium-emphasis`},j=p({__name:`AuditPage`,setup(p){let j=e(),M=i(!1),N=i([]),P=i(0),F=i(1),I=i(null),L=i(``),R=i(``),z=C,B=y([{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}]);async function V(){M.value=!0;try{let e=(F.value-1)*50,t=await l.get(`/audit/`,{limit:50,offset:e,action:I.value||void 0,admin_username:L.value||void 0,date_from:R.value||void 0});N.value=T(t.items||[]),P.value=t.total||0}catch{N.value=[],j(`加载审计日志失败`,`error`)}finally{M.value=!1}}function H(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return d(()=>V()),(e,i)=>(v(),h(t,{fluid:``,class:`pa-6`},{default:s(()=>[f(c,{elevation:`0`,border:``,rounded:`lg`},{default:s(()=>[f(n,{class:`d-flex align-center`},{default:s(()=>[i[7]||=g(` 审计日志 `,-1),f(o),f(b,{modelValue:I.value,"onUpdate:modelValue":[i[0]||=e=>I.value=e,i[1]||=e=>{F.value=1,V()}],items:u(z),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),f(_,{modelValue:L.value,"onUpdate:modelValue":[i[2]||=e=>L.value=e,i[3]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),f(_,{modelValue:R.value,"onUpdate:modelValue":[i[4]||=e=>R.value=e,i[5]||=e=>{F.value=1,V()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),f(x,{items:N.value,headers:u(B),"items-length":P.value,loading:M.value,page:F.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":i[6]||=e=>{F.value=e,V()}},{"item.action":s(({item:e})=>[f(r,{color:H(e.action),size:`small`,variant:`tonal`,label:``},{default:s(()=>[g(a(u(w)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":s(({item:e})=>[m(`span`,E,a(e.admin_username),1)]),"item.target":s(({item:e})=>[m(`span`,D,a(u(S)(e.resource_type))+a(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":s(({item:e})=>[m(`span`,O,a(e.detail||`—`),1)]),"item.ip_address":s(({item:e})=>[m(`span`,k,a(e.ip_address||`—`),1)]),"item.created_at":s(({item:e})=>[m(`span`,A,a(e.created_at),1)]),"no-data":s(()=>[...i[8]||=[m(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`headers`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{Er as e,F as t,N as n,Nr as r,Ur as i,_r as a,ar as o,er as s,hr as c,or as l,sr as u,t as d,tr as f,zr as p,zt as m}from"./index-DoaDMa1C.js";import{o as h,t as g}from"./VContainer-BQRFgmV8.js";import{t as _}from"./VChip-w-VUeBu6.js";import{t as v}from"./VSelect-lvvpsnJa.js";import{t as y}from"./VDataTableServer-CqSShzYP.js";import{t as b}from"./VSpacer-DctU2jOI.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=r(!1),M=r([]),N=r(0),P=r(1),F=r(null),I=r(``),L=r(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(r,c)=>(a(),f(g,{fluid:``,class:`pa-6`},{default:e(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:e(()=>[l(t,{class:`d-flex align-center`},{default:e(()=>[c[7]||=o(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:p(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":e(({item:t})=>[l(_,{color:V(t.action),size:`small`,variant:`tonal`,label:``},{default:e(()=>[o(i(p(C)(t.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":e(({item:e})=>[s(`span`,T,i(e.admin_username),1)]),"item.target":e(({item:e})=>[s(`span`,E,i(p(x)(e.resource_type))+i(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":e(({item:e})=>[s(`span`,D,i(e.detail||`—`),1)]),"item.ip_address":e(({item:e})=>[s(`span`,O,i(e.ip_address||`—`),1)]),"item.created_at":e(({item:e})=>[s(`span`,k,i(e.created_at),1)]),"no-data":e(()=>[...c[8]||=[s(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,N as n,Vr as r,ar as i,er as a,gr as o,jr as s,mr as c,or as l,sr as u,t as d,tr as f,wr as p,zt as m}from"./index-BoZyi8Cu.js";import{o as h,t as g}from"./VContainer-BT9w_m28.js";import{t as _}from"./VChip-BVR48u8E.js";import{t as v}from"./VSelect-BuZWt-gO.js";import{t as y}from"./VDataTableServer-CjBcrBi4.js";import{t as b}from"./VSpacer-BHLI8VYt.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-bmSA0RyN.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=u({__name:`AuditPage`,setup(u){let A=h(),j=s(!1),M=s([]),N=s(0),P=s(1),F=s(null),I=s(``),L=s(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await m.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return c(()=>B()),(s,c)=>(o(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[l(n,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[l(e,{class:`d-flex align-center`},{default:p(()=>[c[7]||=i(` 审计日志 `,-1),l(b),l(v,{modelValue:F.value,"onUpdate:modelValue":[c[0]||=e=>F.value=e,c[1]||=e=>{P.value=1,B()}],items:t(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),l(d,{modelValue:I.value,"onUpdate:modelValue":[c[2]||=e=>I.value=e,c[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),l(d,{modelValue:L.value,"onUpdate:modelValue":[c[4]||=e=>L.value=e,c[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),l(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":c[6]||=e=>{P.value=e,B()}},{"item.action":p(({item:e})=>[l(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:p(()=>[i(r(t(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":p(({item:e})=>[a(`span`,T,r(e.admin_username),1)]),"item.target":p(({item:e})=>[a(`span`,E,r(t(x)(e.resource_type))+r(e.resource_id?` #${e.resource_id}`:``),1)]),"item.detail":p(({item:e})=>[a(`span`,D,r(e.detail||`—`),1)]),"item.ip_address":p(({item:e})=>[a(`span`,O,r(e.ip_address||`—`),1)]),"item.created_at":p(({item:e})=>[a(`span`,k,r(e.created_at),1)]),"no-data":p(()=>[...c[8]||=[a(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
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
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{$n as e,Ar as t,Br as n,Cr as r,F as i,Ir as a,M as o,N as s,Rt as c,ar as l,er as u,hr as d,ir as f,or as p,pr as m}from"./index-1vc2lwKY.js";import{o as h,t as g}from"./VContainer-Cyz28tn8.js";import{t as _}from"./VChip-DxlrSIbY.js";import{t as v}from"./VSelect-bmHFJ0Qw.js";import{t as y}from"./VDataTableServer-BgrY7CII.js";import{t as b}from"./VSpacer-DTN9CH1q.js";import{t as x}from"./apiError-CCaH8lNC.js";import{t as S}from"./useServerList-BTHLukDM.js";var C={class:`text-body-2`},w={class:`text-medium-emphasis`},T={class:`text-medium-emphasis`},E={class:`text-caption text-medium-emphasis`},D={class:`font-weight-medium`},O={class:`text-caption text-medium-emphasis`},k=p({__name:`CommandsPage`,setup(p){let k=h(),{servers:A,loadServers:j}=S(),M=t(!1),N=t(`commands`),P=t(1),F=t(0),I=t(30),L=t(null),R=t([]),z=t([]),B=[{title:`命令`,key:`command`},{title:`服务器`,key:`server_name`},{title:`用户`,key:`admin_username`,width:100},{title:`时间`,key:`created_at`,width:160}],V=[{title:`服务器`,key:`server_name`},{title:`状态`,key:`status`,width:80},{title:`来源`,key:`remote_addr`,width:120},{title:`开始时间`,key:`started_at`,width:160}];async function H(){M.value=!0;try{if(N.value===`commands`){let e=await c.getList(`/assets/command-logs`,{server_id:L.value||void 0,page:P.value,per_page:I.value});R.value=e.items,F.value=e.total}else{let e=await c.getList(`/assets/ssh-sessions`,{server_id:L.value||void 0,page:P.value,per_page:I.value});z.value=e.items,F.value=e.total}}catch(e){R.value=[],z.value=[],k(x(e,`加载命令日志失败`),`error`)}finally{M.value=!1}}return m(()=>{j(),H()}),(t,c)=>(d(),u(g,{fluid:``,class:`pa-6`},{default:r(()=>[l(s,{elevation:`0`,border:``,rounded:`lg`},{default:r(()=>[l(i,{class:`d-flex align-center`},{default:r(()=>[c[4]||=f(` 命令日志 `,-1),l(b),l(v,{modelValue:N.value,"onUpdate:modelValue":[c[0]||=e=>N.value=e,H],items:[{title:`命令视图`,value:`commands`},{title:`会话视图`,value:`sessions`}],"item-title":`title`,"item-value":`value`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`}},null,8,[`modelValue`]),l(v,{modelValue:L.value,"onUpdate:modelValue":[c[1]||=e=>L.value=e,H],items:a(A),"item-title":`name`,"item-value":`id`,label:`服务器`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`180px`},class:`ml-3`,clearable:``},null,8,[`modelValue`,`items`])]),_:1}),l(o),N.value===`commands`?(d(),u(y,{key:0,items:R.value,headers:B,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":c[2]||=e=>{P.value=e,H()}},{"item.command":r(({item:t})=>[e(`code`,C,n(t.command),1)]),"item.server_name":r(({item:t})=>[e(`span`,w,n(t.server_name||`—`),1)]),"item.admin_username":r(({item:t})=>[e(`span`,T,n(t.admin_username||`—`),1)]),"item.created_at":r(({item:t})=>[e(`span`,E,n(t.created_at),1)]),"no-data":r(()=>[...c[5]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])):(d(),u(y,{key:1,items:z.value,headers:V,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":c[3]||=e=>{P.value=e,H()}},{"item.server_name":r(({item:t})=>[e(`span`,D,n(t.server_name||`—`),1)]),"item.status":r(({item:e})=>[l(_,{color:e.status===`closed`?`grey`:`success`,size:`small`,variant:`tonal`,label:``},{default:r(()=>[f(n(e.status),1)]),_:2},1032,[`color`])]),"item.started_at":r(({item:t})=>[e(`span`,O,n(t.started_at),1)]),"no-data":r(()=>[...c[6]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`]))]),_:1})]),_:1}))}});export{k as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,M as n,N as r,Vr as i,ar as a,er as o,gr as s,jr as c,mr as l,or as u,sr as d,tr as f,wr as p,zt as m}from"./index-BOX5ad4G.js";import{o as h,t as g}from"./VContainer-CsduC4GT.js";import{t as _}from"./VChip-D0VaTwoJ.js";import{t as v}from"./VSelect-skTyo71B.js";import{t as y}from"./VDataTableServer-CyZWQ374.js";import{t as b}from"./VSpacer-CysfZoCS.js";import{t as x}from"./apiError-D87YOHDz.js";import{t as S}from"./useServerList-EQ3n4ASB.js";var C={class:`text-body-2`},w={class:`text-medium-emphasis`},T={class:`text-medium-emphasis`},E={class:`text-caption text-medium-emphasis`},D={class:`font-weight-medium`},O={class:`text-caption text-medium-emphasis`},k=d({__name:`CommandsPage`,setup(d){let k=h(),{servers:A,loadServers:j}=S(),M=c(!1),N=c(`commands`),P=c(1),F=c(0),I=c(30),L=c(null),R=c([]),z=c([]),B=[{title:`命令`,key:`command`},{title:`服务器`,key:`server_name`},{title:`用户`,key:`admin_username`,width:100},{title:`时间`,key:`created_at`,width:160}],V=[{title:`服务器`,key:`server_name`},{title:`状态`,key:`status`,width:80},{title:`来源`,key:`remote_addr`,width:120},{title:`开始时间`,key:`started_at`,width:160}];async function H(){M.value=!0;try{if(N.value===`commands`){let e=await m.getList(`/assets/command-logs`,{server_id:L.value||void 0,page:P.value,per_page:I.value});R.value=e.items,F.value=e.total}else{let e=await m.getList(`/assets/ssh-sessions`,{server_id:L.value||void 0,page:P.value,per_page:I.value});z.value=e.items,F.value=e.total}}catch(e){R.value=[],z.value=[],k(x(e,`加载命令日志失败`),`error`)}finally{M.value=!1}}return l(()=>{j(),H()}),(c,l)=>(s(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[u(r,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[u(e,{class:`d-flex align-center`},{default:p(()=>[l[4]||=a(` 命令日志 `,-1),u(b),u(v,{modelValue:N.value,"onUpdate:modelValue":[l[0]||=e=>N.value=e,H],items:[{title:`命令视图`,value:`commands`},{title:`会话视图`,value:`sessions`}],"item-title":`title`,"item-value":`value`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`}},null,8,[`modelValue`]),u(v,{modelValue:L.value,"onUpdate:modelValue":[l[1]||=e=>L.value=e,H],items:t(A),"item-title":`name`,"item-value":`id`,label:`服务器`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`180px`},class:`ml-3`,clearable:``},null,8,[`modelValue`,`items`])]),_:1}),u(n),N.value===`commands`?(s(),f(y,{key:0,items:R.value,headers:B,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":l[2]||=e=>{P.value=e,H()}},{"item.command":p(({item:e})=>[o(`code`,C,i(e.command),1)]),"item.server_name":p(({item:e})=>[o(`span`,w,i(e.server_name||`—`),1)]),"item.admin_username":p(({item:e})=>[o(`span`,T,i(e.admin_username||`—`),1)]),"item.created_at":p(({item:e})=>[o(`span`,E,i(e.created_at),1)]),"no-data":p(()=>[...l[5]||=[o(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])):(s(),f(y,{key:1,items:z.value,headers:V,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":l[3]||=e=>{P.value=e,H()}},{"item.server_name":p(({item:e})=>[o(`span`,D,i(e.server_name||`—`),1)]),"item.status":p(({item:e})=>[u(_,{color:e.status===`closed`?`grey`:`success`,size:`small`,variant:`tonal`,label:``},{default:p(()=>[a(i(e.status),1)]),_:2},1032,[`color`])]),"item.started_at":p(({item:e})=>[o(`span`,O,i(e.started_at),1)]),"no-data":p(()=>[...l[6]||=[o(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`]))]),_:1})]),_:1}))}});export{k as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,M as n,N as r,Vr as i,ar as a,er as o,gr as s,jr as c,mr as l,or as u,sr as d,tr as f,wr as p,zt as m}from"./index-DhXUzl5h.js";import{o as h,t as g}from"./VContainer-CrR_anyV.js";import{t as _}from"./VChip-Bj2Bn9Ad.js";import{t as v}from"./VSelect-B8ti2NSE.js";import{t as y}from"./VDataTableServer-DGuK8add.js";import{t as b}from"./VSpacer-DhDsDhMR.js";import{t as x}from"./apiError-CNIyCFzv.js";import{t as S}from"./useServerList-mVKIkYtT.js";var C={class:`text-body-2`},w={class:`text-medium-emphasis`},T={class:`text-medium-emphasis`},E={class:`text-caption text-medium-emphasis`},D={class:`font-weight-medium`},O={class:`text-caption text-medium-emphasis`},k=d({__name:`CommandsPage`,setup(d){let k=h(),{servers:A,loadServers:j}=S(),M=c(!1),N=c(`commands`),P=c(1),F=c(0),I=c(30),L=c(null),R=c([]),z=c([]),B=[{title:`命令`,key:`command`},{title:`服务器`,key:`server_name`},{title:`用户`,key:`admin_username`,width:100},{title:`时间`,key:`created_at`,width:160}],V=[{title:`服务器`,key:`server_name`},{title:`状态`,key:`status`,width:80},{title:`来源`,key:`remote_addr`,width:120},{title:`开始时间`,key:`started_at`,width:160}];async function H(){M.value=!0;try{if(N.value===`commands`){let e=await m.getList(`/assets/command-logs`,{server_id:L.value||void 0,page:P.value,per_page:I.value});R.value=e.items,F.value=e.total}else{let e=await m.getList(`/assets/ssh-sessions`,{server_id:L.value||void 0,page:P.value,per_page:I.value});z.value=e.items,F.value=e.total}}catch(e){R.value=[],z.value=[],k(x(e,`加载命令日志失败`),`error`)}finally{M.value=!1}}return l(()=>{j(),H()}),(c,l)=>(s(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[u(r,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[u(e,{class:`d-flex align-center`},{default:p(()=>[l[4]||=a(` 命令日志 `,-1),u(b),u(v,{modelValue:N.value,"onUpdate:modelValue":[l[0]||=e=>N.value=e,H],items:[{title:`命令视图`,value:`commands`},{title:`会话视图`,value:`sessions`}],"item-title":`title`,"item-value":`value`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`}},null,8,[`modelValue`]),u(v,{modelValue:L.value,"onUpdate:modelValue":[l[1]||=e=>L.value=e,H],items:t(A),"item-title":`name`,"item-value":`id`,label:`服务器`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`180px`},class:`ml-3`,clearable:``},null,8,[`modelValue`,`items`])]),_:1}),u(n),N.value===`commands`?(s(),f(y,{key:0,items:R.value,headers:B,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":l[2]||=e=>{P.value=e,H()}},{"item.command":p(({item:e})=>[o(`code`,C,i(e.command),1)]),"item.server_name":p(({item:e})=>[o(`span`,w,i(e.server_name||`—`),1)]),"item.admin_username":p(({item:e})=>[o(`span`,T,i(e.admin_username||`—`),1)]),"item.created_at":p(({item:e})=>[o(`span`,E,i(e.created_at),1)]),"no-data":p(()=>[...l[5]||=[o(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])):(s(),f(y,{key:1,items:z.value,headers:V,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":l[3]||=e=>{P.value=e,H()}},{"item.server_name":p(({item:e})=>[o(`span`,D,i(e.server_name||`—`),1)]),"item.status":p(({item:e})=>[u(_,{color:e.status===`closed`?`grey`:`success`,size:`small`,variant:`tonal`,label:``},{default:p(()=>[a(i(e.status),1)]),_:2},1032,[`color`])]),"item.started_at":p(({item:e})=>[o(`span`,O,i(e.started_at),1)]),"no-data":p(()=>[...l[6]||=[o(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`]))]),_:1})]),_:1}))}});export{k as default};
|
||||
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
@@ -0,0 +1 @@
|
||||
import{F as e,Lr as t,M as n,N as r,Vr as i,ar as a,er as o,gr as s,jr as c,mr as l,or as u,sr as d,tr as f,wr as p,zt as m}from"./index-8R5Vx1WH.js";import{o as h,t as g}from"./VContainer-ByAXNVXy.js";import{t as _}from"./VChip-CO9HT0ch.js";import{t as v}from"./VSelect-BB7JEkD3.js";import{t as y}from"./VDataTableServer-Bx8d7a_i.js";import{t as b}from"./VSpacer-CTDq7Cy_.js";import{t as x}from"./apiError-DdoXm_FH.js";import{t as S}from"./useServerList-Xwm-w1s9.js";var C={class:`text-body-2`},w={class:`text-medium-emphasis`},T={class:`text-medium-emphasis`},E={class:`text-caption text-medium-emphasis`},D={class:`font-weight-medium`},O={class:`text-caption text-medium-emphasis`},k=d({__name:`CommandsPage`,setup(d){let k=h(),{servers:A,loadServers:j}=S(),M=c(!1),N=c(`commands`),P=c(1),F=c(0),I=c(30),L=c(null),R=c([]),z=c([]),B=[{title:`命令`,key:`command`},{title:`服务器`,key:`server_name`},{title:`用户`,key:`admin_username`,width:100},{title:`时间`,key:`created_at`,width:160}],V=[{title:`服务器`,key:`server_name`},{title:`状态`,key:`status`,width:80},{title:`来源`,key:`remote_addr`,width:120},{title:`开始时间`,key:`started_at`,width:160}];async function H(){M.value=!0;try{if(N.value===`commands`){let e=await m.getList(`/assets/command-logs`,{server_id:L.value||void 0,page:P.value,per_page:I.value});R.value=e.items,F.value=e.total}else{let e=await m.getList(`/assets/ssh-sessions`,{server_id:L.value||void 0,page:P.value,per_page:I.value});z.value=e.items,F.value=e.total}}catch(e){R.value=[],z.value=[],k(x(e,`加载命令日志失败`),`error`)}finally{M.value=!1}}return l(()=>{j(),H()}),(c,l)=>(s(),f(g,{fluid:``,class:`pa-6`},{default:p(()=>[u(r,{elevation:`0`,border:``,rounded:`lg`},{default:p(()=>[u(e,{class:`d-flex align-center`},{default:p(()=>[l[4]||=a(` 命令日志 `,-1),u(b),u(v,{modelValue:N.value,"onUpdate:modelValue":[l[0]||=e=>N.value=e,H],items:[{title:`命令视图`,value:`commands`},{title:`会话视图`,value:`sessions`}],"item-title":`title`,"item-value":`value`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`}},null,8,[`modelValue`]),u(v,{modelValue:L.value,"onUpdate:modelValue":[l[1]||=e=>L.value=e,H],items:t(A),"item-title":`name`,"item-value":`id`,label:`服务器`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`180px`},class:`ml-3`,clearable:``},null,8,[`modelValue`,`items`])]),_:1}),u(n),N.value===`commands`?(s(),f(y,{key:0,items:R.value,headers:B,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":l[2]||=e=>{P.value=e,H()}},{"item.command":p(({item:e})=>[o(`code`,C,i(e.command),1)]),"item.server_name":p(({item:e})=>[o(`span`,w,i(e.server_name||`—`),1)]),"item.admin_username":p(({item:e})=>[o(`span`,T,i(e.admin_username||`—`),1)]),"item.created_at":p(({item:e})=>[o(`span`,E,i(e.created_at),1)]),"no-data":p(()=>[...l[5]||=[o(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])):(s(),f(y,{key:1,items:z.value,headers:V,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":l[3]||=e=>{P.value=e,H()}},{"item.server_name":p(({item:e})=>[o(`span`,D,i(e.server_name||`—`),1)]),"item.status":p(({item:e})=>[u(_,{color:e.status===`closed`?`grey`:`success`,size:`small`,variant:`tonal`,label:``},{default:p(()=>[a(i(e.status),1)]),_:2},1032,[`color`])]),"item.started_at":p(({item:e})=>[o(`span`,O,i(e.started_at),1)]),"no-data":p(()=>[...l[6]||=[o(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`]))]),_:1})]),_:1}))}});export{k as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{$n as e,Ar as t,Br as n,Cr as r,F as i,Ir as a,M as o,N as s,Rt as c,ar as l,er as u,hr as d,ir as f,or as p,pr as m}from"./index-Da-oPxMv.js";import{o as h,t as g}from"./VContainer-Dp-8rduj.js";import{t as _}from"./VChip-uNN5BiCx.js";import{t as v}from"./VSelect-B1u55nVH.js";import{t as y}from"./VDataTableServer-BLAQDDBi.js";import{t as b}from"./VSpacer-ZMhNXvUl.js";import{t as x}from"./apiError-DICy4JAA.js";import{t as S}from"./useServerList-Dj3PjUZW.js";var C={class:`text-body-2`},w={class:`text-medium-emphasis`},T={class:`text-medium-emphasis`},E={class:`text-caption text-medium-emphasis`},D={class:`font-weight-medium`},O={class:`text-caption text-medium-emphasis`},k=p({__name:`CommandsPage`,setup(p){let k=h(),{servers:A,loadServers:j}=S(),M=t(!1),N=t(`commands`),P=t(1),F=t(0),I=t(30),L=t(null),R=t([]),z=t([]),B=[{title:`命令`,key:`command`},{title:`服务器`,key:`server_name`},{title:`用户`,key:`admin_username`,width:100},{title:`时间`,key:`created_at`,width:160}],V=[{title:`服务器`,key:`server_name`},{title:`状态`,key:`status`,width:80},{title:`来源`,key:`remote_addr`,width:120},{title:`开始时间`,key:`started_at`,width:160}];async function H(){M.value=!0;try{if(N.value===`commands`){let e=await c.getList(`/assets/command-logs`,{server_id:L.value||void 0,page:P.value,per_page:I.value});R.value=e.items,F.value=e.total}else{let e=await c.getList(`/assets/ssh-sessions`,{server_id:L.value||void 0,page:P.value,per_page:I.value});z.value=e.items,F.value=e.total}}catch(e){R.value=[],z.value=[],k(x(e,`加载命令日志失败`),`error`)}finally{M.value=!1}}return m(()=>{j(),H()}),(t,c)=>(d(),u(g,{fluid:``,class:`pa-6`},{default:r(()=>[l(s,{elevation:`0`,border:``,rounded:`lg`},{default:r(()=>[l(i,{class:`d-flex align-center`},{default:r(()=>[c[4]||=f(` 命令日志 `,-1),l(b),l(v,{modelValue:N.value,"onUpdate:modelValue":[c[0]||=e=>N.value=e,H],items:[{title:`命令视图`,value:`commands`},{title:`会话视图`,value:`sessions`}],"item-title":`title`,"item-value":`value`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`}},null,8,[`modelValue`]),l(v,{modelValue:L.value,"onUpdate:modelValue":[c[1]||=e=>L.value=e,H],items:a(A),"item-title":`name`,"item-value":`id`,label:`服务器`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`180px`},class:`ml-3`,clearable:``},null,8,[`modelValue`,`items`])]),_:1}),l(o),N.value===`commands`?(d(),u(y,{key:0,items:R.value,headers:B,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":c[2]||=e=>{P.value=e,H()}},{"item.command":r(({item:t})=>[e(`code`,C,n(t.command),1)]),"item.server_name":r(({item:t})=>[e(`span`,w,n(t.server_name||`—`),1)]),"item.admin_username":r(({item:t})=>[e(`span`,T,n(t.admin_username||`—`),1)]),"item.created_at":r(({item:t})=>[e(`span`,E,n(t.created_at),1)]),"no-data":r(()=>[...c[5]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])):(d(),u(y,{key:1,items:z.value,headers:V,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":c[3]||=e=>{P.value=e,H()}},{"item.server_name":r(({item:t})=>[e(`span`,D,n(t.server_name||`—`),1)]),"item.status":r(({item:e})=>[l(_,{color:e.status===`closed`?`grey`:`success`,size:`small`,variant:`tonal`,label:``},{default:r(()=>[f(n(e.status),1)]),_:2},1032,[`color`])]),"item.started_at":r(({item:t})=>[e(`span`,O,n(t.started_at),1)]),"no-data":r(()=>[...c[6]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`]))]),_:1})]),_:1}))}});export{k as default};
|
||||
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
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
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
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user