-

图片上传与识别

-

选择图片后上传,服务端调用大模型解析为可编辑的 JSON,再确认入库。

- -
- {% csrf_token %} - - - -
- -
-
-

图片预览

- 预览 -
-
-

识别结果(可编辑)

-
- - -
-
-
- -
-
+
+
+

图片上传与识别

+

选择图片后上传,服务端调用大模型解析为可编辑的 JSON,再确认入库。

+
-
- - - +
+

上传图片

+

点击下方按钮选择图片,或拖拽图片到此区域

+
+ {% csrf_token %} + +
+ +
+
+
+ +
+
+

图片预览

+ 预览 +
+ +
+

识别结果(可编辑)

+
+ + +
+
+ +
+
+ +
+ + +
@@ -177,15 +350,63 @@ const confirmMsg = document.getElementById('confirmMsg'); const kvForm = document.getElementById('kvForm'); const addFieldBtn = document.getElementById('addFieldBtn'); const syncFromTextBtn = document.getElementById('syncFromTextBtn'); +const dropArea = document.getElementById('dropArea'); let currentImageRel = ''; +// 拖拽上传功能 +['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, preventDefaults, false); +}); + +function preventDefaults(e) { + e.preventDefault(); + e.stopPropagation(); +} + +['dragenter', 'dragover'].forEach(eventName => { + dropArea.addEventListener(eventName, highlight, false); +}); + +['dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, unhighlight, false); +}); + +function highlight() { + dropArea.classList.add('drag-over'); +} + +function unhighlight() { + dropArea.classList.remove('drag-over'); +} + +dropArea.addEventListener('drop', handleDrop, false); + +function handleDrop(e) { + const dt = e.dataTransfer; + const files = dt.files; + if (files.length) { + fileInput.files = files; + const event = new Event('change', { bubbles: true }); + fileInput.dispatchEvent(event); + } +} + +// 文件选择后预览 +fileInput.addEventListener('change', function(e) { + const file = e.target.files[0]; + if (file && file.type.startsWith('image/')) { + const reader = new FileReader(); + reader.onload = function(e) { + preview.src = e.target.result; + }; + reader.readAsDataURL(file); + } +}); + function createRow(k = '', v = '') { const row = document.createElement('div'); - row.style.display = 'grid'; - row.style.gridTemplateColumns = '1fr 1fr auto'; - row.style.gap = '8px'; - row.style.marginBottom = '6px'; + row.className = 'form-row'; const keyInput = document.createElement('input'); keyInput.type = 'text'; keyInput.placeholder = '字段名'; @@ -196,9 +417,17 @@ function createRow(k = '', v = '') { valInput.value = typeof v === 'object' ? JSON.stringify(v) : (v ?? ''); const delBtn = document.createElement('button'); delBtn.type = 'button'; - delBtn.className = 'btn btn-secondary'; + delBtn.className = 'btn btn-danger'; delBtn.textContent = '删除'; - delBtn.onclick = () => { kvForm.removeChild(row); syncTextarea(); }; + delBtn.onclick = () => { + if (kvForm.children.length > 1) { + kvForm.removeChild(row); + } else { + keyInput.value = ''; + valInput.value = ''; + } + syncTextarea(); + }; keyInput.oninput = syncTextarea; valInput.oninput = syncTextarea; row.appendChild(keyInput); @@ -246,9 +475,16 @@ syncFromTextBtn.addEventListener('click', () => { try { const obj = JSON.parse(resultBox.value || '{}'); renderFormFromObject(obj); + uploadMsg.textContent = '已从文本区刷新表单'; + uploadMsg.className = 'status-message success'; + uploadMsg.style.display = 'block'; + setTimeout(() => { + uploadMsg.style.display = 'none'; + }, 2000); } catch (e) { uploadMsg.textContent = '文本区不是有效JSON'; - uploadMsg.className = 'error'; + uploadMsg.className = 'status-message error'; + uploadMsg.style.display = 'block'; } }); @@ -263,7 +499,8 @@ uploadForm.addEventListener('submit', async (e) => { const file = fileInput.files[0]; if (!file) { uploadMsg.textContent = '请选择图片文件'; - uploadMsg.className = 'error'; + uploadMsg.className = 'status-message error'; + uploadMsg.style.display = 'block'; return; } @@ -282,14 +519,16 @@ uploadForm.addEventListener('submit', async (e) => { throw new Error(data.message || '上传识别失败'); } uploadMsg.textContent = data.message || '识别成功'; - uploadMsg.className = 'success'; + uploadMsg.className = 'status-message success'; + uploadMsg.style.display = 'block'; preview.src = data.image_url; renderFormFromObject(data.data || {}); currentImageRel = data.image; confirmBtn.disabled = false; } catch (e) { uploadMsg.textContent = e.message || '发生错误'; - uploadMsg.className = 'error'; + uploadMsg.className = 'status-message error'; + uploadMsg.style.display = 'block'; } }); @@ -311,10 +550,10 @@ confirmBtn.addEventListener('click', async () => { throw new Error(data.message || '录入失败'); } confirmMsg.textContent = data.message || '录入成功'; - confirmMsg.className = 'success'; + confirmMsg.style.color = '#179957'; } catch (e) { confirmMsg.textContent = e.message || '发生错误'; - confirmMsg.className = 'error'; + confirmMsg.style.color = '#d14343'; } }); @@ -323,6 +562,7 @@ clearBtn.addEventListener('click', () => { preview.src = ''; resultBox.value = ''; kvForm.innerHTML = ''; + kvForm.appendChild(createRow()); // 保留一个空行 uploadMsg.textContent = ''; confirmMsg.textContent = ''; confirmBtn.disabled = true; diff --git a/elastic/templates/elastic/users.html b/elastic/templates/elastic/users.html index 406dc68..48d55cd 100644 --- a/elastic/templates/elastic/users.html +++ b/elastic/templates/elastic/users.html @@ -35,7 +35,7 @@ .sidebar h3 { margin-top: 0; font-size: 18px; - color: #ff79c6; + color: #add8e6; text-align: center; margin-bottom: 20px; } diff --git a/main/templates/main/home.html b/main/templates/main/home.html index 6bef3af..79d3a5c 100644 --- a/main/templates/main/home.html +++ b/main/templates/main/home.html @@ -35,7 +35,7 @@ .sidebar h3 { margin-top: 0; font-size: 18px; - color: #ff79c6; + color: #add8e6; text-align: center; margin-bottom: 20px; }