From 649e667ad00b08ff397d046c625b9a30b2e12257 Mon Sep 17 00:00:00 2001 From: wangyu <823267011@qq.com> Date: Mon, 29 Jun 2026 16:01:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E6=97=B6=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=8C=81=E4=B9=85=E5=8C=96=E5=88=B0=E8=AE=A1=E5=88=92/?= =?UTF-8?q?=E5=AE=9E=E7=BB=A9=20+=20=E8=B4=A8=E9=87=8F=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E6=94=B9=E7=89=88(=E9=92=A2=E5=8D=B7=E4=BF=A1=E6=81=AF+?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E8=AE=B0=E5=BD=95+=E7=BB=A7=E6=89=BF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在产计划持久化实时数据快照(run_data),物料跟踪每次轮询上报;生产完成写入实绩 process_data - 实绩页点击行展开生产阶段数据 + 实时数据快照(测点列表) - 质量管理默认进入异常管理,改为全宽:顶部选卷 + 钢卷信息(5列) + 异常记录表 - 异常记录新增「继承来源」列与「继承」按钮(从来源卷复制缺陷),进入即预置6行 - qc_defect 增加 inherit_source 字段 Co-Authored-By: Claude Opus 4.8 --- backend/app/api/plan.py | 14 +++- backend/app/database.py | 3 + backend/app/models/plan.py | 1 + backend/app/models/production.py | 3 +- backend/app/models/quality.py | 1 + backend/app/schemas/plan.py | 1 + backend/app/schemas/production.py | 1 + backend/app/schemas/quality.py | 3 + backend/app/services/line_service.py | 1 + frontend/src/api/index.js | 1 + frontend/src/views/Material.vue | 16 +++- frontend/src/views/Production.vue | 18 +++++ frontend/src/views/Quality.vue | 116 ++++++++++++++++++--------- 13 files changed, 139 insertions(+), 40 deletions(-) diff --git a/backend/app/api/plan.py b/backend/app/api/plan.py index f9b8907..7f8c2f3 100644 --- a/backend/app/api/plan.py +++ b/backend/app/api/plan.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException, Query +from fastapi import APIRouter, Depends, HTTPException, Query, Body from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func, desc from typing import Optional @@ -192,6 +192,18 @@ async def commit_producing(plan_id: int, db: AsyncSession = Depends(get_db), _ = return Response.ok(PlanOut.model_validate(plan)) +@router.patch("/{plan_id}/runtime", response_model=Response[dict]) +async def save_runtime(plan_id: int, body: dict = Body(...), db: AsyncSession = Depends(get_db), _ = Depends(get_current_user)): + """持久化在产卷的实时数据快照到对应计划。""" + result = await db.execute(select(ProductionPlan).where(ProductionPlan.id == plan_id)) + plan = result.scalar_one_or_none() + if not plan: + raise HTTPException(status_code=404, detail="计划不存在") + if plan.status == "producing": + plan.run_data = body + return Response.ok({"ok": True}) + + @router.get("/saddle/current", response_model=Response[Optional[PlanOut]]) async def get_saddle(db: AsyncSession = Depends(get_db), _ = Depends(get_current_user)): """上卷鞍座当前计划(含实时速度/已生产长度),并推进联动。""" diff --git a/backend/app/database.py b/backend/app/database.py index 174976e..f5adbbf 100644 --- a/backend/app/database.py +++ b/backend/app/database.py @@ -67,6 +67,9 @@ async def _run_migrations(conn): "ALTER TABLE production_plans ADD COLUMN IF NOT EXISTS run_speed DOUBLE PRECISION DEFAULT 0", "ALTER TABLE production_plans ADD COLUMN IF NOT EXISTS run_length_m DOUBLE PRECISION DEFAULT 0", "ALTER TABLE production_plans ADD COLUMN IF NOT EXISTS produced_at TIMESTAMP", + "ALTER TABLE production_plans ADD COLUMN IF NOT EXISTS run_data JSONB", + "ALTER TABLE production_records ADD COLUMN IF NOT EXISTS process_data JSONB", + "ALTER TABLE qc_defect ADD COLUMN IF NOT EXISTS inherit_source VARCHAR(30)", # 状态列改为 VARCHAR 以适配新值 "ALTER TABLE production_plans ALTER COLUMN status TYPE VARCHAR(20) USING status::text", # production_records 新字段 diff --git a/backend/app/models/plan.py b/backend/app/models/plan.py index 5b6746f..9f5f954 100644 --- a/backend/app/models/plan.py +++ b/backend/app/models/plan.py @@ -43,6 +43,7 @@ class ProductionPlan(Base): run_speed = Column(Float, default=0, comment="当前线速度 m/min") run_length_m = Column(Float, default=0, comment="带头已生产长度 m") produced_at = Column(DateTime, comment="生产完成时间") + run_data = Column(JSON, comment="生产期间实时数据快照") # 兼容历史字段 shift = Column(String(10), comment="班次") diff --git a/backend/app/models/production.py b/backend/app/models/production.py index 44816e5..84ede7a 100644 --- a/backend/app/models/production.py +++ b/backend/app/models/production.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String, Float, DateTime, Text, ForeignKey, func +from sqlalchemy import Column, Integer, String, Float, DateTime, Text, ForeignKey, JSON, func from app.database import Base @@ -30,6 +30,7 @@ class ProductionRecord(Base): length_per_ton = Column(Float, comment="吨钢长度 m/t") offline_time = Column(DateTime, comment="下线时间") status = Column(String(20), default="UNWEIGH", comment="状态: UNWEIGH/PRODUCT") + process_data = Column(JSON, comment="生产阶段实时数据快照") # 兼容历史字段 shift_date = Column(DateTime) diff --git a/backend/app/models/quality.py b/backend/app/models/quality.py index 3d7dfff..fbd1b93 100644 --- a/backend/app/models/quality.py +++ b/backend/app/models/quality.py @@ -57,6 +57,7 @@ class QcDefect(Base): side_middle = Column(Boolean, default=False, comment="中间") side_drive = Column(Boolean, default=False, comment="驱动侧") is_main = Column(Boolean, default=False, comment="主缺陷") + inherit_source = Column(String(30), nullable=True, comment="继承来源卷号") image_url = Column(String(255), nullable=True, comment="缺陷图片URL") # 兼容旧字段 diff --git a/backend/app/schemas/plan.py b/backend/app/schemas/plan.py index f6da6ad..cccf8e2 100644 --- a/backend/app/schemas/plan.py +++ b/backend/app/schemas/plan.py @@ -86,6 +86,7 @@ class PlanOut(BaseModel): run_speed: Optional[float] = 0 run_length_m: Optional[float] = 0 produced_at: Optional[datetime] = None + run_data: Optional[dict] = None class Config: from_attributes = True diff --git a/backend/app/schemas/production.py b/backend/app/schemas/production.py index 212dd15..659b142 100644 --- a/backend/app/schemas/production.py +++ b/backend/app/schemas/production.py @@ -94,6 +94,7 @@ class ProductionRecordOut(BaseModel): inlet_width: Optional[float] = None quality_grade: Optional[str] = None operator: Optional[str] = None + process_data: Optional[dict] = None created_at: datetime class Config: diff --git a/backend/app/schemas/quality.py b/backend/app/schemas/quality.py index 7136e06..9d83671 100644 --- a/backend/app/schemas/quality.py +++ b/backend/app/schemas/quality.py @@ -98,6 +98,7 @@ class QcDefectBase(BaseModel): side_middle: Optional[bool] = False side_drive: Optional[bool] = False is_main: Optional[bool] = False + inherit_source: Optional[str] = None image_url: Optional[str] = None defect_code: Optional[str] = None defect_type: Optional[str] = None @@ -130,6 +131,7 @@ class QcDefectUpdate(BaseModel): side_middle: Optional[bool] = None side_drive: Optional[bool] = None is_main: Optional[bool] = None + inherit_source: Optional[str] = None image_url: Optional[str] = None defect_code: Optional[str] = None defect_type: Optional[str] = None @@ -156,6 +158,7 @@ class QcDefectOut(BaseModel): side_middle: Optional[bool] = None side_drive: Optional[bool] = None is_main: Optional[bool] = None + inherit_source: Optional[str] = None image_url: Optional[str] = None production_line: Optional[str] = None position: Optional[str] = None diff --git a/backend/app/services/line_service.py b/backend/app/services/line_service.py index 6900587..2231900 100644 --- a/backend/app/services/line_service.py +++ b/backend/app/services/line_service.py @@ -148,6 +148,7 @@ async def _produce(db: AsyncSession, plan: ProductionPlan): inlet_width=plan.incoming_width, quality_grade="A", operator="系统", + process_data=plan.run_data, ) db.add(rec) logger.info(f"生产完成并产生实绩: {plan.cold_coil_no or plan.plan_no}") diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 287d8e7..5e9c478 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -27,6 +27,7 @@ export const movePlan = (id, position) => request.patch(`/plan/${id}/move`, null export const getPositions = () => request.get('/plan/positions/all') export const commitProducing = id => request.patch(`/plan/${id}/commit`) // 投入生产 export const getSaddle = () => request.get('/plan/saddle/current') +export const saveRuntime = (id, data) => request.patch(`/plan/${id}/runtime`, data) export const seedPlans = (count = 50) => request.post('/plan/seed', null, { params: { count } }) export const getLastPlanTemplate = () => request.get('/plan/last-template') diff --git a/frontend/src/views/Material.vue b/frontend/src/views/Material.vue index a8a642c..d026fa2 100644 --- a/frontend/src/views/Material.vue +++ b/frontend/src/views/Material.vue @@ -337,7 +337,7 @@