feat: 移除PDI和订单号字段,新增设备巡检模块

- 从物料跟踪页面移除订单号列和表单字段
- 从导航菜单移除PDI管理,添加设备巡检
- 新增InspectionLocation和InspectionRecord后端模型和API
- 新增设备巡检前端页面(左侧点位列表,右侧设备和历史记录)
This commit is contained in:
2026-05-27 16:38:40 +08:00
commit 193da0018f
86 changed files with 11379 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
from app.schemas.common import Response, PageResponse, PageParams
from app.schemas.user import UserCreate, UserUpdate, UserOut, Token, LoginRequest
from app.schemas.material import CoilCreate, CoilUpdate, CoilOut, TrackingCreate, TrackingOut
from app.schemas.production import ProductionRecordCreate, ProductionRecordUpdate, ProductionRecordOut
from app.schemas.plan import PlanCreate, PlanUpdate, PlanOut
from app.schemas.downtime import DowntimeCreate, DowntimeUpdate, DowntimeOut, CategoryCreate, CategoryOut
from app.schemas.equipment import EquipmentCreate, EquipmentUpdate, EquipmentOut, MaintenanceCreate, MaintenanceOut

View File

@@ -0,0 +1,30 @@
from pydantic import BaseModel
from typing import Generic, TypeVar, Optional, List
T = TypeVar("T")
class Response(BaseModel, Generic[T]):
code: int = 200
msg: str = "success"
data: Optional[T] = None
@classmethod
def ok(cls, data=None, msg="success"):
return cls(code=200, msg=msg, data=data)
@classmethod
def error(cls, msg="error", code=500):
return cls(code=code, msg=msg, data=None)
class PageParams(BaseModel):
page: int = 1
page_size: int = 20
class PageResponse(BaseModel, Generic[T]):
total: int
page: int
page_size: int
items: List[T]

View File

@@ -0,0 +1,67 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
class CategoryCreate(BaseModel):
code: str
name: str
category: str
description: Optional[str] = None
class CategoryOut(BaseModel):
id: int
code: str
name: str
category: str
description: Optional[str]
is_active: int
class Config:
from_attributes = True
class DowntimeCreate(BaseModel):
category_id: Optional[int] = None
category_code: Optional[str] = None
shift: Optional[str] = None
shift_date: Optional[datetime] = None
start_time: datetime
end_time: Optional[datetime] = None
equipment_code: Optional[str] = None
fault_desc: Optional[str] = None
action_taken: Optional[str] = None
root_cause: Optional[str] = None
reporter: Optional[str] = None
handler: Optional[str] = None
is_planned: int = 0
class DowntimeUpdate(BaseModel):
end_time: Optional[datetime] = None
action_taken: Optional[str] = None
root_cause: Optional[str] = None
handler: Optional[str] = None
fault_desc: Optional[str] = None
class DowntimeOut(BaseModel):
id: int
category_code: Optional[str]
category_name: Optional[str]
shift: Optional[str]
shift_date: Optional[datetime]
start_time: datetime
end_time: Optional[datetime]
duration: Optional[float]
equipment_code: Optional[str]
fault_desc: Optional[str]
action_taken: Optional[str]
reporter: Optional[str]
handler: Optional[str]
is_planned: int
created_at: datetime
class Config:
from_attributes = True

View File

