多文件上传累积模式,前端文件列表支持删除,后端接收前端指定的文件列表
This commit is contained in:
12
web/main.py
12
web/main.py
@@ -356,6 +356,7 @@ def run_analysis_task(session_id: str, files: list, user_requirement: str, is_fo
|
|||||||
class StartRequest(BaseModel):
|
class StartRequest(BaseModel):
|
||||||
requirement: str
|
requirement: str
|
||||||
template: Optional[str] = None
|
template: Optional[str] = None
|
||||||
|
files: Optional[List[str]] = None
|
||||||
|
|
||||||
class ChatRequest(BaseModel):
|
class ChatRequest(BaseModel):
|
||||||
session_id: str
|
session_id: str
|
||||||
@@ -383,10 +384,15 @@ async def upload_files(files: list[UploadFile] = File(...)):
|
|||||||
async def start_analysis(request: StartRequest, background_tasks: BackgroundTasks):
|
async def start_analysis(request: StartRequest, background_tasks: BackgroundTasks):
|
||||||
session_id = session_manager.create_session()
|
session_id = session_manager.create_session()
|
||||||
|
|
||||||
# Use only the most recently uploaded files, not everything in uploads/
|
# Priority: request.files (from frontend) > last_uploaded > scan uploads/
|
||||||
files = getattr(app.state, 'last_uploaded_files', None)
|
files = None
|
||||||
|
if request.files:
|
||||||
|
files = [f for f in request.files if os.path.exists(f)]
|
||||||
|
if not files:
|
||||||
|
files = getattr(app.state, 'last_uploaded_files', None)
|
||||||
|
if files:
|
||||||
|
files = [f for f in files if os.path.exists(f)]
|
||||||
if not files:
|
if not files:
|
||||||
# Fallback: scan uploads directory
|
|
||||||
files = glob.glob("uploads/*.csv") + glob.glob("uploads/*.xlsx")
|
files = glob.glob("uploads/*.csv") + glob.glob("uploads/*.xlsx")
|
||||||
if not files:
|
if not files:
|
||||||
raise HTTPException(status_code=400, detail="No data files found. Please upload files first.")
|
raise HTTPException(status_code=400, detail="No data files found. Please upload files first.")
|
||||||
|
|||||||
@@ -409,6 +409,18 @@ body {
|
|||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-remove {
|
||||||
|
margin-left: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #9CA3AF;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 0.25rem;
|
||||||
|
}
|
||||||
|
.file-remove:hover {
|
||||||
|
color: #EF4444;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tabs */
|
/* Tabs */
|
||||||
.tabs {
|
.tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -64,29 +64,49 @@ if (fileInput) {
|
|||||||
fileInput.addEventListener('click', (e) => e.stopPropagation());
|
fileInput.addEventListener('click', (e) => e.stopPropagation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track all uploaded file paths for this session
|
||||||
|
let uploadedFilePaths = [];
|
||||||
|
|
||||||
async function handleFiles(files) {
|
async function handleFiles(files) {
|
||||||
if (files.length === 0) return;
|
if (files.length === 0) return;
|
||||||
|
|
||||||
fileList.innerHTML = '';
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
formData.append('files', file);
|
formData.append('files', file);
|
||||||
const fileItem = document.createElement('div');
|
|
||||||
fileItem.className = 'file-item';
|
|
||||||
fileItem.innerHTML = `<i class="fa-regular fa-file-excel"></i> ${file.name}`;
|
|
||||||
fileList.appendChild(fileItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/upload', { method: 'POST', body: formData });
|
const res = await fetch('/api/upload', { method: 'POST', body: formData });
|
||||||
if (!res.ok) alert('Upload failed');
|
if (!res.ok) { alert('Upload failed'); return; }
|
||||||
|
const data = await res.json();
|
||||||
|
// Accumulate uploaded paths
|
||||||
|
if (data.paths) {
|
||||||
|
uploadedFilePaths = uploadedFilePaths.concat(data.paths);
|
||||||
|
}
|
||||||
|
renderFileList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert('Upload failed');
|
alert('Upload failed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderFileList() {
|
||||||
|
fileList.innerHTML = '';
|
||||||
|
for (let i = 0; i < uploadedFilePaths.length; i++) {
|
||||||
|
const fname = uploadedFilePaths[i].split('/').pop().split('\\').pop();
|
||||||
|
const fileItem = document.createElement('div');
|
||||||
|
fileItem.className = 'file-item';
|
||||||
|
fileItem.innerHTML = `<i class="fa-regular fa-file-excel"></i> ${fname}
|
||||||
|
<span class="file-remove" onclick="removeUploadedFile(${i})" title="移除">×</span>`;
|
||||||
|
fileList.appendChild(fileItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.removeUploadedFile = function(index) {
|
||||||
|
uploadedFilePaths.splice(index, 1);
|
||||||
|
renderFileList();
|
||||||
|
}
|
||||||
|
|
||||||
// --- Template Logic ---
|
// --- Template Logic ---
|
||||||
async function loadTemplates() {
|
async function loadTemplates() {
|
||||||
try {
|
try {
|
||||||
@@ -142,6 +162,9 @@ async function startAnalysis() {
|
|||||||
if (selectedTemplate) {
|
if (selectedTemplate) {
|
||||||
body.template = selectedTemplate;
|
body.template = selectedTemplate;
|
||||||
}
|
}
|
||||||
|
if (uploadedFilePaths.length > 0) {
|
||||||
|
body.files = uploadedFilePaths;
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetch('/api/start', {
|
const res = await fetch('/api/start', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
Reference in New Issue
Block a user