diff --git a/backend/app/api/qc.py b/backend/app/api/qc.py index bc551bc1..27ad3d51 100644 --- a/backend/app/api/qc.py +++ b/backend/app/api/qc.py @@ -14,6 +14,8 @@ from app.crud import qc_task_result as result_svc from app.crud import qc_task as task_svc from app.crud import qc_third_party_company as tpc_svc from app.crud import qc_third_party_item as tpi_svc +from app.crud import qc_material as mat_svc +from app.crud import qc_material_usage as mu_svc from app.crud import qc_work_order as wo_svc from app.crud.collection_protocol import total_pages from app.api.sample_reception import _get_current_user_id, _get_tenant_no, _page_payload @@ -373,6 +375,7 @@ class TaskCompleteWithResultBody(BaseModel): results: List[Dict[str, Any]] = Field(default_factory=list, alias="results") remark: Optional[str] = Field(None, alias="remark") third_party_attachments: Optional[str] = Field(None, alias="thirdPartyAttachments") + material_usages: Optional[List[Dict[str, Any]]] = Field(None, alias="materialUsages") @router.get("/qc-lab-test-items") @@ -745,6 +748,18 @@ async def complete_task_with_result( db, task_id, attachments=body.third_party_attachments, tenant_no=tenant_no, user_id=user_id, ) + # 物料扣减(可选,仅当传了 materialUsages 时) + if body.material_usages: + task_obj_pre = task_svc.get_task(db, task_id, tenant_no=tenant_no) + task_no = task_obj_pre.task_no if task_obj_pre else "" + mu_svc.deduct_for_task( + db, + task_id, + task_no=task_no, + material_usages=body.material_usages, + tenant_no=tenant_no, + user_id=user_id, + ) obj = task_svc.apply_task_result( db, task_id, @@ -788,3 +803,165 @@ async def update_qc_task_attachments( return error_response(str(e)) except Exception as e: return error_response(str(e)) + + +# ---------- 质检物料库 ---------- + + +class QcMaterialBody(BaseModel): + model_config = ConfigDict(populate_by_name=True) + material_code: str = Field(..., alias="materialCode") + material_name: str = Field(..., alias="materialName") + spec_model: Optional[str] = Field(None, alias="specModel") + unit: Optional[str] = None + warning_qty: Optional[float] = Field(0, alias="warningQty") + status: str = "1" + remark: Optional[str] = None + + +class QcMaterialUpdateBody(BaseModel): + model_config = ConfigDict(populate_by_name=True) + material_code: Optional[str] = Field(None, alias="materialCode") + material_name: Optional[str] = Field(None, alias="materialName") + spec_model: Optional[str] = Field(None, alias="specModel") + unit: Optional[str] = None + warning_qty: Optional[float] = Field(None, alias="warningQty") + status: Optional[str] = None + remark: Optional[str] = None + + +class StockInBody(BaseModel): + model_config = ConfigDict(populate_by_name=True) + qty: float = Field(..., gt=0) + remark: Optional[str] = None + + +@router.get("/qc-materials") +async def list_qc_materials( + page: int = Query(1, ge=1), + pageSize: int = Query(10, ge=1, le=100), + keyword: Optional[str] = None, + status: Optional[str] = None, + db: Session = Depends(get_db), + user: dict = Depends(require_menu_code("biz:qcMaterial:list")), +): + try: + tenant_no = _get_tenant_no(db, user) + items, total = mat_svc.list_materials( + db, page=page, page_size=pageSize, keyword=keyword, status=status, tenant_no=tenant_no, + ) + return success_response(_page_payload(items, total, page, pageSize)) + except PermissionError as e: + return error_response(str(e), code=403) + except Exception as e: + return error_response(str(e)) + + +@router.get("/qc-materials/options") +async def qc_material_options( + db: Session = Depends(get_db), + user: dict = Depends(require_menu_code("biz:qcMaterial:list")), +): + try: + tenant_no = _get_tenant_no(db, user) + return success_response(mat_svc.options_list(db, tenant_no=tenant_no)) + except Exception as e: + return error_response(str(e)) + + +@router.post("/qc-materials") +async def create_qc_material( + body: QcMaterialBody, + db: Session = Depends(get_db), + user: dict = Depends(require_menu_code("biz:qcMaterial:list")), +): + try: + tenant_no = _get_tenant_no(db, user) + user_id = _get_current_user_id(user) + obj = mat_svc.create_material(db, body.model_dump(by_alias=True, exclude_none=True), tenant_no=tenant_no, user_id=user_id) + return success_response(obj, message="创建成功") + except ValueError as e: + return error_response(str(e)) + except Exception as e: + return error_response(str(e)) + + +@router.put("/qc-materials/{material_id}") +async def update_qc_material( + material_id: str, + body: QcMaterialUpdateBody, + db: Session = Depends(get_db), + user: dict = Depends(require_menu_code("biz:qcMaterial:list")), +): + try: + tenant_no = _get_tenant_no(db, user) + user_id = _get_current_user_id(user) + obj = mat_svc.update_material(db, material_id, body.model_dump(by_alias=True, exclude_none=True), tenant_no=tenant_no, user_id=user_id) + return success_response(obj, message="更新成功") + except ValueError as e: + return error_response(str(e)) + except Exception as e: + return error_response(str(e)) + + +@router.delete("/qc-materials/{material_id}") +async def delete_qc_material( + material_id: str, + db: Session = Depends(get_db), + user: dict = Depends(require_menu_code("biz:qcMaterial:list")), +): + try: + tenant_no = _get_tenant_no(db, user) + user_id = _get_current_user_id(user) + mat_svc.soft_delete_material(db, material_id, tenant_no=tenant_no, user_id=user_id) + return success_response(None, message="删除成功") + except ValueError as e: + return error_response(str(e)) + except Exception as e: + return error_response(str(e)) + + +@router.post("/qc-materials/{material_id}/stock-in") +async def stock_in_qc_material( + material_id: str, + body: StockInBody, + db: Session = Depends(get_db), + user: dict = Depends(require_menu_code("biz:qcMaterial:list")), +): + try: + tenant_no = _get_tenant_no(db, user) + user_id = _get_current_user_id(user) + obj = mat_svc.stock_in(db, material_id, qty=body.qty, remark=body.remark, tenant_no=tenant_no, user_id=user_id) + return success_response(obj, message="入库成功") + except ValueError as e: + return error_response(str(e)) + except Exception as e: + return error_response(str(e)) + + +# ---------- 物料变动记录 ---------- + + +@router.get("/qc-material-usages") +async def list_qc_material_usages( + page: int = Query(1, ge=1), + pageSize: int = Query(10, ge=1, le=100), + materialId: Optional[str] = None, + changeType: Optional[str] = None, + startTime: Optional[str] = None, + endTime: Optional[str] = None, + db: Session = Depends(get_db), + user: dict = Depends(require_menu_code("biz:qcMaterialUsage:list")), +): + try: + tenant_no = _get_tenant_no(db, user) + items, total = mu_svc.list_usages( + db, tenant_no, page=page, page_size=pageSize, + material_id=materialId, change_type=changeType, + start_time=startTime, end_time=endTime, + ) + return success_response(_page_payload(items, total, page, pageSize)) + except PermissionError as e: + return error_response(str(e), code=403) + except Exception as e: + return error_response(str(e)) diff --git a/backend/app/crud/qc_material.py b/backend/app/crud/qc_material.py new file mode 100644 index 00000000..4a53f3ad --- /dev/null +++ b/backend/app/crud/qc_material.py @@ -0,0 +1,313 @@ +"""物料库 CRUD""" +from __future__ import annotations + +from datetime import datetime +from decimal import Decimal +from typing import Any, Dict, List, Optional, Tuple + +from sqlalchemy import and_, func, or_ +from sqlalchemy.orm import Session + +from app.core.utils import generate_uuid +from app.models.models import BizQcMaterial, BizQcMaterialUsage + + +def _dec(v: Any) -> float: + if v is None: + return 0.0 + return float(v) + + +def _fmt_dt(t: Optional[datetime]) -> str: + return t.strftime("%Y-%m-%d %H:%M:%S") if t else "" + + +def _row_to_dict(r: BizQcMaterial) -> Dict[str, Any]: + return { + "id": r.id, + "materialCode": r.material_code, + "materialName": r.material_name, + "specModel": r.spec_model or "", + "unit": r.unit or "", + "stockQty": _dec(r.stock_qty), + "warningQty": _dec(r.warning_qty), + "status": r.status or "1", + "orderNum": r.order_num if r.order_num is not None else 0, + "remark": r.remark or "", + "createTime": _fmt_dt(r.create_time), + "updateTime": _fmt_dt(r.update_time), + } + + +def code_exists( + db: Session, code: str, tenant_no: str, exclude_id: Optional[str] = None +) -> bool: + c = (code or "").strip() + if not c: + return False + q = db.query(BizQcMaterial).filter( + BizQcMaterial.material_code == c, + BizQcMaterial.tenant_no == tenant_no, + BizQcMaterial.del_flag == 0, + ) + if exclude_id: + q = q.filter(BizQcMaterial.id != exclude_id) + return q.first() is not None + + +def total_pages(db: Session, tenant_no: str, page_size: int = 10) -> int: + cnt = ( + db.query(func.count(BizQcMaterial.id)) + .filter( + BizQcMaterial.tenant_no == tenant_no, + BizQcMaterial.del_flag == 0, + ) + .scalar() + or 0 + ) + ps = max(1, page_size) + return (cnt + ps - 1) // ps + + +def list_materials( + db: Session, + *, + page: int = 1, + page_size: int = 10, + keyword: Optional[str] = None, + status: Optional[str] = None, + tenant_no: Optional[str] = None, +) -> Tuple[List[Dict[str, Any]], int]: + def _filtered(): + q0 = db.query(BizQcMaterial).filter(BizQcMaterial.del_flag == 0) + if tenant_no: + q0 = q0.filter(BizQcMaterial.tenant_no == tenant_no) + kw = (keyword or "").strip() + if kw: + like = f"%{kw}%" + q0 = q0.filter( + or_( + BizQcMaterial.material_code.like(like), + BizQcMaterial.material_name.like(like), + ) + ) + st = (status or "").strip() + if st in ("0", "1"): + q0 = q0.filter(BizQcMaterial.status == st) + return q0 + + total = _filtered().order_by(None).with_entities(func.count(BizQcMaterial.id)).scalar() or 0 + page = max(1, page) + page_size = min(max(1, page_size), 100) + rows = ( + _filtered() + .order_by(BizQcMaterial.create_time.desc()) + .offset((page - 1) * page_size) + .limit(page_size) + .all() + ) + return [_row_to_dict(x) for x in rows], int(total) + + +def get_material( + db: Session, row_id: str, tenant_no: Optional[str] = None +) -> Optional[BizQcMaterial]: + q = db.query(BizQcMaterial).filter( + BizQcMaterial.id == row_id, BizQcMaterial.del_flag == 0 + ) + if tenant_no: + q = q.filter(BizQcMaterial.tenant_no == tenant_no) + return q.first() + + +def create_material( + db: Session, + *, + material_code: str, + material_name: str, + spec_model: Optional[str] = None, + unit: Optional[str] = None, + stock_qty: Optional[Decimal] = None, + warning_qty: Optional[Decimal] = None, + status: str = "1", + tenant_no: str, + order_num: Optional[int] = None, + remark: Optional[str] = None, + user_id: Optional[str] = None, + create_dept_id: str = "", +) -> BizQcMaterial: + code = (material_code or "").strip() + if not code: + raise ValueError("物料编码不能为空") + if code_exists(db, code, tenant_no): + raise ValueError("物料编码已存在") + name = (material_name or "").strip() + if not name: + raise ValueError("物料名称不能为空") + st = (status or "1").strip() + if st not in ("0", "1"): + raise ValueError("状态无效") + obj = BizQcMaterial( + id=generate_uuid(), + material_code=code, + material_name=name, + spec_model=(spec_model or "").strip() or None, + unit=(unit or "").strip() or None, + stock_qty=stock_qty if stock_qty is not None else 0, + warning_qty=warning_qty if warning_qty is not None else 0, + status=st, + tenant_no=tenant_no, + order_num=order_num if order_num is not None else 0, + remark=(remark or "").strip() or None, + create_by=user_id, + create_dept_id=create_dept_id or "", + create_time=datetime.now(), + update_by=user_id, + update_time=datetime.now(), + del_flag=0, + ) + db.add(obj) + db.commit() + db.refresh(obj) + return obj + + +def update_material( + db: Session, + row_id: str, + *, + material_code: Optional[str] = None, + material_name: Optional[str] = None, + spec_model: Optional[str] = None, + unit: Optional[str] = None, + warning_qty: Optional[Decimal] = None, + status: Optional[str] = None, + order_num: Optional[int] = None, + remark: Optional[str] = None, + user_id: Optional[str] = None, + tenant_no: Optional[str] = None, +) -> BizQcMaterial: + obj = get_material(db, row_id, tenant_no=tenant_no) + if not obj: + raise ValueError("物料不存在") + if material_code is not None: + code = material_code.strip() + if not code: + raise ValueError("物料编码不能为空") + if code != obj.material_code and code_exists(db, code, obj.tenant_no, exclude_id=row_id): + raise ValueError("物料编码已存在") + obj.material_code = code + if material_name is not None: + name = (material_name or "").strip() + if not name: + raise ValueError("物料名称不能为空") + obj.material_name = name + if spec_model is not None: + obj.spec_model = (spec_model or "").strip() or None + if unit is not None: + obj.unit = (unit or "").strip() or None + if warning_qty is not None: + obj.warning_qty = warning_qty + if status is not None: + st = status.strip() + if st not in ("0", "1"): + raise ValueError("状态无效") + obj.status = st + if order_num is not None: + obj.order_num = order_num + if remark is not None: + obj.remark = (remark or "").strip() or None + obj.update_by = user_id + obj.update_time = datetime.now() + db.add(obj) + db.commit() + db.refresh(obj) + return obj + + +def soft_delete_material(db: Session, row_id: str, tenant_no: Optional[str] = None) -> None: + obj = get_material(db, row_id, tenant_no=tenant_no) + if not obj: + raise ValueError("物料不存在") + if obj.stock_qty is not None and _dec(obj.stock_qty) > 0: + raise ValueError("该物料仍有库存,无法删除") + obj.del_flag = 1 + obj.update_time = datetime.now() + db.add(obj) + db.commit() + + +def stock_in( + db: Session, + row_id: str, + *, + qty: Decimal, + remark: Optional[str] = None, + user_id: Optional[str] = None, + tenant_no: Optional[str] = None, +) -> BizQcMaterial: + if qty is None or float(qty) <= 0: + raise ValueError("入库数量必须大于0") + # 行锁锁定物料行 + obj = ( + db.query(BizQcMaterial) + .filter(BizQcMaterial.id == row_id, BizQcMaterial.del_flag == 0) + .with_for_update() + .first() + ) + if not obj: + raise ValueError("物料不存在") + old_stock = obj.stock_qty or 0 + new_stock = old_stock + qty + obj.stock_qty = new_stock + obj.update_by = user_id + obj.update_time = datetime.now() + db.add(obj) + # 写入使用记录 + usage = BizQcMaterialUsage( + id=generate_uuid(), + material_id=obj.id, + material_code=obj.material_code, + material_name=obj.material_name, + change_type="in", + change_qty=qty, + stock_after=new_stock, + source_type="manual_in", + tenant_no=obj.tenant_no, + remark=(remark or "").strip() or None, + create_by=user_id, + create_dept_id=obj.create_dept_id or "", + create_time=datetime.now(), + update_by=user_id, + update_time=datetime.now(), + del_flag=0, + ) + db.add(usage) + db.commit() + db.refresh(obj) + return obj + + +def options_list(db: Session, tenant_no: str) -> List[Dict[str, Any]]: + rows = ( + db.query(BizQcMaterial) + .filter( + BizQcMaterial.tenant_no == tenant_no, + BizQcMaterial.del_flag == 0, + BizQcMaterial.status == "1", + ) + .order_by(BizQcMaterial.create_time.desc()) + .all() + ) + return [ + { + "id": r.id, + "label": r.material_name, + "materialCode": r.material_code, + "materialName": r.material_name, + "specModel": r.spec_model or "", + "unit": r.unit or "", + "stockQty": _dec(r.stock_qty), + } + for r in rows + ] diff --git a/backend/app/crud/qc_material_usage.py b/backend/app/crud/qc_material_usage.py new file mode 100644 index 00000000..6bddaef0 --- /dev/null +++ b/backend/app/crud/qc_material_usage.py @@ -0,0 +1,140 @@ +"""物料变动记录 CRUD""" +from __future__ import annotations + +from datetime import datetime +from decimal import Decimal +from typing import Any, Dict, List, Optional, Tuple + +from fastapi import HTTPException +from sqlalchemy import func +from sqlalchemy.orm import Session + +from app.core.utils import generate_uuid +from app.models.qc_material import BizQcMaterial, BizQcMaterialUsage + + +def _row_to_dict(r: BizQcMaterialUsage) -> Dict[str, Any]: + return { + "id": r.id, + "materialId": r.material_id, + "materialCode": r.material_code or "", + "materialName": r.material_name or "", + "changeType": r.change_type, + "changeQty": float(r.change_qty) if r.change_qty is not None else 0, + "stockAfter": float(r.stock_after) if r.stock_after is not None else 0, + "qcTaskId": r.qc_task_id or "", + "qcTaskNo": r.qc_task_no or "", + "sourceType": r.source_type, + "remark": r.remark or "", + "createBy": r.create_by or "", + "createTime": r.create_time.strftime("%Y-%m-%d %H:%M:%S") if r.create_time else "", + "updateTime": r.update_time.strftime("%Y-%m-%d %H:%M:%S") if r.update_time else "", + } + + +def list_usages( + db: Session, + tenant_no: str, + *, + page: int = 1, + page_size: int = 10, + material_id: Optional[str] = None, + change_type: Optional[str] = None, + start_time: Optional[str] = None, + end_time: Optional[str] = None, +) -> Tuple[List[Dict[str, Any]], int]: + def _filtered(): + q0 = db.query(BizQcMaterialUsage).filter( + BizQcMaterialUsage.tenant_no == tenant_no, + BizQcMaterialUsage.del_flag == 0, + ) + if material_id: + q0 = q0.filter(BizQcMaterialUsage.material_id == material_id) + if change_type: + q0 = q0.filter(BizQcMaterialUsage.change_type == change_type) + if start_time: + q0 = q0.filter(BizQcMaterialUsage.create_time >= start_time) + if end_time: + q0 = q0.filter(BizQcMaterialUsage.create_time <= end_time + " 23:59:59") + return q0 + + total = _filtered().order_by(None).with_entities(func.count(BizQcMaterialUsage.id)).scalar() or 0 + page = max(1, page) + page_size = min(max(1, page_size), 100) + rows = ( + _filtered() + .order_by(BizQcMaterialUsage.create_time.desc()) + .offset((page - 1) * page_size) + .limit(page_size) + .all() + ) + return [_row_to_dict(x) for x in rows], int(total) + + +def deduct_for_task( + db: Session, + task_id: str, + task_no: str, + material_usages: List[Dict[str, Any]], + tenant_no: str, + user_id: Optional[str] = None, + dept_id: str = "", +) -> None: + """扣减物料库存并生成变动记录。调用方控制事务,本函数不 commit。""" + for item in material_usages: + mid = item.get("materialId") or "" + qty = item.get("qty") + if not mid: + raise HTTPException(status_code=400, detail="物料ID不能为空") + if qty is None or Decimal(str(qty)) <= 0: + raise HTTPException(status_code=400, detail="物料使用数量必须大于0") + + qty_dec = Decimal(str(qty)) + + material = ( + db.query(BizQcMaterial) + .with_for_update() + .filter( + BizQcMaterial.id == mid, + BizQcMaterial.tenant_no == tenant_no, + BizQcMaterial.del_flag == 0, + BizQcMaterial.status == "1", + ) + .first() + ) + if not material: + raise HTTPException(status_code=400, detail=f"物料不存在或已停用(ID: {mid})") + + stock_qty = material.stock_qty if material.stock_qty is not None else Decimal("0") + if stock_qty < qty_dec: + raise HTTPException( + status_code=400, + detail=f"物料「{material.material_name}」库存不足,当前库存 {stock_qty},需使用 {qty_dec}", + ) + + new_stock = stock_qty - qty_dec + material.stock_qty = new_stock + material.update_by = user_id + material.update_time = datetime.now() + + usage = BizQcMaterialUsage( + id=generate_uuid(), + material_id=mid, + material_code=material.material_code, + material_name=material.material_name, + change_type="use", + change_qty=-qty_dec, + stock_after=new_stock, + qc_task_id=task_id, + qc_task_no=task_no, + source_type="task_complete", + tenant_no=tenant_no, + remark="", + create_by=user_id, + create_dept_id=dept_id, + create_time=datetime.now(), + update_by=user_id, + update_time=datetime.now(), + del_flag=0, + ) + db.add(usage) diff --git a/web-admin/src/api/qc-material-usage.ts b/web-admin/src/api/qc-material-usage.ts new file mode 100644 index 00000000..01367e8a --- /dev/null +++ b/web-admin/src/api/qc-material-usage.ts @@ -0,0 +1,39 @@ +import request from '@/utils/http' + +type PageList = { + list: T[] + total: number + page: number + pageSize: number + totalPages: number +} + +export type QcMaterialUsage = { + id: string + materialId: string + materialCode: string + materialName: string + changeType: string + changeQty: number + stockAfter: number + qcTaskId: string + qcTaskNo: string + sourceType: string + remark: string + createBy: string + createTime: string +} + +export function fetchQcMaterialUsageList(params?: { + page?: number + pageSize?: number + materialId?: string + changeType?: string + startTime?: string + endTime?: string +}) { + return request.get>({ + url: '/web/admin/qc-material-usages', + params + }) +} diff --git a/web-admin/src/api/qc-material.ts b/web-admin/src/api/qc-material.ts new file mode 100644 index 00000000..40833336 --- /dev/null +++ b/web-admin/src/api/qc-material.ts @@ -0,0 +1,81 @@ +import request from '@/utils/http' + +type PageList = { + list: T[] + total: number + page: number + pageSize: number + totalPages: number +} + +export type QcMaterial = { + id: string + materialCode: string + materialName: string + specModel: string + unit: string + stockQty: number + warningQty: number + status: string + tenantNo: string + orderNum: number + remark: string + createBy: string + createTime: string + updateTime: string +} + +export type QcMaterialOption = { + id: string + label: string + materialCode: string + materialName: string + specModel: string + unit: string + stockQty: number +} + +export function fetchQcMaterialList(params?: { + page?: number + pageSize?: number + keyword?: string + status?: string +}) { + return request.get>({ + url: '/web/admin/qc-materials', + params + }) +} + +export function fetchQcMaterialOptions() { + return request.get({ + url: '/web/admin/qc-materials/options' + }) +} + +export function fetchCreateQcMaterial(data: Record) { + return request.post({ + url: '/web/admin/qc-materials', + data + }) +} + +export function fetchUpdateQcMaterial(id: string, data: Record) { + return request.put({ + url: `/web/admin/qc-materials/${id}`, + data + }) +} + +export function fetchDeleteQcMaterial(id: string) { + return request.delete({ + url: `/web/admin/qc-materials/${id}` + }) +} + +export function fetchStockInQcMaterial(id: string, data: { qty: number; remark?: string }) { + return request.post({ + url: `/web/admin/qc-materials/${id}/stock-in`, + data + }) +} diff --git a/web-admin/src/router/modules/sqxp.ts b/web-admin/src/router/modules/sqxp.ts index b389602a..6bd2488f 100644 --- a/web-admin/src/router/modules/sqxp.ts +++ b/web-admin/src/router/modules/sqxp.ts @@ -191,6 +191,13 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [ path: 'sample-code', component: '/basic-info/sample-code/index', meta: { title: '样本编码管理', keepAlive: true } + }, + { + id: '40000000000000000000000000000007', + name: 'BasicInfoInsuranceCompany', + path: 'insurance-company', + component: '/basic-info/insurance-company/index', + meta: { title: '保险公司及项目价格', keepAlive: true } } ] }, @@ -240,6 +247,20 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [ path: 'refund-confirm', component: '/cell-storage/refund-confirm/index', meta: { title: '退费确认(财务)', keepAlive: true } + }, + { + id: '50000000000000000000000000000006', + name: 'CellStorageStorageCert', + path: 'storage-cert', + component: '/cell-storage/storage-cert/index', + meta: { title: '储存证管理', keepAlive: true } + }, + { + id: '50000000000000000000000000000008', + name: 'CellStorageFullmoonTodo', + path: 'fullmoon-todo', + component: '/cell-storage/fullmoon-todo/index', + meta: { title: '满月待办管理', keepAlive: true } } ] }, @@ -344,6 +365,20 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [ keepAlive: true, menuCode: 'biz:qcTask:list' } + }, + { + id: '70000000000000000000000000000009', + name: 'QualityInspectionQcMaterial', + path: 'qc-material', + component: '/quality-inspection/qc-material/index', + meta: { title: '质检物料库', keepAlive: true, menuCode: 'biz:qcMaterial:list' } + }, + { + id: '70000000000000000000000000000010', + name: 'QualityInspectionQcMaterialUsage', + path: 'qc-material-usage', + component: '/quality-inspection/qc-material-usage/index', + meta: { title: '物料变动记录', keepAlive: true, menuCode: 'biz:qcMaterialUsage:list' } } ] }, diff --git a/web-admin/src/views/quality-inspection/qc-material-usage/index.vue b/web-admin/src/views/quality-inspection/qc-material-usage/index.vue new file mode 100644 index 00000000..2a6fb6c0 --- /dev/null +++ b/web-admin/src/views/quality-inspection/qc-material-usage/index.vue @@ -0,0 +1,259 @@ + + + + + + diff --git a/web-admin/src/views/quality-inspection/qc-material/index.vue b/web-admin/src/views/quality-inspection/qc-material/index.vue new file mode 100644 index 00000000..43145927 --- /dev/null +++ b/web-admin/src/views/quality-inspection/qc-material/index.vue @@ -0,0 +1,472 @@ + + + + + + + diff --git a/web-admin/src/views/quality-inspection/qc-task/index.vue b/web-admin/src/views/quality-inspection/qc-task/index.vue index 0a421dd0..de033f30 100644 --- a/web-admin/src/views/quality-inspection/qc-task/index.vue +++ b/web-admin/src/views/quality-inspection/qc-task/index.vue @@ -9,7 +9,7 @@
-

任务列表

+

任务列表

@@ -178,6 +178,43 @@
+
+
物料使用(选填)
+
+ + + + + + + + + + + +
+ + 添加物料 + +
检测结论
@@ -186,21 +223,18 @@ 不通过 -
-
-
备注
-
-
- +
+ +