@@ -0,0 +1,79 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from app.models.equipment import EquipmentStatus
class EquipmentCreate(BaseModel):
code: str
name: str
category: Optional[str] = None
model: Optional[str] = None
manufacturer: Optional[str] = None
install_date: Optional[datetime] = None
location: Optional[str] = None
rated_power: Optional[float] = None
remark: Optional[str] = None
class EquipmentUpdate(BaseModel):
name: Optional[str] = None
category: Optional[str] = None
model: Optional[str] = None
location: Optional[str] = None
status: Optional[EquipmentStatus] = None
remark: Optional[str] = None
class EquipmentOut(BaseModel):
id: int
code: str
name: str
category: Optional[str]
model: Optional[str]
manufacturer: Optional[str]
install_date: Optional[datetime]
location: Optional[str]
status: EquipmentStatus
rated_power: Optional[float]
created_at: datetime
class Config:
from_attributes = True
class MaintenanceCreate(BaseModel):
equipment_id: int
equipment_code: Optional[str] = None
maintenance_type: str
title: str
description: Optional[str] = None
start_time: datetime
end_time: Optional[datetime] = None
duration: Optional[float] = None
cost: Optional[float] = None
spare_parts: Optional[str] = None
technician: Optional[str] = None
approver: Optional[str] = None
next_maintenance: Optional[datetime] = None
result: Optional[str] = None
remark: Optional[str] = None
class MaintenanceOut(BaseModel):
id: int
equipment_id: int
equipment_code: Optional[str]
maintenance_type: str
title: str
description: Optional[str]
start_time: datetime
end_time: Optional[datetime]
duration: Optional[float]
cost: Optional[float]
technician: Optional[str]
result: Optional[str]
created_at: datetime
class Config:
from_attributes = True

View File

@@ -0,0 +1,37 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
class InspectionLocationCreate(BaseModel):
code: str
name: str
description: Optional[str] = None
sort_order: int = 0
class InspectionLocationOut(InspectionLocationCreate):
id: int
created_at: datetime
class Config:
from_attributes = True
class InspectionRecordCreate(BaseModel):
location_id: int
equipment_code: Optional[str] = None
equipment_name: Optional[str] = None
scan_code: Optional[str] = None
inspector: str
result: str = "normal"
notes: Optional[str] = None
class InspectionRecordOut(InspectionRecordCreate):
id: int
location_name: Optional[str] = None
created_at: datetime
class Config:
from_attributes = True

View File

@@ -0,0 +1,82 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from app.models.material import CoilStatus
class CoilCreate(BaseModel):
coil_no: str
order_no: Optional[str] = None
steel_grade: Optional[str] = None
spec_thickness: Optional[float] = None
spec_width: Optional[float] = None
target_thickness: Optional[float] = None
target_width: Optional[float] = None
gross_weight: Optional[float] = None
net_weight: Optional[float] = None
inner_diameter: Optional[float] = None
plan_id: Optional[int] = None
remark: Optional[str] = None
class CoilUpdate(BaseModel):
order_no: Optional[str] = None
steel_grade: Optional[str] = None
spec_thickness: Optional[float] = None
spec_width: Optional[float] = None
target_thickness: Optional[float] = None
target_width: Optional[float] = None
gross_weight: Optional[float] = None
net_weight: Optional[float] = None
status: Optional[CoilStatus] = None
plan_id: Optional[int] = None
remark: Optional[str] = None
class CoilOut(BaseModel):
id: int
coil_no: str
order_no: Optional[str]
steel_grade: Optional[str]
spec_thickness: Optional[float]
spec_width: Optional[float]
target_thickness: Optional[float]
target_width: Optional[float]
gross_weight: Optional[float]
net_weight: Optional[float]
status: CoilStatus
plan_id: Optional[int]
created_at: datetime
class Config:
from_attributes = True
class TrackingCreate(BaseModel):
coil_no: str
position: Optional[str] = None
event_type: str
event_desc: Optional[str] = None
actual_thickness: Optional[float] = None
actual_width: Optional[float] = None
speed: Optional[float] = None
tension: Optional[float] = None
operator: Optional[str] = None
event_time: datetime
class TrackingOut(BaseModel):
id: int
coil_id: int
coil_no: str
position: Optional[str]
event_type: str
event_desc: Optional[str]
actual_thickness: Optional[float]
actual_width: Optional[float]
speed: Optional[float]
operator: Optional[str]
event_time: datetime
class Config:
from_attributes = True

View File

@@ -0,0 +1,81 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from app.models.pdi import L3Status, L2Status
class PDIRecordCreate(BaseModel):
coil_no: str
order_no: Optional[str] = None
customer: Optional[str] = None
steel_grade: Optional[str] = None
thickness: Optional[float] = None
width: Optional[float] = None
target_thickness: Optional[float] = None
target_width: Optional[float] = None
yield_strength: Optional[float] = None
tensile_strength: Optional[float] = None
elongation: Optional[float] = None
coil_weight: Optional[float] = None
inner_diameter: Optional[float] = None
outer_diameter: Optional[float] = None
process_route: Optional[str] = None
priority: Optional[int] = 3
l3_status: Optional[L3Status] = L3Status.pending
l2_status: Optional[L2Status] = L2Status.pending
send_time: Optional[datetime] = None
confirm_time: Optional[datetime] = None
remark: Optional[str] = None
class PDIRecordUpdate(BaseModel):
order_no: Optional[str] = None
customer: Optional[str] = None
steel_grade: Optional[str] = None
thickness: Optional[float] = None
width: Optional[float] = None
target_thickness: Optional[float] = None
target_width: Optional[float] = None
yield_strength: Optional[float] = None
tensile_strength: Optional[float] = None
elongation: Optional[float] = None
coil_weight: Optional[float] = None
inner_diameter: Optional[float] = None
outer_diameter: Optional[float] = None
process_route: Optional[str] = None
priority: Optional[int] = None
l3_status: Optional[L3Status] = None
l2_status: Optional[L2Status] = None
send_time: Optional[datetime] = None
confirm_time: Optional[datetime] = None
remark: Optional[str] = None
class PDIRecordOut(BaseModel):
id: int
coil_no: str
order_no: Optional[str]
customer: Optional[str]
steel_grade: Optional[str]
thickness: Optional[float]
width: Optional[float]
target_thickness: Optional[float]
target_width: Optional[float]
yield_strength: Optional[float]
tensile_strength: Optional[float]
elongation: Optional[float]
coil_weight: Optional[float]
inner_diameter: Optional[float]
outer_diameter: Optional[float]
process_route: Optional[str]
priority: Optional[int]
l3_status: Optional[L3Status]
l2_status: Optional[L2Status]
send_time: Optional[datetime]
confirm_time: Optional[datetime]
remark: Optional[str]
created_at: datetime
updated_at: Optional[datetime]
class Config:
from_attributes = True

View File

@@ -0,0 +1,48 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from app.models.plan import PlanStatus
class PlanCreate(BaseModel):
plan_no: str
plan_date: datetime
shift: Optional[str] = None
plan_quantity: int = 0
plan_weight: float = 0
steel_grade: Optional[str] = None
spec_range: Optional[str] = None
priority: int = 5
remark: Optional[str] = None
class PlanUpdate(BaseModel):
plan_date: Optional[datetime] = None
shift: Optional[str] = None
plan_quantity: Optional[int] = None
plan_weight: Optional[float] = None
actual_quantity: Optional[int] = None
actual_weight: Optional[float] = None
status: Optional[PlanStatus] = None
priority: Optional[int] = None
remark: Optional[str] = None
class PlanOut(BaseModel):
id: int
plan_no: str
plan_date: datetime
shift: Optional[str]
plan_quantity: int
plan_weight: float
actual_quantity: int
actual_weight: float
status: PlanStatus
steel_grade: Optional[str]
spec_range: Optional[str]
priority: int
created_by: Optional[str]
created_at: datetime
class Config:
from_attributes = True

View File

@@ -0,0 +1,57 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
class ProductionRecordCreate(BaseModel):
coil_no: str
plan_id: Optional[int] = None
shift: Optional[str] = None
shift_date: Optional[datetime] = None
start_time: Optional[datetime] = None
end_time: Optional[datetime] = None
process_length: Optional[float] = None
process_weight: Optional[float] = None
avg_speed: Optional[float] = None
max_speed: Optional[float] = None
acid_consumption: Optional[float] = None
inlet_thickness: Optional[float] = None
outlet_thickness: Optional[float] = None
inlet_width: Optional[float] = None
quality_grade: Optional[str] = None
operator: Optional[str] = None
remark: Optional[str] = None
class ProductionRecordUpdate(BaseModel):
shift: Optional[str] = None
end_time: Optional[datetime] = None
process_length: Optional[float] = None
process_weight: Optional[float] = None
avg_speed: Optional[float] = None
acid_consumption: Optional[float] = None
quality_grade: Optional[str] = None
remark: Optional[str] = None
class ProductionRecordOut(BaseModel):
id: int
coil_no: str
plan_id: Optional[int]
shift: Optional[str]
shift_date: Optional[datetime]
start_time: Optional[datetime]
end_time: Optional[datetime]
process_length: Optional[float]
process_weight: Optional[float]
avg_speed: Optional[float]
max_speed: Optional[float]
acid_consumption: Optional[float]
inlet_thickness: Optional[float]
outlet_thickness: Optional[float]
quality_grade: Optional[str]
operator: Optional[str]
created_at: datetime
class Config:
from_attributes = True

View File

@@ -0,0 +1,66 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
class QualityRecordCreate(BaseModel):
coil_no: str
production_record_id: Optional[int] = None
thickness_actual: Optional[float] = None
width_actual: Optional[float] = None
flatness: Optional[float] = None
crown: Optional[float] = None
surface_defect_type: Optional[str] = None
defect_length_m: Optional[float] = None
defect_position: Optional[str] = None
pi_score: Optional[float] = None
surface_score: Optional[float] = None
overall_grade: Optional[str] = None
acid_residual: Optional[float] = None
roughness_ra: Optional[float] = None
inspector: Optional[str] = None
inspect_time: Optional[datetime] = None
is_passed: Optional[bool] = True
class QualityRecordUpdate(BaseModel):
thickness_actual: Optional[float] = None
width_actual: Optional[float] = None
flatness: Optional[float] = None
crown: Optional[float] = None
surface_defect_type: Optional[str] = None
defect_length_m: Optional[float] = None
defect_position: Optional[str] = None
pi_score: Optional[float] = None
surface_score: Optional[float] = None
overall_grade: Optional[str] = None
acid_residual: Optional[float] = None
roughness_ra: Optional[float] = None
inspector: Optional[str] = None
inspect_time: Optional[datetime] = None
is_passed: Optional[bool] = None
class QualityRecordOut(BaseModel):
id: int
coil_no: str
production_record_id: Optional[int]
thickness_actual: Optional[float]
width_actual: Optional[float]
flatness: Optional[float]
crown: Optional[float]
surface_defect_type: Optional[str]
defect_length_m: Optional[float]
defect_position: Optional[str]
pi_score: Optional[float]
surface_score: Optional[float]
overall_grade: Optional[str]
acid_residual: Optional[float]
roughness_ra: Optional[float]
inspector: Optional[str]
inspect_time: Optional[datetime]
is_passed: Optional[bool]
created_at: datetime
class Config:
from_attributes = True

View File

@@ -0,0 +1,41 @@
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
class LoginRequest(BaseModel):
username: str
password: str
class Token(BaseModel):
access_token: str
token_type: str = "bearer"
username: str
role: str
class UserCreate(BaseModel):
username: str
full_name: Optional[str] = None
password: str
role: str = "operator"
class UserUpdate(BaseModel):
full_name: Optional[str] = None
role: Optional[str] = None
is_active: Optional[bool] = None
password: Optional[str] = None
class UserOut(BaseModel):
id: int
username: str
full_name: Optional[str]
role: str
is_active: bool
created_at: datetime
class Config:
from_attributes = True