feat: processing flow enhancement + responsive UI

Phase 2 - Processing flow:
- Multi-task monitoring: store supports concurrent task tracking
- Task retry: POST /api/tasks/{id}/retry re-runs failed tasks
- Dashboard multi-task cards with progress, error details, retry/dismiss
- Log panel expanded from 10 to 50 lines with "view all" link

Phase 3 - UI/UX:
- Mobile sidebar drawer (< 768px) with hamburger menu
- Layout responsive styles (768px, 480px breakpoints)
- Tasks/Logs pages responsive (stat cards, filters, columns)
- File views responsive (header wrap, button sizing)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-13 19:18:18 +08:00
parent 32af38fe2a
commit 7baf784a39
11 changed files with 585 additions and 101 deletions
+8
View File
@@ -234,6 +234,7 @@ async def ocr_batch(
"""Run OCR on all images in input/."""
tm = _get_task_manager(request)
task = tm.create_task("批量OCR识别")
task.metadata = {"endpoint": "/api/processing/ocr-batch", "body": {}}
image_exts = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif'}
files = _list_input_files(filter_ext=list(image_exts))
@@ -296,6 +297,7 @@ async def process_excel(
"""Convert OCR output Excel files to standardized purchase orders."""
tm = _get_task_manager(request)
task = tm.create_task("Excel标准化处理")
task.metadata = {"endpoint": "/api/processing/excel", "body": body.dict()}
excel_exts = {'.xls', '.xlsx'}
if body.files:
@@ -354,6 +356,7 @@ async def merge_orders(
"""Merge selected purchase order files into one PosPal template."""
tm = _get_task_manager(request)
task = tm.create_task("合并采购单")
task.metadata = {"endpoint": "/api/processing/merge", "body": body.dict()}
# If specific files provided, use them; otherwise merge all
if body.filenames:
@@ -399,6 +402,7 @@ async def full_pipeline(
"""Run the full pipeline: OCR -> Excel -> Result (NO merge)."""
tm = _get_task_manager(request)
task = tm.create_task("一键全流程处理")
task.metadata = {"endpoint": "/api/processing/pipeline", "body": body.dict()}
async def _bg():
def do_work():
@@ -501,6 +505,7 @@ async def ocr_single(
"""OCR a single image file."""
tm = _get_task_manager(request)
task = tm.create_task(f"OCR: {body.filename}")
task.metadata = {"endpoint": "/api/processing/ocr-single", "body": body.dict()}
file_path = _input_dir / body.filename
if not file_path.is_file():
@@ -544,6 +549,7 @@ async def excel_single(
"""Process a single Excel file to purchase order."""
tm = _get_task_manager(request)
task = tm.create_task(f"Excel处理: {body.filename}")
task.metadata = {"endpoint": "/api/processing/excel-single", "body": body.dict()}
file_path = _output_dir / body.filename
if not file_path.is_file():
@@ -582,6 +588,7 @@ async def pipeline_single(
"""Full pipeline for a single image: OCR -> Excel -> Result (no merge)."""
tm = _get_task_manager(request)
task = tm.create_task(f"全流程: {body.filename}")
task.metadata = {"endpoint": "/api/processing/pipeline-single", "body": body.dict()}
file_path = _input_dir / body.filename
if not file_path.is_file():
@@ -659,6 +666,7 @@ async def merge_batch(
"""Merge selected purchase order files into one PosPal template."""
tm = _get_task_manager(request)
task = tm.create_task("批量合并采购单")
task.metadata = {"endpoint": "/api/processing/merge-batch", "body": body.dict()}
file_paths = [_result_dir / f for f in body.filenames if (_result_dir / f).is_file()]
if not file_paths: