531 lines
22 KiB
JavaScript
531 lines
22 KiB
JavaScript
// 页面加载完成后执行
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// 初始化图片预览功能
|
|
initImagePreview('uploadFile', 'uploadPreviewContainer');
|
|
initImagePreview('searchFile', 'searchPreview');
|
|
|
|
// 初始化表单提交事件
|
|
initUploadForm();
|
|
initSearchForm();
|
|
initUpdateForm();
|
|
initDeleteForm();
|
|
initBatchDeleteButtons();
|
|
});
|
|
|
|
// 初始化图片预览功能
|
|
function initImagePreview(inputId, containerId) {
|
|
const input = document.getElementById(inputId);
|
|
const container = document.getElementById(containerId || 'uploadPreviewContainer');
|
|
|
|
if (input && container) {
|
|
input.addEventListener('change', function() {
|
|
// 清空预览容器
|
|
container.innerHTML = '';
|
|
|
|
if (this.files && this.files.length > 0) {
|
|
// 遍历所有选择的文件
|
|
Array.from(this.files).forEach((file, index) => {
|
|
const reader = new FileReader();
|
|
const previewDiv = document.createElement('div');
|
|
previewDiv.className = 'preview-item';
|
|
|
|
const previewImg = document.createElement('img');
|
|
previewImg.className = 'img-preview';
|
|
previewImg.alt = `预览图 ${index + 1}`;
|
|
|
|
previewDiv.appendChild(previewImg);
|
|
container.appendChild(previewDiv);
|
|
|
|
reader.onload = function(e) {
|
|
previewImg.src = e.target.result;
|
|
};
|
|
|
|
reader.readAsDataURL(file);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// 初始化上传表单
|
|
function initUploadForm() {
|
|
const form = document.getElementById('uploadForm');
|
|
|
|
if (form) {
|
|
form.addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
|
|
const files = document.getElementById('uploadFile').files;
|
|
if (!files || files.length === 0) {
|
|
alert('请选择至少一个文件');
|
|
return;
|
|
}
|
|
|
|
const resultDiv = document.getElementById('uploadResult');
|
|
resultDiv.innerHTML = '<div class="alert alert-info"><div class="spinner-border text-primary" role="status"></div>正在上传,请稍候...</div>';
|
|
|
|
let successCount = 0;
|
|
let errorCount = 0;
|
|
let results = [];
|
|
|
|
// 获取表单基本数据
|
|
const type = document.getElementById('imageType').value;
|
|
const description = document.getElementById('imageDescription').value;
|
|
const name = document.getElementById('imageName').value;
|
|
const tags = document.getElementById('imageTags').value || '1'; // 默认为1
|
|
|
|
// 依次上传每个文件
|
|
for (let i = 0; i < files.length; i++) {
|
|
const file = files[i];
|
|
const formData = new FormData();
|
|
|
|
// 添加当前文件
|
|
formData.append('file', file);
|
|
|
|
// 添加其他表单数据
|
|
formData.append('type', type);
|
|
formData.append('description', description);
|
|
formData.append('name', `${name}_${i+1}`);
|
|
formData.append('tags', tags);
|
|
|
|
try {
|
|
const response = await fetch('/imgsearcherApi/upload', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.error) {
|
|
errorCount++;
|
|
} else {
|
|
successCount++;
|
|
results.push(data);
|
|
}
|
|
} catch (error) {
|
|
errorCount++;
|
|
}
|
|
|
|
// 更新进度信息
|
|
resultDiv.innerHTML = `
|
|
<div class="alert alert-info">
|
|
<div class="progress mb-2">
|
|
<div class="progress-bar" role="progressbar" style="width: ${Math.round((i+1) / files.length * 100)}%" aria-valuenow="${i+1}" aria-valuemin="0" aria-valuemax="${files.length}">${Math.round((i+1) / files.length * 100)}%</div>
|
|
</div>
|
|
<p>正在上传: ${i+1}/${files.length}</p>
|
|
<p>成功: ${successCount}, 失败: ${errorCount}</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// 显示最终结果
|
|
if (successCount > 0) {
|
|
let resultHTML = `
|
|
<div class="alert alert-success">
|
|
<h5>上传完成!</h5>
|
|
<p>成功上传: ${successCount}张图片</p>
|
|
<p>上传失败: ${errorCount}张图片</p>
|
|
</div>
|
|
`;
|
|
|
|
if (results.length > 0) {
|
|
resultHTML += '<div class="mt-3"><h6>上传成功的图片:</h6><ul>';
|
|
results.forEach((result, index) => {
|
|
resultHTML += `<li>图片${index+1} - 签名: ${result.cont_sign}</li>`;
|
|
});
|
|
resultHTML += '</ul></div>';
|
|
}
|
|
|
|
resultDiv.innerHTML = resultHTML;
|
|
|
|
// 重置表单
|
|
form.reset();
|
|
document.getElementById('uploadPreviewContainer').innerHTML = '';
|
|
document.getElementById('imageTags').value = '1'; // 重置后设置默认值为1
|
|
} else {
|
|
resultDiv.innerHTML = `<div class="alert alert-danger">所有图片上传失败</div>`;
|
|
}
|
|
});
|
|
|
|
// 设置默认标签值
|
|
const tagsInput = document.getElementById('imageTags');
|
|
if (tagsInput && !tagsInput.value) {
|
|
tagsInput.value = '1';
|
|
}
|
|
}
|
|
}
|
|
|
|
// 初始化搜索表单
|
|
function initSearchForm() {
|
|
const form = document.getElementById('searchForm');
|
|
|
|
if (form) {
|
|
// 设置默认标签值
|
|
const tagsInput = document.getElementById('searchTags');
|
|
if (tagsInput && !tagsInput.value) {
|
|
tagsInput.value = '1';
|
|
}
|
|
|
|
form.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const formData = new FormData(this);
|
|
const resultDiv = document.getElementById('searchResult');
|
|
const resultList = document.getElementById('searchResultList');
|
|
const resultStats = document.getElementById('searchStats');
|
|
const resultCount = document.getElementById('resultCount');
|
|
const batchActions = document.querySelector('.batch-actions');
|
|
const reliableTypesInfo = document.getElementById('reliableTypesInfo');
|
|
|
|
resultList.innerHTML = '<div class="col-12 text-center"><div class="spinner-border text-primary" role="status"></div> 正在搜索,请稍候...</div>';
|
|
resultStats.classList.add('d-none');
|
|
batchActions.classList.add('d-none');
|
|
reliableTypesInfo.classList.add('d-none');
|
|
|
|
fetch('/imgsearcherApi/search', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.error) {
|
|
resultList.innerHTML = `<div class="col-12"><div class="alert alert-danger">${data.error}</div></div>`;
|
|
} else {
|
|
// 显示搜索结果统计
|
|
resultCount.textContent = data.result_num || 0;
|
|
resultStats.classList.remove('d-none');
|
|
|
|
// 显示最可信类型分析结果
|
|
if (data.most_reliable_types) {
|
|
reliableTypesInfo.classList.remove('d-none');
|
|
document.getElementById('method1Type').textContent = data.most_reliable_types.method1 || '-';
|
|
document.getElementById('method2Type').textContent = data.most_reliable_types.method2 || '-';
|
|
document.getElementById('method3Type').textContent = data.most_reliable_types.method3 || '-';
|
|
document.getElementById('method4Type').textContent = data.most_reliable_types.method4 || '-';
|
|
document.getElementById('method5Type').textContent = data.most_reliable_types.method5 || '-';
|
|
} else {
|
|
reliableTypesInfo.classList.add('d-none');
|
|
}
|
|
|
|
// 清空结果列表
|
|
resultList.innerHTML = '';
|
|
|
|
if (data.result && data.result.length > 0) {
|
|
// 显示搜索结果
|
|
data.result.forEach(item => {
|
|
// 解析brief信息
|
|
let briefInfo = {};
|
|
try {
|
|
briefInfo = JSON.parse(item.brief);
|
|
} catch (e) {
|
|
briefInfo = { type: '未知', description: '未知', name: '未知' };
|
|
}
|
|
|
|
const resultItem = document.createElement('div');
|
|
resultItem.className = 'col-md-4 col-sm-6 result-item';
|
|
resultItem.innerHTML = `
|
|
<div class="card result-card">
|
|
<div class="result-img-container">
|
|
<div class="select-checkbox">
|
|
<input type="checkbox" class="item-checkbox" data-cont-sign="${item.cont_sign}">
|
|
</div>
|
|
<img class="result-img" src="/static/img/placeholder.png" alt="${briefInfo.name || '图片'}">
|
|
</div>
|
|
<div class="result-info">
|
|
<h5>${briefInfo.name || '未命名图片'}</h5>
|
|
<p>类型: ${briefInfo.type || '未指定'}</p>
|
|
<p>描述: ${briefInfo.description ? (briefInfo.description.length > 30 ? briefInfo.description.substring(0, 30) + '...' : briefInfo.description) : '无描述'}</p>
|
|
<p>相似度: <span class="result-score">${(item.score * 100).toFixed(2)}%</span></p>
|
|
<div class="result-actions">
|
|
<button class="btn btn-sm btn-outline-primary update-btn"
|
|
data-cont-sign="${item.cont_sign}"
|
|
data-type="${briefInfo.type || ''}"
|
|
data-description="${briefInfo.description || ''}"
|
|
data-name="${briefInfo.name || ''}"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#updateModal">
|
|
编辑
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger delete-btn"
|
|
data-cont-sign="${item.cont_sign}"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#deleteModal">
|
|
删除
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
resultList.appendChild(resultItem);
|
|
});
|
|
|
|
// 初始化编辑和删除按钮事件
|
|
initResultButtons();
|
|
// 初始化批量删除功能
|
|
initBatchDelete();
|
|
} else {
|
|
resultList.innerHTML = '<div class="col-12"><div class="alert alert-warning">没有找到匹配的图片</div></div>';
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
resultList.innerHTML = `<div class="col-12"><div class="alert alert-danger">搜索失败: ${error.message}</div></div>`;
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// 初始化搜索结果中的按钮事件
|
|
function initResultButtons() {
|
|
// 更新按钮点击事件
|
|
document.querySelectorAll('.update-btn').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const contSign = this.getAttribute('data-cont-sign');
|
|
const type = this.getAttribute('data-type');
|
|
const description = this.getAttribute('data-description');
|
|
const name = this.getAttribute('data-name');
|
|
|
|
document.getElementById('updateContSign').value = contSign;
|
|
document.getElementById('updateType').value = type;
|
|
document.getElementById('updateDescription').value = description;
|
|
document.getElementById('updateName').value = name;
|
|
});
|
|
});
|
|
|
|
// 删除按钮点击事件
|
|
document.querySelectorAll('.delete-btn').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const contSign = this.getAttribute('data-cont-sign');
|
|
document.getElementById('deleteContSign').value = contSign;
|
|
});
|
|
});
|
|
}
|
|
|
|
// 初始化更新表单
|
|
function initUpdateForm() {
|
|
const updateButton = document.getElementById('updateSubmit');
|
|
|
|
if (updateButton) {
|
|
updateButton.addEventListener('click', function() {
|
|
const contSign = document.getElementById('updateContSign').value;
|
|
const type = document.getElementById('updateType').value;
|
|
const description = document.getElementById('updateDescription').value;
|
|
const name = document.getElementById('updateName').value;
|
|
const tags = document.getElementById('updateTags').value || '1';
|
|
|
|
// 创建brief信息
|
|
const brief = {
|
|
type: type,
|
|
description: description,
|
|
name: name
|
|
};
|
|
|
|
// 发送更新请求
|
|
fetch('/imgsearcherApi/update', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
cont_sign: contSign,
|
|
brief: brief,
|
|
tags: tags
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// 关闭模态框
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('updateModal'));
|
|
modal.hide();
|
|
|
|
if (data.error) {
|
|
alert(`更新失败: ${data.error}`);
|
|
} else {
|
|
alert('更新成功!');
|
|
// 如果当前在搜索标签页,刷新搜索结果
|
|
if (document.getElementById('search-tab').classList.contains('active')) {
|
|
document.getElementById('searchForm').dispatchEvent(new Event('submit'));
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert(`更新失败: ${error.message}`);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// 初始化删除表单
|
|
function initDeleteForm() {
|
|
const deleteButton = document.getElementById('deleteSubmit');
|
|
|
|
if (deleteButton) {
|
|
deleteButton.addEventListener('click', function() {
|
|
const contSign = document.getElementById('deleteContSign').value;
|
|
|
|
// 发送删除请求
|
|
fetch('/imgsearcherApi/delete', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
cont_sign: contSign
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// 关闭模态框
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('deleteModal'));
|
|
modal.hide();
|
|
|
|
if (data.error) {
|
|
alert(`删除失败: ${data.error}`);
|
|
} else {
|
|
alert('删除成功!');
|
|
// 如果当前在搜索标签页,刷新搜索结果
|
|
if (document.getElementById('search-tab').classList.contains('active')) {
|
|
document.getElementById('searchForm').dispatchEvent(new Event('submit'));
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert(`删除失败: ${error.message}`);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// 初始化批量删除按钮
|
|
function initBatchDeleteButtons() {
|
|
const batchDeleteBtn = document.getElementById('batchDeleteBtn');
|
|
const cancelSelectionBtn = document.getElementById('cancelSelectionBtn');
|
|
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
|
|
|
|
if (batchDeleteBtn) {
|
|
batchDeleteBtn.addEventListener('click', function() {
|
|
const selectedItems = document.querySelectorAll('.item-checkbox:checked');
|
|
|
|
if (selectedItems.length === 0) {
|
|
alert('请选择要删除的图片');
|
|
return;
|
|
}
|
|
|
|
if (confirm(`确定要删除选中的 ${selectedItems.length} 张图片吗?此操作不可撤销。`)) {
|
|
batchDelete(selectedItems);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (cancelSelectionBtn) {
|
|
cancelSelectionBtn.addEventListener('click', function() {
|
|
document.querySelectorAll('.item-checkbox').forEach(checkbox => {
|
|
checkbox.checked = false;
|
|
});
|
|
if (selectAllCheckbox) {
|
|
selectAllCheckbox.checked = false;
|
|
}
|
|
document.querySelector('.batch-actions').classList.add('d-none');
|
|
});
|
|
}
|
|
|
|
// 全选功能
|
|
if (selectAllCheckbox) {
|
|
selectAllCheckbox.addEventListener('change', function() {
|
|
const checkboxes = document.querySelectorAll('.item-checkbox');
|
|
const batchActions = document.querySelector('.batch-actions');
|
|
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.checked = this.checked;
|
|
});
|
|
|
|
if (this.checked && checkboxes.length > 0) {
|
|
batchActions.classList.remove('d-none');
|
|
} else {
|
|
batchActions.classList.add('d-none');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// 初始化批量删除功能
|
|
function initBatchDelete() {
|
|
const checkboxes = document.querySelectorAll('.item-checkbox');
|
|
const batchActions = document.querySelector('.batch-actions');
|
|
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
|
|
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.addEventListener('change', function() {
|
|
const checkedBoxes = document.querySelectorAll('.item-checkbox:checked');
|
|
const anyChecked = checkedBoxes.length > 0;
|
|
const allChecked = checkedBoxes.length === checkboxes.length;
|
|
|
|
// 更新全选复选框状态
|
|
if (selectAllCheckbox) {
|
|
selectAllCheckbox.checked = allChecked && checkboxes.length > 0;
|
|
}
|
|
|
|
if (anyChecked) {
|
|
batchActions.classList.remove('d-none');
|
|
} else {
|
|
batchActions.classList.add('d-none');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 批量删除选中的图片
|
|
async function batchDelete(selectedItems) {
|
|
const resultList = document.getElementById('searchResultList');
|
|
const loadingDiv = document.createElement('div');
|
|
loadingDiv.className = 'col-12 text-center batch-delete-progress';
|
|
loadingDiv.innerHTML = '<div class="alert alert-info"><div class="spinner-border text-primary" role="status"></div> 正在删除选中图片,请稍候...</div>';
|
|
|
|
resultList.appendChild(loadingDiv);
|
|
|
|
let successCount = 0;
|
|
let errorCount = 0;
|
|
|
|
for (let i = 0; i < selectedItems.length; i++) {
|
|
const contSign = selectedItems[i].getAttribute('data-cont-sign');
|
|
|
|
try {
|
|
const response = await fetch('/imgsearcherApi/delete', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
cont_sign: contSign
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.error) {
|
|
errorCount++;
|
|
} else {
|
|
successCount++;
|
|
}
|
|
|
|
// 更新进度
|
|
loadingDiv.innerHTML = `
|
|
<div class="alert alert-info">
|
|
<div class="progress mb-2">
|
|
<div class="progress-bar" role="progressbar" style="width: ${Math.round((i+1) / selectedItems.length * 100)}%" aria-valuenow="${i+1}" aria-valuemin="0" aria-valuemax="${selectedItems.length}">${Math.round((i+1) / selectedItems.length * 100)}%</div>
|
|
</div>
|
|
<p>正在删除: ${i+1}/${selectedItems.length}</p>
|
|
<p>成功: ${successCount}, 失败: ${errorCount}</p>
|
|
</div>
|
|
`;
|
|
|
|
} catch (error) {
|
|
errorCount++;
|
|
}
|
|
}
|
|
|
|
// 删除完成后刷新搜索结果
|
|
alert(`批量删除完成\n成功: ${successCount} 张\n失败: ${errorCount} 张`);
|
|
document.getElementById('searchForm').dispatchEvent(new Event('submit'));
|
|
}
|