- Fix runAiEnhance missing return/closing brace from previous commit - Remove duplicate code fragments in worker.js - Add renderPhotoPending() to show pending/processing jobs in worker dashboard - New pending queue table in worker.html photo section
192 lines
9.8 KiB
HTML
192 lines
9.8 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Воркер ИИ — Админ-панель</title>
|
||
<link rel="stylesheet" href="admin.css">
|
||
<style>
|
||
.enhance-compare { position: relative; border-radius: 10px; overflow: hidden; background: var(--bg); border: 1px solid var(--border); user-select: none; touch-action: none; cursor: ew-resize; }
|
||
.enhance-stage { transform-origin: 0 0; }
|
||
.enhance-stage > img { display: block; width: 100%; height: auto; }
|
||
.enhance-top { position: absolute; inset: 0; overflow: hidden; }
|
||
.enhance-top .enhance-stage { position: absolute; inset: 0; }
|
||
.enhance-top .enhance-stage > img { height: 100%; object-fit: fill; }
|
||
.enhance-divider { position: absolute; top: 0; bottom: 0; left: 50%; width: 32px; transform: translateX(-50%); cursor: ew-resize; }
|
||
.enhance-divider::before { content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; transform: translateX(-50%); background: #fff; box-shadow: 0 0 6px rgba(0,0,0,.6); pointer-events: none; }
|
||
.enhance-handle { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 34px; height: 34px; border-radius: 50%; background: #fff; color: #333; display: flex; align-items: center; justify-content: center; font-size: 1rem; box-shadow: 0 2px 8px rgba(0,0,0,.4); pointer-events: none; }
|
||
.enhance-badge { position: absolute; top: 10px; padding: 3px 10px; border-radius: 999px; background: rgba(0,0,0,.55); color: #fff; font-size: .72rem; pointer-events: none; }
|
||
.enhance-badge.left { left: 10px; }
|
||
.enhance-badge.right { right: 10px; }
|
||
.photo-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border); display: block; }
|
||
</style>
|
||
</head>
|
||
<body data-page="worker">
|
||
<div class="layout">
|
||
<div class="sidebar" id="sidebar"></div>
|
||
<div class="main">
|
||
<div class="page-head">
|
||
<h2>Воркер ИИ</h2>
|
||
<p class="page-sub">Отслеживание автоматической проверки сообщений записей</p>
|
||
</div>
|
||
|
||
<div class="actions-row">
|
||
<span class="worker-state" id="stateLine"><span class="worker-dot"></span> Загрузка…</span>
|
||
<span style="flex:1"></span>
|
||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:.85rem">
|
||
<input type="checkbox" id="autoRefresh"> Автообновление
|
||
</label>
|
||
<button class="btn-link" id="refreshBtn" type="button">Обновить</button>
|
||
<button class="btn-link" id="wakeBtn" type="button" title="Запустить обработку очереди немедленно">Разбудить</button>
|
||
<button class="btn-link" id="requeueBtn" type="button" title="Вернуть все записи с ошибкой в очередь">Перепроверить ошибки</button>
|
||
<button class="btn-link" id="photoWakeBtn" type="button" title="Разбудить фото-воркер">📷 Разбудить фото</button>
|
||
<button class="btn-link" id="photoToggleBtn" type="button">📷 …</button>
|
||
<button class="btn-link" id="toggleBtn" type="button">…</button>
|
||
</div>
|
||
|
||
<div class="sys-grid" id="workerCards" style="margin-top:16px">
|
||
<div class="sys-card loading">Загрузка…</div>
|
||
</div>
|
||
|
||
<div class="worker-counts" id="workerCounts"></div>
|
||
|
||
<div class="settings-stack settings-stack-grid" style="margin-top:20px">
|
||
<div class="settings-card wide">
|
||
<div class="card-head">
|
||
<div class="ic"><i data-lucide="hourglass"></i></div>
|
||
<h3>Очередь (<span id="pendingCount">0</span>)</h3>
|
||
</div>
|
||
<p class="hint">Записи, ожидающие проверки моделью. Обновляются автоматически.</p>
|
||
<div class="audit-wrap">
|
||
<table class="audit-table" id="pendingTable">
|
||
<thead>
|
||
<tr><th>ID</th><th>Ученик</th><th>Группа</th><th>Создана</th></tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
<div class="empty" id="pendingEmpty" style="display:none">Очередь пуста.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-card wide">
|
||
<div class="card-head">
|
||
<div class="ic"><i data-lucide="history"></i></div>
|
||
<h3>Последние проверки</h3>
|
||
</div>
|
||
<div class="actions-row" style="margin-bottom:10px">
|
||
<select id="recentFilter" class="settings-input" style="max-width:220px">
|
||
<option value="">Все статусы</option>
|
||
<option value="done">Проверено</option>
|
||
<option value="skipped">Пропущено</option>
|
||
<option value="error">Ошибка</option>
|
||
<option value="reverted">Возвращён оригинал</option>
|
||
</select>
|
||
<span class="hint" id="recentCount"></span>
|
||
</div>
|
||
<div class="audit-wrap">
|
||
<table class="audit-table" id="recentTable">
|
||
<thead>
|
||
<tr><th>Проверено</th><th>Ученик</th><th>Группа</th><th>Статус</th><th>Изменения</th><th>Детали</th></tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
<div class="empty" id="recentEmpty" style="display:none">Проверок пока нет.</div>
|
||
</div>
|
||
<div class="pager" id="recentPager"></div>
|
||
</div>
|
||
|
||
<div class="settings-card wide">
|
||
<div class="card-head">
|
||
<div class="ic"><i data-lucide="image"></i></div>
|
||
<h3>Обработка фотографий</h3>
|
||
</div>
|
||
<div class="actions-row" style="margin-bottom:10px">
|
||
<span class="hint" id="photoStateHint"></span>
|
||
<span style="flex:1"></span>
|
||
<button class="btn-link" id="photoRequeueBtn" type="button" title="Вернуть задания с ошибкой в очередь">Повторить ошибки фото</button>
|
||
</div>
|
||
<div class="worker-counts" id="photoCounts" style="margin-bottom:10px"></div>
|
||
<p class="hint">В очереди (<span id="photoPendingCount">0</span>)</p>
|
||
<div class="audit-wrap" style="margin-bottom:14px">
|
||
<table class="audit-table" id="photoPendingTable">
|
||
<thead>
|
||
<tr><th>Задание</th><th>Запись</th><th>Действие</th><th>Поставлено</th></tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
<div class="empty" id="photoPendingEmpty" style="display:none">Очередь пуста.</div>
|
||
</div>
|
||
<div class="audit-wrap">
|
||
<table class="audit-table" id="photoTable">
|
||
<thead>
|
||
<tr><th>Время</th><th>Запись</th><th>Действие</th><th>Статус</th><th>Было</th><th>Стало</th><th>Ошибка</th><th></th></tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
<div class="empty" id="photoEmpty" style="display:none">Обработок фотографий пока нет.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="settings-card wide" id="errorsCard" style="display:none">
|
||
<div class="card-head">
|
||
<div class="ic"><i data-lucide="triangle-alert"></i></div>
|
||
<h3>Ошибки (<span id="errorsCount">0</span>)</h3>
|
||
</div>
|
||
<p class="hint">Записи, которые не удалось обработать после всех попыток. Нажмите «Перепроверить ошибки», чтобы запустить их снова.</p>
|
||
<div class="audit-wrap">
|
||
<table class="audit-table" id="errorsTable">
|
||
<thead>
|
||
<tr><th>Время</th><th>ID</th><th>Ученик</th><th>Ошибка</th></tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-overlay" id="aiDetailModal">
|
||
<div class="edit-modal" style="max-width:680px">
|
||
<h3 id="aiDetailTitle">Детали ИИ-проверки</h3>
|
||
<div class="ai-detail-block">
|
||
<div class="ai-detail-label">Отправлено в модель</div>
|
||
<pre id="aiDetailInput"></pre>
|
||
</div>
|
||
<div class="ai-detail-block">
|
||
<div class="ai-detail-label">Ответ модели</div>
|
||
<pre id="aiDetailOutput"></pre>
|
||
</div>
|
||
<div class="card-foot" style="justify-content:flex-end;gap:8px">
|
||
<button type="button" class="cancel" id="aiDetailCloseBtn">Закрыть</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-overlay" id="photoJobModal">
|
||
<div class="edit-modal" style="max-width:900px">
|
||
<h3 id="photoJobTitle">Обработка фотографии</h3>
|
||
<div class="enhance-compare" id="photoJobCompare">
|
||
<div class="enhance-stage">
|
||
<img id="photoJobBefore" alt="Было">
|
||
</div>
|
||
<div class="enhance-top" id="photoJobTop" style="clip-path:inset(0 0 0 50%)">
|
||
<div class="enhance-stage enhance-stage-fill">
|
||
<img id="photoJobAfter" alt="Стало">
|
||
</div>
|
||
</div>
|
||
<div class="enhance-divider" id="photoJobDivider"><div class="enhance-handle">⇔</div></div>
|
||
<div class="enhance-badge left">Было</div>
|
||
<div class="enhance-badge right">Стало</div>
|
||
</div>
|
||
<div class="card-foot" style="justify-content:flex-end;gap:8px;margin-top:14px">
|
||
<button type="button" class="btn-primary ghost" id="photoJobCloseBtn">Закрыть</button>
|
||
<button type="button" class="btn-primary" id="photoJobRollbackBtn">↩ Откатить к версии «Было»</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="toast" id="toast"></div>
|
||
<script src="vendor/lucide.min.js"></script>
|
||
<script src="admin.js"></script>
|
||
<script src="js/worker.js"></script>
|
||
</body>
|
||
</html>
|