fix: 重写 app.js 使用传统语法避免 Windows 编码问题
This commit is contained in:
+184
-367
@@ -1,14 +1,11 @@
|
|||||||
// 视觉记录系统前端逻辑
|
// Vision Record System - Frontend Logic
|
||||||
|
|
||||||
const API_BASE = '';
|
var API_BASE = '';
|
||||||
let currentImageId = null;
|
var currentImageId = null;
|
||||||
let refreshTimer = null;
|
var refreshTimer = null;
|
||||||
let refreshInterval = 5;
|
|
||||||
|
|
||||||
// ============== 初始化 ==============
|
// Initialize
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
loadConfig();
|
|
||||||
refreshAll();
|
refreshAll();
|
||||||
startAutoRefresh();
|
startAutoRefresh();
|
||||||
});
|
});
|
||||||
@@ -17,342 +14,178 @@ function startAutoRefresh() {
|
|||||||
if (refreshTimer) {
|
if (refreshTimer) {
|
||||||
clearInterval(refreshTimer);
|
clearInterval(refreshTimer);
|
||||||
}
|
}
|
||||||
refreshTimer = setInterval(refreshAll, refreshInterval * 1000);
|
refreshTimer = setInterval(refreshAll, 5000);
|
||||||
document.getElementById('refresh-info').textContent = `刷新间隔: ${refreshInterval}秒`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshAll() {
|
function refreshAll() {
|
||||||
loadStatus();
|
loadStatus();
|
||||||
loadDateFolders();
|
|
||||||
loadImages();
|
loadImages();
|
||||||
loadEvents();
|
loadEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============== 配置相关 ==============
|
// Status
|
||||||
|
function loadStatus() {
|
||||||
async function loadConfig() {
|
fetch(API_BASE + '/api/status')
|
||||||
try {
|
.then(function(res) { return res.json(); })
|
||||||
const res = await fetch(`${API_BASE}/api/config`);
|
.then(function(data) {
|
||||||
const config = await res.json();
|
updateUI(data);
|
||||||
|
})
|
||||||
refreshInterval = config.refresh_interval || 5;
|
.catch(function(e) {
|
||||||
startAutoRefresh();
|
console.error('Load status failed:', e);
|
||||||
|
|
||||||
// 更新显示数量提示
|
|
||||||
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)
|
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
}
|
||||||
|
|
||||||
if (data.success) {
|
function updateUI(data) {
|
||||||
alert('设置已保存!');
|
var startBtn = document.getElementById('start-btn');
|
||||||
|
var stopBtn = document.getElementById('stop-btn');
|
||||||
|
|
||||||
// 更新刷新间隔
|
if (data.scheduler.running) {
|
||||||
refreshInterval = settings.refresh_interval;
|
startBtn.disabled = true;
|
||||||
startAutoRefresh();
|
stopBtn.disabled = false;
|
||||||
|
document.getElementById('scheduler-status').innerHTML = 'Status: Running';
|
||||||
closeSettingsModal();
|
} else {
|
||||||
refreshAll();
|
startBtn.disabled = false;
|
||||||
}
|
stopBtn.disabled = true;
|
||||||
} catch (e) {
|
document.getElementById('scheduler-status').innerHTML = 'Status: Stopped';
|
||||||
alert('保存失败: ' + e.message);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function browseImagesDir() {
|
document.getElementById('capture-count').textContent = 'Capture count: ' + data.scheduler.capture_count;
|
||||||
// 提示用户手动输入路径
|
document.getElementById('total-images').textContent = data.stats.total_images;
|
||||||
alert('请手动输入图片保存目录路径\n例如: D:\\vision-images\n每天的图片会自动保存到日期子文件夹');
|
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');
|
||||||
async function loadStatus() {
|
eventFilter.innerHTML = '<option value="all">All types</option>';
|
||||||
try {
|
data.stats.event_types.forEach(function(item) {
|
||||||
const res = await fetch(`${API_BASE}/api/status`);
|
var opt = document.createElement('option');
|
||||||
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 =
|
|
||||||
`状态: <span style="color: #4CAF50;">✓ 运行中</span>`;
|
|
||||||
} else {
|
|
||||||
startBtn.disabled = false;
|
|
||||||
stopBtn.disabled = true;
|
|
||||||
document.getElementById('scheduler-status').innerHTML =
|
|
||||||
`状态: <span style="color: #f44336;">⏹ 已停止</span>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 更新事件类型筛选
|
|
||||||
const eventFilter = document.getElementById('event-filter');
|
|
||||||
const currentValue = eventFilter.value;
|
|
||||||
|
|
||||||
eventFilter.innerHTML = '<option value="all">全部类型</option>';
|
|
||||||
stats.event_types.forEach(item => {
|
|
||||||
const opt = document.createElement('option');
|
|
||||||
opt.value = item.type;
|
opt.value = item.type;
|
||||||
opt.textContent = `${item.type} (${item.count})`;
|
opt.textContent = item.type + ' (' + item.count + ')';
|
||||||
eventFilter.appendChild(opt);
|
eventFilter.appendChild(opt);
|
||||||
});
|
});
|
||||||
|
|
||||||
eventFilter.value = currentValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============== 控制相关 ==============
|
// 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); });
|
||||||
|
}
|
||||||
|
|
||||||
async function startScheduler() {
|
function stopScheduler() {
|
||||||
try {
|
fetch(API_BASE + '/api/scheduler/stop', {method: 'POST'})
|
||||||
const res = await fetch(`${API_BASE}/api/scheduler/start`, {
|
.then(function(res) { return res.json(); })
|
||||||
method: 'POST'
|
.then(function(data) {
|
||||||
});
|
if (data.success) {
|
||||||
const data = await res.json();
|
alert('Stopped!');
|
||||||
|
refreshAll();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(e) { alert('Error: ' + e.message); });
|
||||||
|
}
|
||||||
|
|
||||||
if (data.success) {
|
function captureNow() {
|
||||||
alert('启动成功!');
|
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();
|
refreshAll();
|
||||||
} else {
|
})
|
||||||
alert('启动失败: ' + data.error);
|
.catch(function(e) { alert('Error: ' + e.message); });
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
alert('请求失败: ' + e.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stopScheduler() {
|
// Images
|
||||||
try {
|
function loadImages() {
|
||||||
const res = await fetch(`${API_BASE}/api/scheduler/stop`, {
|
var filter = document.getElementById('image-filter').value;
|
||||||
method: 'POST'
|
var analyzedOnly = filter === 'analyzed';
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
if (data.success) {
|
fetch(API_BASE + '/api/images?limit=50&analyzed_only=' + analyzedOnly)
|
||||||
alert('已停止!');
|
.then(function(res) { return res.json(); })
|
||||||
refreshAll();
|
.then(function(data) {
|
||||||
}
|
renderImages(data.images);
|
||||||
} catch (e) {
|
})
|
||||||
alert('请求失败: ' + e.message);
|
.catch(function(e) { console.error('Load images failed:', e); });
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function captureNow() {
|
function renderImages(images) {
|
||||||
try {
|
var list = document.getElementById('images-list');
|
||||||
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 = '<option value="all">全部日期</option>';
|
|
||||||
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');
|
|
||||||
list.innerHTML = '';
|
list.innerHTML = '';
|
||||||
|
|
||||||
if (images.length === 0) {
|
if (images.length === 0) {
|
||||||
list.innerHTML = '<p style="color: #888; text-align: center;">暂无图片记录</p>';
|
list.innerHTML = '<p style="color:#888;text-align:center;">No images</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
images.forEach((img, index) => {
|
images.forEach(function(img) {
|
||||||
const item = document.createElement('div');
|
var item = document.createElement('div');
|
||||||
item.className = `image-item ${img.analyzed ? 'analyzed' : 'unanalyzed'}`;
|
item.className = 'image-item ' + (img.analyzed ? 'analyzed' : 'unanalyzed');
|
||||||
item.onclick = () => openImageModal(img.id);
|
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 = `
|
item.innerHTML = '<span class="image-number">#' + img.id + '</span>' +
|
||||||
<span class="image-number">#${img.id}</span>
|
'<span class="image-time">' + time + '</span>' +
|
||||||
<span class="image-time">${time}</span>
|
'<span class="image-status">' + status + '</span>' +
|
||||||
<span class="image-status ${img.analyzed ? 'analyzed' : 'unanalyzed'}">
|
'<span class="image-events-summary">' + events + '</span>';
|
||||||
${img.analyzed ? '✓ 已分析' : '○ 未分析'}
|
|
||||||
</span>
|
|
||||||
<span class="image-events-summary">${img.events_summary || '无事件'}</span>
|
|
||||||
`;
|
|
||||||
|
|
||||||
list.appendChild(item);
|
list.appendChild(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openImageModal(imageId) {
|
function openImageModal(imageId) {
|
||||||
currentImageId = imageId;
|
currentImageId = imageId;
|
||||||
|
|
||||||
try {
|
fetch(API_BASE + '/api/images/' + imageId)
|
||||||
const res = await fetch(`${API_BASE}/api/images/${imageId}`);
|
.then(function(res) { return res.json(); })
|
||||||
const data = await 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();
|
||||||
|
|
||||||
const modal = document.getElementById('image-modal');
|
var eventsDiv = document.getElementById('modal-events');
|
||||||
const modalImg = document.getElementById('modal-image');
|
eventsDiv.innerHTML = '';
|
||||||
const modalTitle = document.getElementById('modal-title');
|
|
||||||
const modalTime = document.getElementById('modal-time');
|
|
||||||
const modalFolder = document.getElementById('modal-folder');
|
|
||||||
const modalEvents = document.getElementById('modal-events');
|
|
||||||
|
|
||||||
// 获取图片文件名
|
if (data.events && data.events.length > 0) {
|
||||||
const filename = data.path.split('/').pop().split('\\').pop();
|
data.events.forEach(function(event) {
|
||||||
modalImg.src = `/images/${filename}`;
|
var div = document.createElement('div');
|
||||||
modalTitle.textContent = `图片 #${data.id}`;
|
div.className = 'modal-event';
|
||||||
modalTime.textContent = `时间: ${new Date(data.timestamp).toLocaleString('zh-CN')}`;
|
div.innerHTML = '<strong>' + event.event_type + '</strong> ' +
|
||||||
modalFolder.textContent = `日期文件夹: ${data.date_folder || '未知'}`;
|
'<span class="event-confidence">' + event.confidence + '</span>' +
|
||||||
|
'<div>' + event.description + '</div>';
|
||||||
|
eventsDiv.appendChild(div);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
eventsDiv.innerHTML = '<p style="color:#888;">No events</p>';
|
||||||
|
}
|
||||||
|
|
||||||
modalEvents.innerHTML = '';
|
document.getElementById('image-modal').classList.add('active');
|
||||||
if (data.events && data.events.length > 0) {
|
})
|
||||||
data.events.forEach(event => {
|
.catch(function(e) { console.error('Load image failed:', e); });
|
||||||
const eventDiv = document.createElement('div');
|
|
||||||
eventDiv.className = 'modal-event';
|
|
||||||
eventDiv.innerHTML = `
|
|
||||||
<strong>${event.event_type}</strong>
|
|
||||||
<span class="event-confidence">${event.confidence}</span>
|
|
||||||
<div>${event.description}</div>
|
|
||||||
`;
|
|
||||||
modalEvents.appendChild(eventDiv);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
modalEvents.innerHTML = '<p style="color: #888;">暂无事件记录</p>';
|
|
||||||
}
|
|
||||||
|
|
||||||
modal.classList.add('active');
|
|
||||||
} catch (e) {
|
|
||||||
console.error('加载图片详情失败:', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeImageModal() {
|
function closeImageModal() {
|
||||||
@@ -360,98 +193,82 @@ function closeImageModal() {
|
|||||||
currentImageId = null;
|
currentImageId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function analyzeCurrentImage() {
|
function analyzeCurrentImage() {
|
||||||
if (!currentImageId) return;
|
if (!currentImageId) return;
|
||||||
|
|
||||||
try {
|
fetch(API_BASE + '/api/analyze/' + currentImageId, {method: 'POST'})
|
||||||
const res = await fetch(`${API_BASE}/api/analyze/${currentImageId}`, {
|
.then(function(res) { return res.json(); })
|
||||||
method: 'POST'
|
.then(function(data) {
|
||||||
});
|
if (data.success) {
|
||||||
const data = await res.json();
|
alert('Analyzed!');
|
||||||
|
openImageModal(currentImageId);
|
||||||
if (data.success) {
|
refreshAll();
|
||||||
alert('分析完成!');
|
} else {
|
||||||
openImageModal(currentImageId);
|
alert('Failed: ' + data.error);
|
||||||
refreshAll();
|
}
|
||||||
} else {
|
})
|
||||||
alert('分析失败: ' + data.error);
|
.catch(function(e) { alert('Error: ' + e.message); });
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
alert('请求失败: ' + e.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteCurrentImage() {
|
function deleteCurrentImage() {
|
||||||
if (!currentImageId) return;
|
if (!currentImageId) return;
|
||||||
|
if (!confirm('Delete this image?')) return;
|
||||||
|
|
||||||
if (!confirm('确定删除此图片?')) return;
|
fetch(API_BASE + '/api/images/' + currentImageId, {method: 'DELETE'})
|
||||||
|
.then(function(res) { return res.json(); })
|
||||||
try {
|
.then(function(data) {
|
||||||
const res = await fetch(`${API_BASE}/api/images/${currentImageId}`, {
|
if (data.success) {
|
||||||
method: 'DELETE'
|
closeImageModal();
|
||||||
});
|
refreshAll();
|
||||||
const data = await res.json();
|
}
|
||||||
|
})
|
||||||
if (data.success) {
|
.catch(function(e) { alert('Error: ' + e.message); });
|
||||||
closeImageModal();
|
|
||||||
refreshAll();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
alert('请求失败: ' + e.message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============== 事件相关 ==============
|
// Events
|
||||||
|
function loadEvents() {
|
||||||
|
var filter = document.getElementById('event-filter').value;
|
||||||
|
var eventType = filter === 'all' ? '' : filter;
|
||||||
|
|
||||||
async function loadEvents() {
|
var url = API_BASE + '/api/events?limit=50';
|
||||||
const filter = document.getElementById('event-filter').value;
|
if (eventType) {
|
||||||
const eventType = filter === 'all' ? '' : filter;
|
url += '&event_type=' + encodeURIComponent(eventType);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
function renderEvents(events) {
|
||||||
const list = document.getElementById('events-list');
|
var list = document.getElementById('events-list');
|
||||||
list.innerHTML = '';
|
list.innerHTML = '';
|
||||||
|
|
||||||
if (events.length === 0) {
|
if (events.length === 0) {
|
||||||
list.innerHTML = '<p style="color: #888; text-align: center;">暂无事件记录</p>';
|
list.innerHTML = '<p style="color:#888;text-align:center;">No events</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
events.forEach(event => {
|
events.forEach(function(event) {
|
||||||
const card = document.createElement('div');
|
var card = document.createElement('div');
|
||||||
card.className = `event-card type-${event.event_type}`;
|
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 = `
|
card.innerHTML = '<div class="event-header">' +
|
||||||
<div class="event-header">
|
'<span class="event-type">' + event.event_type + '</span>' +
|
||||||
<span class="event-type">${event.event_type}</span>
|
'<span class="event-time">' + time + '</span></div>' +
|
||||||
<span class="event-time">${time}</span>
|
'<div class="event-desc">' + event.description + '</div>' +
|
||||||
</div>
|
'<div class="event-confidence">' + event.confidence + '</div>';
|
||||||
<div class="event-desc">${event.description}</div>
|
|
||||||
<div class="event-confidence">${event.confidence}</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
card.onclick = () => openImageModal(event.image_id);
|
|
||||||
list.appendChild(card);
|
list.appendChild(card);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 筛选变化监听
|
// Filter listeners
|
||||||
document.getElementById('date-filter').addEventListener('change', loadImages);
|
|
||||||
document.getElementById('image-filter').addEventListener('change', loadImages);
|
document.getElementById('image-filter').addEventListener('change', loadImages);
|
||||||
document.getElementById('event-filter').addEventListener('change', loadEvents);
|
document.getElementById('event-filter').addEventListener('change', loadEvents);
|
||||||
Reference in New Issue
Block a user