From 05f7be71ef6eec811f164adb50cef51a5bed556d Mon Sep 17 00:00:00 2001 From: hubian <908234780@qq.com> Date: Thu, 16 Apr 2026 11:34:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=87=8D=E5=86=99=20app.js=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E4=BC=A0=E7=BB=9F=E8=AF=AD=E6=B3=95=E9=81=BF=E5=85=8D?= =?UTF-8?q?=20Windows=20=E7=BC=96=E7=A0=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/static/app.js | 573 ++++++++++++++++------------------------------ 1 file changed, 195 insertions(+), 378 deletions(-) diff --git a/web/static/app.js b/web/static/app.js index 2e22f10..1608855 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -1,14 +1,11 @@ -// 视觉记录系统前端逻辑 +// Vision Record System - Frontend Logic -const API_BASE = ''; -let currentImageId = null; -let refreshTimer = null; -let refreshInterval = 5; +var API_BASE = ''; +var currentImageId = null; +var refreshTimer = null; -// ============== 初始化 ============== - -document.addEventListener('DOMContentLoaded', () => { - loadConfig(); +// Initialize +document.addEventListener('DOMContentLoaded', function() { refreshAll(); startAutoRefresh(); }); @@ -17,342 +14,178 @@ function startAutoRefresh() { if (refreshTimer) { clearInterval(refreshTimer); } - refreshTimer = setInterval(refreshAll, refreshInterval * 1000); - document.getElementById('refresh-info').textContent = `刷新间隔: ${refreshInterval}秒`; + refreshTimer = setInterval(refreshAll, 5000); } function refreshAll() { loadStatus(); - loadDateFolders(); loadImages(); loadEvents(); } -// ============== 配置相关 ============== - -async function loadConfig() { - try { - const res = await fetch(`${API_BASE}/api/config`); - const config = await res.json(); - - refreshInterval = config.refresh_interval || 5; - startAutoRefresh(); - - // 更新显示数量提示 - document.getElementById('display-limit-note')?.textContent = - `显示最近 ${config.display_limit || 20} 条`; - } catch (e) { - console.error('加载配置失败:', e); - } -} - -function openSettingsModal() { - loadSettingsForm(); - document.getElementById('settings-modal').classList.add('active'); -} - -function closeSettingsModal() { - document.getElementById('settings-modal').classList.remove('active'); -} - -async function loadSettingsForm() { - try { - const res = await fetch(`${API_BASE}/api/config`); - const config = await res.json(); - - document.getElementById('setting-images-dir').value = config.images_dir || ''; - document.getElementById('setting-interval').value = config.capture_interval || 60; - document.getElementById('setting-camera-index').value = config.camera_index || 0; - document.getElementById('setting-auto-analyze').checked = config.auto_analyze !== false; - document.getElementById('setting-display-limit').value = config.display_limit || 20; - document.getElementById('setting-refresh-interval').value = config.refresh_interval || 5; - document.getElementById('setting-api-url').value = config.vision_api_url || ''; - document.getElementById('setting-api-key').value = config.vision_api_key || ''; - document.getElementById('setting-model').value = config.vision_model || ''; - } catch (e) { - console.error('加载设置失败:', e); - } -} - -async function saveSettings() { - const settings = { - images_dir: document.getElementById('setting-images-dir').value, - capture_interval: parseInt(document.getElementById('setting-interval').value), - camera_index: parseInt(document.getElementById('setting-camera-index').value), - auto_analyze: document.getElementById('setting-auto-analyze').checked, - display_limit: parseInt(document.getElementById('setting-display-limit').value), - refresh_interval: parseInt(document.getElementById('setting-refresh-interval').value), - vision_api_url: document.getElementById('setting-api-url').value, - vision_api_key: document.getElementById('setting-api-key').value, - vision_model: document.getElementById('setting-model').value - }; - - try { - const res = await fetch(`${API_BASE}/api/config`, { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(settings) +// Status +function loadStatus() { + fetch(API_BASE + '/api/status') + .then(function(res) { return res.json(); }) + .then(function(data) { + updateUI(data); + }) + .catch(function(e) { + console.error('Load status failed:', e); }); - const data = await res.json(); - - if (data.success) { - alert('设置已保存!'); - - // 更新刷新间隔 - refreshInterval = settings.refresh_interval; - startAutoRefresh(); - - closeSettingsModal(); - refreshAll(); - } - } catch (e) { - alert('保存失败: ' + e.message); - } } -function browseImagesDir() { - // 提示用户手动输入路径 - alert('请手动输入图片保存目录路径\n例如: D:\\vision-images\n每天的图片会自动保存到日期子文件夹'); -} - -// ============== 状态相关 ============== - -async function loadStatus() { - try { - const res = await fetch(`${API_BASE}/api/status`); - const data = await res.json(); - - updateStatusBar(data.scheduler); - updateStats(data.stats); - - // 更新按钮状态 - const startBtn = document.getElementById('start-btn'); - const stopBtn = document.getElementById('stop-btn'); - - if (data.scheduler.running) { - startBtn.disabled = true; - stopBtn.disabled = false; - document.getElementById('scheduler-status').innerHTML = - `状态: ✓ 运行中`; - } else { - startBtn.disabled = false; - stopBtn.disabled = true; - document.getElementById('scheduler-status').innerHTML = - `状态: ⏹ 已停止`; - } - - document.getElementById('capture-count').textContent = - `拍照次数: ${data.scheduler.capture_count}`; - - } catch (e) { - console.error('加载状态失败:', e); - } -} - -function updateStats(stats) { - document.getElementById('total-images').textContent = stats.total_images; - document.getElementById('analyzed-images').textContent = stats.analyzed_images; - document.getElementById('total-events').textContent = stats.total_events; +function updateUI(data) { + var startBtn = document.getElementById('start-btn'); + var stopBtn = document.getElementById('stop-btn'); - // 更新事件类型筛选 - const eventFilter = document.getElementById('event-filter'); - const currentValue = eventFilter.value; + if (data.scheduler.running) { + startBtn.disabled = true; + stopBtn.disabled = false; + document.getElementById('scheduler-status').innerHTML = 'Status: Running'; + } else { + startBtn.disabled = false; + stopBtn.disabled = true; + document.getElementById('scheduler-status').innerHTML = 'Status: Stopped'; + } - eventFilter.innerHTML = ''; - stats.event_types.forEach(item => { - const opt = document.createElement('option'); + document.getElementById('capture-count').textContent = 'Capture count: ' + data.scheduler.capture_count; + document.getElementById('total-images').textContent = data.stats.total_images; + document.getElementById('analyzed-images').textContent = data.stats.analyzed_images; + document.getElementById('total-events').textContent = data.stats.total_events; + + // Event type filter + var eventFilter = document.getElementById('event-filter'); + eventFilter.innerHTML = ''; + data.stats.event_types.forEach(function(item) { + var opt = document.createElement('option'); opt.value = item.type; - opt.textContent = `${item.type} (${item.count})`; + opt.textContent = item.type + ' (' + item.count + ')'; eventFilter.appendChild(opt); }); +} + +// Control +function startScheduler() { + fetch(API_BASE + '/api/scheduler/start', {method: 'POST'}) + .then(function(res) { return res.json(); }) + .then(function(data) { + if (data.success) { + alert('Started!'); + refreshAll(); + } else { + alert('Failed: ' + data.error); + } + }) + .catch(function(e) { alert('Error: ' + e.message); }); +} + +function stopScheduler() { + fetch(API_BASE + '/api/scheduler/stop', {method: 'POST'}) + .then(function(res) { return res.json(); }) + .then(function(data) { + if (data.success) { + alert('Stopped!'); + refreshAll(); + } + }) + .catch(function(e) { alert('Error: ' + e.message); }); +} + +function captureNow() { + fetch(API_BASE + '/api/capture', {method: 'POST'}) + .then(function(res) { return res.json(); }) + .then(function(data) { + if (data.success) { + alert('Capture success! Image ID: ' + data.image_id); + refreshAll(); + } else { + alert('Capture failed: ' + data.error); + } + }) + .catch(function(e) { alert('Error: ' + e.message); }); +} + +function analyzeUnanalyzed() { + fetch(API_BASE + '/api/analyze/unanalyzed', {method: 'POST'}) + .then(function(res) { return res.json(); }) + .then(function(data) { + alert('Analyzed ' + data.results.length + ' images'); + refreshAll(); + }) + .catch(function(e) { alert('Error: ' + e.message); }); +} + +// Images +function loadImages() { + var filter = document.getElementById('image-filter').value; + var analyzedOnly = filter === 'analyzed'; - eventFilter.value = currentValue; + fetch(API_BASE + '/api/images?limit=50&analyzed_only=' + analyzedOnly) + .then(function(res) { return res.json(); }) + .then(function(data) { + renderImages(data.images); + }) + .catch(function(e) { console.error('Load images failed:', e); }); } -// ============== 控制相关 ============== - -async function startScheduler() { - try { - const res = await fetch(`${API_BASE}/api/scheduler/start`, { - method: 'POST' - }); - const data = await res.json(); - - if (data.success) { - alert('启动成功!'); - refreshAll(); - } else { - alert('启动失败: ' + data.error); - } - } catch (e) { - alert('请求失败: ' + e.message); - } -} - -async function stopScheduler() { - try { - const res = await fetch(`${API_BASE}/api/scheduler/stop`, { - method: 'POST' - }); - const data = await res.json(); - - if (data.success) { - alert('已停止!'); - refreshAll(); - } - } catch (e) { - alert('请求失败: ' + e.message); - } -} - -async function captureNow() { - try { - const res = await fetch(`${API_BASE}/api/capture`, { - method: 'POST' - }); - const data = await res.json(); - - if (data.success) { - alert(`拍照成功!\n图片ID: #${data.image_id}\n保存到: ${data.date_folder}`); - refreshAll(); - } else { - alert('拍照失败: ' + data.error); - } - } catch (e) { - alert('请求失败: ' + e.message); - } -} - -async function analyzeUnanalyzed() { - try { - const res = await fetch(`${API_BASE}/api/analyze/unanalyzed`, { - method: 'POST' - }); - const data = await res.json(); - - alert(`分析了 ${data.results.length} 张图片`); - refreshAll(); - } catch (e) { - alert('请求失败: ' + e.message); - } -} - -// ============== 日期文件夹相关 ============== - -async function loadDateFolders() { - try { - const res = await fetch(`${API_BASE}/api/date-folders`); - const data = await res.json(); - - const dateFilter = document.getElementById('date-filter'); - const currentValue = dateFilter.value; - - dateFilter.innerHTML = ''; - data.folders.forEach(item => { - const opt = document.createElement('option'); - opt.value = item.date; - opt.textContent = `${item.date} (${item.count}张)`; - dateFilter.appendChild(opt); - }); - - dateFilter.value = currentValue; - } catch (e) { - console.error('加载日期文件夹失败:', e); - } -} - -// ============== 图片相关 ============== - -async function loadImages() { - const dateFilter = document.getElementById('date-filter').value; - const imageFilter = document.getElementById('image-filter').value; - const analyzedOnly = imageFilter === 'analyzed'; - - try { - let url = `${API_BASE}/api/images?limit=100&analyzed_only=${analyzedOnly}`; - if (dateFilter !== 'all') { - url += `&date_folder=${dateFilter}`; - } - - const res = await fetch(url); - const data = await res.json(); - - renderImagesList(data.images); - } catch (e) { - console.error('加载图片失败:', e); - } -} - -function renderImagesList(images) { - const list = document.getElementById('images-list'); +function renderImages(images) { + var list = document.getElementById('images-list'); list.innerHTML = ''; if (images.length === 0) { - list.innerHTML = '

