Enhance backend functionality and frontend UI

- Updated main.py to include authentication for brand, model, and OTA routers.
- Added new OTA schemas in schemas.py for version management.
- Enhanced model retrieval with sorting options in models.py.
- Improved model update functionality to support multipart/form-data uploads.
- Updated frontend layout and styles for a more modern look, including new font integration.
- Implemented login route and authentication checks in router/index.js.
- Added sorting capabilities in model table and improved file handling in model view.
- Updated requirements.txt to include PyJWT for token management.
This commit is contained in:
yangy
2026-05-14 17:54:36 +08:00
parent b1d088a755
commit 661b85ce62
23 changed files with 2104 additions and 119 deletions
+80 -36
View File
@@ -1,10 +1,10 @@
from fastapi import APIRouter, Depends, HTTPException, Query, Body, UploadFile, File, Form
from sqlalchemy.orm import Session
from sqlalchemy import asc, desc
from typing import List, Optional
import logging
from database import get_db
from models.model import Model
from schemas import ModelCreate, ModelUpdate, ModelResponse
from response import ApiResponse, PageData
import requests
import os
@@ -36,18 +36,34 @@ def get_models(
limit: int = Query(100, ge=1, le=1000, description="返回记录数"),
brand_name: Optional[str] = Query(None, description="按品牌名称模糊查询"),
name: Optional[str] = Query(None, description="按型号名称模糊查询"),
sort_by: str = Query("id", description="排序字段:id 或 create_at"),
sort_order: str = Query("desc", description="排序方向:asc 或 desc"),
db: Session = Depends(get_db)
):
"""获取所有型号列表(支持按品牌名称和型号名称模糊查询)"""
"""获取所有型号列表(支持按品牌名称和型号名称模糊查询;支持按 id、create_at 排序"""
try:
logger.info(f"Getting models: skip={skip}, limit={limit}, brand_name={brand_name}, name={name}")
logger.info(
f"Getting models: skip={skip}, limit={limit}, brand_name={brand_name}, name={name}, "
f"sort_by={sort_by}, sort_order={sort_order}"
)
query = db.query(Model)
if brand_name:
query = query.filter(Model.brand_name.like(f"%{brand_name}%"))
if name:
query = query.filter(Model.name.like(f"%{name}%"))
total = query.count()
sort_columns = {"id": Model.id, "create_at": Model.create_at}
order_col = sort_columns.get(sort_by, Model.id)
order_dir = (sort_order or "desc").lower()
if order_dir not in ("asc", "desc"):
order_dir = "desc"
if order_dir == "desc":
query = query.order_by(desc(order_col))
else:
query = query.order_by(asc(order_col))
models = query.offset(skip).limit(limit).all()
logger.info(f"Found {len(models)} models, total={total}")
@@ -149,43 +165,71 @@ def create_model(
@router.put("/{model_id}", response_model=ApiResponse)
def update_model(model_id: int, model: ModelUpdate, db: Session = Depends(get_db)):
"""更新型号"""
def update_model(
model_id: int,
brand_name: Optional[str] = Form(None),
name: Optional[str] = Form(None),
form: Optional[str] = Form(None),
rig: Optional[str] = Form(None),
source: Optional[str] = Form(None),
eq_key: Optional[str] = Form(None),
measurement_file: UploadFile = File(None),
db: Session = Depends(get_db),
):
"""更新型号(与 POST 一致,支持 multipart/form-data,可选上传频响文件)"""
try:
logger.info(f"Updating model: id={model_id}, data={model.dict()}")
logger.info(
f"Updating model: id={model_id}, brand_name={brand_name}, name={name}, form={form}"
)
db_model = db.query(Model).filter(Model.id == model_id).first()
if not db_model:
logger.warning(f"Model not found: id={model_id}")
return ApiResponse(code=2, msg="empty", data=None)
# 如果更新品牌或型号名称,检查是否冲突
if model.brand_name or model.name:
new_brand_name = model.brand_name or db_model.brand_name
new_name = model.name or db_model.name
if new_brand_name != db_model.brand_name or new_name != db_model.name:
existing = db.query(Model).filter(
Model.brand_name == new_brand_name,
Model.name == new_name
).first()
if existing:
logger.warning(f"Model already exists: brand_name={new_brand_name}, name={new_name}")
return ApiResponse(code=0, msg="该品牌下型号名称已存在", data=None)
# 更新字段
if model.brand_name:
db_model.brand_name = model.brand_name
if model.name:
db_model.name = model.name
if model.form is not None:
db_model.form = model.form
if model.rig is not None:
db_model.rig = model.rig
if model.source is not None:
db_model.source = model.source
if model.eq_key is not None:
db_model.eq_key = model.eq_key
new_brand_name = db_model.brand_name if brand_name is None else brand_name
new_name = db_model.name if name is None else name
if new_brand_name != db_model.brand_name or new_name != db_model.name:
existing = (
db.query(Model)
.filter(Model.brand_name == new_brand_name, Model.name == new_name)
.first()
)
if existing:
logger.warning(
f"Model already exists: brand_name={new_brand_name}, name={new_name}"
)
return ApiResponse(code=0, msg="该品牌下型号名称已存在", data=None)
eff_source = db_model.source if source is None else source
eff_form = db_model.form if form is None else form
if measurement_file and measurement_file.filename:
logger.info(f"Uploading measurement file: {measurement_file.filename}")
file_ext = os.path.splitext(measurement_file.filename)[1].lower()
if file_ext not in ALLOWED_EXTENSIONS:
logger.error(f"Unsupported file format: {file_ext}")
return ApiResponse(code=0, msg=f"不支持的文件格式:{file_ext}", data=None)
if not eff_source or not eff_form:
return ApiResponse(code=0, msg="上传频响文件需要来源与形式字段", data=None)
save_dir = UPLOAD_FOLDER / eff_source / "data" / eff_form
save_dir.mkdir(parents=True, exist_ok=True)
file_path = save_dir / measurement_file.filename
with open(file_path, "wb") as buffer:
shutil.copyfileobj(measurement_file.file, buffer)
logger.info(f"File saved: {file_path}")
if brand_name is not None:
db_model.brand_name = brand_name
if name is not None:
db_model.name = name
if form is not None:
db_model.form = form
if rig is not None:
db_model.rig = rig
if source is not None:
db_model.source = source
if eq_key is not None:
db_model.eq_key = eq_key
db.commit()
db.refresh(db_model)
logger.info(f"Model updated successfully: id={db_model.id}")