/** * 文件上传模块 */ import { App } from './state.js'; import { addFileMessage, addChatMessage } from './chat.js'; import { promptMissingConfig, checkAutoStart } from './config.js'; import { handleSupplementUpload, hideAgentRequest } from './agent.js'; import { ensureSession } from './utils.js'; export async function handleFiles(input) { const files = Array.from(input.files); const result = await _processFiles(files); if (result.hasNewFiles && App.processState === 'done') { App.processState = 'idle'; App.newFilenames = result.newFilenames; } if (result.hasNewFiles && App.processState === 'awaiting_supplement') { hideAgentRequest(); await _uploadNewFiles(result.newFilenames); App.newFilenames = result.newFilenames; App.processState = 'idle'; return; } promptMissingConfig(); input.value = ''; } /** * 统一文件处理逻辑(handleFiles 和拖拽共享) */ async function _processFiles(files) { const validExts = /\.(pdf|png|jpe?g|bmp|webp)$/i; let hasNewFiles = false; const newFilenames = []; for (const f of files) { if (/\.json$/i.test(f.name)) { await parseConfigFile(f); continue; } if (!App.allFiles.find(x => x.name === f.name) && validExts.test(f.name)) { f.__source = 'local'; App.allFiles.push(f); document.getElementById('file-zone').classList.add('active'); addFileMessage(f.name); hasNewFiles = true; newFilenames.push(f.name); } } return { hasNewFiles, newFilenames }; } async function _uploadNewFiles(filenames) { await ensureSession(); for (const f of App.allFiles) { if (filenames.includes(f.name)) { const fd = new FormData(); fd.append('file', f); await fetch(`/api/upload/${App.sessionId}`, { method: 'POST', body: fd }); } } } async function parseConfigFile(file) { return new Promise((resolve) => { const reader = new FileReader(); reader.onload = function (e) { try { const cfg = JSON.parse(e.target.result); Object.assign(App.sessionConfig, { username: cfg.username || '', password: cfg.password || '', default_name: cfg.default_name || '', default_card_no: cfg.default_card_no || '', default_person_id: cfg.default_person_id || '', consumable_storage: cfg.consumable_storage || '', }); addChatMessage(`config.json 已加载`, 'system'); resolve(true); } catch (err) { console.warn('config.json 解析失败:', err.message); resolve(false); } }; reader.readAsText(file); }); } export function initDragDrop() { const zone = document.getElementById('file-zone'); if (!zone) return; zone.addEventListener('dragover', e => { e.preventDefault(); zone.classList.add('dragover'); }); zone.addEventListener('dragleave', () => zone.classList.remove('dragover')); zone.addEventListener('drop', async e => { e.preventDefault(); zone.classList.remove('dragover'); const droppedFiles = Array.from(e.dataTransfer.files); const result = await _processFiles(droppedFiles); if (result.hasNewFiles && App.processState === 'done') { App.processState = 'idle'; } // 在 awaiting_supplement 状态下,只上传新文件并走 supplement 流程(手动触发) if (result.hasNewFiles && App.processState === 'awaiting_supplement') { hideAgentRequest(); await _uploadNewFiles(result.newFilenames); App.newFilenames = result.newFilenames; App.processState = 'idle'; return; } promptMissingConfig(); }); } /** * 切换上传面板折叠/展开状态 */ export function toggleUploadPanel() { const body = document.getElementById('upload-panel-body'); const icon = document.getElementById('upload-toggle-icon'); if (!body || !icon) return; body.classList.toggle('collapsed'); icon.classList.toggle('collapsed'); }