暂无图片记录

'; + list.innerHTML = '

No images

'; return; } - images.forEach((img, index) => { - const item = document.createElement('div'); - item.className = `image-item ${img.analyzed ? 'analyzed' : 'unanalyzed'}`; - item.onclick = () => openImageModal(img.id); + images.forEach(function(img) { + var item = document.createElement('div'); + item.className = 'image-item ' + (img.analyzed ? 'analyzed' : 'unanalyzed'); + item.onclick = function() { openImageModal(img.id); }; - const time = new Date(img.timestamp).toLocaleString('zh-CN'); + var time = new Date(img.timestamp).toLocaleString(); + var status = img.analyzed ? 'Analyzed' : 'Unanalyzed'; + var events = img.events_summary || 'No events'; - item.innerHTML = ` - #${img.id} - ${time} - - ${img.analyzed ? '✓ 已分析' : '○ 未分析'} - - ${img.events_summary || '无事件'} - `; + item.innerHTML = '#' + img.id + '' + + '' + time + '' + + '' + status + '' + + '' + events + ''; list.appendChild(item); }); } -async function openImageModal(imageId) { +function openImageModal(imageId) { currentImageId = imageId; - try { - const res = await fetch(`${API_BASE}/api/images/${imageId}`); - const data = await res.json(); - - const modal = document.getElementById('image-modal'); - const modalImg = document.getElementById('modal-image'); - const modalTitle = document.getElementById('modal-title'); - const modalTime = document.getElementById('modal-time'); - const modalFolder = document.getElementById('modal-folder'); - const modalEvents = document.getElementById('modal-events'); - - // 获取图片文件名 - const filename = data.path.split('/').pop().split('\\').pop(); - modalImg.src = `/images/${filename}`; - modalTitle.textContent = `图片 #${data.id}`; - modalTime.textContent = `时间: ${new Date(data.timestamp).toLocaleString('zh-CN')}`; - modalFolder.textContent = `日期文件夹: ${data.date_folder || '未知'}`; - - modalEvents.innerHTML = ''; - if (data.events && data.events.length > 0) { - data.events.forEach(event => { - const eventDiv = document.createElement('div'); - eventDiv.className = 'modal-event'; - eventDiv.innerHTML = ` - ${event.event_type} - ${event.confidence} -
${event.description}
- `; - modalEvents.appendChild(eventDiv); - }); - } else { - modalEvents.innerHTML = '

暂无事件记录

'; - } - - modal.classList.add('active'); - } catch (e) { - console.error('加载图片详情失败:', e); - } + fetch(API_BASE + '/api/images/' + imageId) + .then(function(res) { return res.json(); }) + .then(function(data) { + var filename = data.path.split('/').pop().split('\\').pop(); + document.getElementById('modal-image').src = '/images/' + filename; + document.getElementById('modal-title').textContent = 'Image #' + data.id; + document.getElementById('modal-time').textContent = 'Time: ' + new Date(data.timestamp).toLocaleString(); + + var eventsDiv = document.getElementById('modal-events'); + eventsDiv.innerHTML = ''; + + if (data.events && data.events.length > 0) { + data.events.forEach(function(event) { + var div = document.createElement('div'); + div.className = 'modal-event'; + div.innerHTML = '' + event.event_type + ' ' + + '' + event.confidence + '' + + '
' + event.description + '
'; + eventsDiv.appendChild(div); + }); + } else { + eventsDiv.innerHTML = '

No events

'; + } + + document.getElementById('image-modal').classList.add('active'); + }) + .catch(function(e) { console.error('Load image failed:', e); }); } function closeImageModal() { @@ -360,98 +193,82 @@ function closeImageModal() { currentImageId = null; } -async function analyzeCurrentImage() { +function analyzeCurrentImage() { if (!currentImageId) return; - try { - const res = await fetch(`${API_BASE}/api/analyze/${currentImageId}`, { - method: 'POST' - }); - const data = await res.json(); - - if (data.success) { - alert('分析完成!'); - openImageModal(currentImageId); - refreshAll(); - } else { - alert('分析失败: ' + data.error); - } - } catch (e) { - alert('请求失败: ' + e.message); - } + fetch(API_BASE + '/api/analyze/' + currentImageId, {method: 'POST'}) + .then(function(res) { return res.json(); }) + .then(function(data) { + if (data.success) { + alert('Analyzed!'); + openImageModal(currentImageId); + refreshAll(); + } else { + alert('Failed: ' + data.error); + } + }) + .catch(function(e) { alert('Error: ' + e.message); }); } -async function deleteCurrentImage() { +function deleteCurrentImage() { if (!currentImageId) return; + if (!confirm('Delete this image?')) return; - if (!confirm('确定删除此图片?')) return; - - try { - const res = await fetch(`${API_BASE}/api/images/${currentImageId}`, { - method: 'DELETE' - }); - const data = await res.json(); - - if (data.success) { - closeImageModal(); - refreshAll(); - } - } catch (e) { - alert('请求失败: ' + e.message); - } + fetch(API_BASE + '/api/images/' + currentImageId, {method: 'DELETE'}) + .then(function(res) { return res.json(); }) + .then(function(data) { + if (data.success) { + closeImageModal(); + refreshAll(); + } + }) + .catch(function(e) { alert('Error: ' + e.message); }); } -// ============== 事件相关 ============== - -async function loadEvents() { - const filter = document.getElementById('event-filter').value; - const eventType = filter === 'all' ? '' : filter; +// Events +function loadEvents() { + var filter = document.getElementById('event-filter').value; + var eventType = filter === 'all' ? '' : filter; - try { - let url = `${API_BASE}/api/events?limit=100`; - if (eventType) { - url += `&event_type=${encodeURIComponent(eventType)}`; - } - - const res = await fetch(url); - const data = await res.json(); - - renderEvents(data.events); - } catch (e) { - console.error('加载事件失败:', e); + var url = API_BASE + '/api/events?limit=50'; + if (eventType) { + url += '&event_type=' + encodeURIComponent(eventType); } + + fetch(url) + .then(function(res) { return res.json(); }) + .then(function(data) { + renderEvents(data.events); + }) + .catch(function(e) { console.error('Load events failed:', e); }); } function renderEvents(events) { - const list = document.getElementById('events-list'); + var list = document.getElementById('events-list'); list.innerHTML = ''; if (events.length === 0) { - list.innerHTML = '

暂无事件记录

'; + list.innerHTML = '

No events

'; return; } - events.forEach(event => { - const card = document.createElement('div'); - card.className = `event-card type-${event.event_type}`; + events.forEach(function(event) { + var card = document.createElement('div'); + card.className = 'event-card type-' + event.event_type; + card.onclick = function() { openImageModal(event.image_id); }; - const time = new Date(event.timestamp).toLocaleString('zh-CN'); + var time = new Date(event.timestamp).toLocaleString(); - card.innerHTML = ` -
- ${event.event_type} - ${time} -
-
${event.description}
-
${event.confidence}
- `; + card.innerHTML = '
' + + '' + event.event_type + '' + + '' + time + '
' + + '
' + event.description + '
' + + '
' + event.confidence + '
'; - card.onclick = () => openImageModal(event.image_id); list.appendChild(card); }); } -// 筛选变化监听 -document.getElementById('date-filter').addEventListener('change', loadImages); +// Filter listeners document.getElementById('image-filter').addEventListener('change', loadImages); document.getElementById('event-filter').addEventListener('change', loadEvents); \ No newline at end of file