提交
This commit is contained in:
1 parent
19de2040d0
commit
3779de12ce
78 files changed
+23068
-13824
No files matched your search
+1
-1
@@ -1 +1 @@
|
||||
90748
|
||||
81964
|
||||
@@ -15,6 +15,7 @@ from app.api.production import router as production_router
|
||||
from app.api.pilot_service import router as pilot_service_router
|
||||
from app.api.insurance_company import router as insurance_company_router
|
||||
from app.api.material import router as material_router
|
||||
from app.api.storage_release_review import router as storage_release_review_router
|
||||
|
||||
__all__ = [
|
||||
"public_router",
|
||||
@@ -33,4 +34,5 @@ __all__ = [
|
||||
"pilot_service_router",
|
||||
"insurance_company_router",
|
||||
"material_router",
|
||||
"storage_release_review_router",
|
||||
]
|
||||
@@ -0,0 +1,190 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.menu_guard import require_admin_or_super_admin, require_menu_code
|
||||
from app.core.response import error_response, success_response
|
||||
from app.crud import storage_release_review as srr_svc
|
||||
from app.models.models import SysUser
|
||||
from app.schemas.storage_release_review import (
|
||||
BatchReviewBody,
|
||||
BatchStoreBody,
|
||||
ReviewSubmitBody,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/web/admin", tags=["入库放行审核"])
|
||||
|
||||
|
||||
def _get_tenant_no(db: Session, user: dict) -> str:
|
||||
user_id = user.get("user_id")
|
||||
if not user_id:
|
||||
raise PermissionError("用户身份信息缺失,无法确定租户")
|
||||
sys_user = db.query(SysUser).filter(SysUser.id == user_id).first()
|
||||
if not sys_user or not sys_user.tenant_no:
|
||||
raise PermissionError("用户租户信息缺失")
|
||||
return sys_user.tenant_no
|
||||
|
||||
|
||||
def _get_current_user_id(user: dict) -> str:
|
||||
user_id = user.get("user_id")
|
||||
if not user_id:
|
||||
raise PermissionError("用户身份信息缺失")
|
||||
return str(user_id)
|
||||
|
||||
|
||||
@router.get("/storage-release-reviews")
|
||||
async def list_storage_release_reviews(
|
||||
page: int = Query(1, ge=1),
|
||||
pageSize: int = Query(10, ge=1, le=100),
|
||||
keyword: Optional[str] = None,
|
||||
receptionSource: Optional[str] = None,
|
||||
reviewConclusion: Optional[str] = None,
|
||||
storageStatus: Optional[str] = None,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_menu_code("biz:storageReleaseReview:list")),
|
||||
):
|
||||
try:
|
||||
tenant_no = _get_tenant_no(db, user)
|
||||
result = srr_svc.list_reviews(
|
||||
db,
|
||||
tenant_no=tenant_no,
|
||||
page=page,
|
||||
page_size=pageSize,
|
||||
reception_source=receptionSource,
|
||||
review_conclusion=reviewConclusion,
|
||||
storage_status=storageStatus,
|
||||
keyword=keyword,
|
||||
user_info=user,
|
||||
)
|
||||
return success_response(result)
|
||||
except PermissionError as e:
|
||||
return error_response(str(e), code=403)
|
||||
except Exception as e:
|
||||
return error_response(str(e))
|
||||
|
||||
|
||||
@router.get("/storage-release-reviews/{review_id}")
|
||||
async def get_storage_release_review_detail(
|
||||
review_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_menu_code("biz:storageReleaseReview:list")),
|
||||
):
|
||||
try:
|
||||
tenant_no = _get_tenant_no(db, user)
|
||||
detail = srr_svc.get_review_detail(db, review_id, tenant_no)
|
||||
if not detail:
|
||||
return error_response("审核记录不存在", code=404)
|
||||
return success_response(detail)
|
||||
except PermissionError as e:
|
||||
return error_response(str(e), code=403)
|
||||
except Exception as e:
|
||||
return error_response(str(e))
|
||||
|
||||
|
||||
@router.put("/storage-release-reviews/{review_id}/review")
|
||||
async def submit_single_review(
|
||||
review_id: str,
|
||||
body: ReviewSubmitBody,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
try:
|
||||
tenant_no = _get_tenant_no(db, user)
|
||||
user_id = _get_current_user_id(user)
|
||||
rev = srr_svc.submit_review(
|
||||
db,
|
||||
review_id,
|
||||
review_conclusion=body.reviewConclusion,
|
||||
review_remark=body.reviewRemark,
|
||||
review_attachments=body.reviewAttachments,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
)
|
||||
db.commit()
|
||||
return success_response(srr_svc._row_to_dict(rev))
|
||||
except PermissionError as e:
|
||||
return error_response(str(e), code=403)
|
||||
except ValueError as e:
|
||||
return error_response(str(e))
|
||||
except Exception as e:
|
||||
return error_response(str(e))
|
||||
|
||||
|
||||
@router.put("/storage-release-reviews/batch-review")
|
||||
async def batch_review(
|
||||
body: BatchReviewBody,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
try:
|
||||
tenant_no = _get_tenant_no(db, user)
|
||||
user_id = _get_current_user_id(user)
|
||||
result = srr_svc.batch_submit_review(
|
||||
db,
|
||||
body.ids,
|
||||
review_conclusion=body.reviewConclusion,
|
||||
review_remark=body.reviewRemark,
|
||||
review_attachments=body.reviewAttachments,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
)
|
||||
db.commit()
|
||||
return success_response(result)
|
||||
except PermissionError as e:
|
||||
return error_response(str(e), code=403)
|
||||
except ValueError as e:
|
||||
return error_response(str(e))
|
||||
except Exception as e:
|
||||
return error_response(str(e))
|
||||
|
||||
|
||||
@router.put("/storage-release-reviews/{review_id}/store")
|
||||
async def store_single_sample(
|
||||
review_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
try:
|
||||
tenant_no = _get_tenant_no(db, user)
|
||||
user_id = _get_current_user_id(user)
|
||||
rev = srr_svc.store_sample(
|
||||
db,
|
||||
review_id,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
)
|
||||
db.commit()
|
||||
return success_response(srr_svc._row_to_dict(rev))
|
||||
except PermissionError as e:
|
||||
return error_response(str(e), code=403)
|
||||
except ValueError as e:
|
||||
return error_response(str(e))
|
||||
except Exception as e:
|
||||
return error_response(str(e))
|
||||
|
||||
|
||||
@router.put("/storage-release-reviews/batch-store")
|
||||
async def batch_store_samples(
|
||||
body: BatchStoreBody,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
try:
|
||||
tenant_no = _get_tenant_no(db, user)
|
||||
user_id = _get_current_user_id(user)
|
||||
result = srr_svc.batch_store_samples(
|
||||
db,
|
||||
body.ids,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
)
|
||||
db.commit()
|
||||
return success_response(result)
|
||||
except PermissionError as e:
|
||||
return error_response(str(e), code=403)
|
||||
except ValueError as e:
|
||||
return error_response(str(e))
|
||||
except Exception as e:
|
||||
return error_response(str(e))
|
||||
@@ -8,7 +8,7 @@ from typing import Any, Dict, List, Optional, Tuple
|
||||
from sqlalchemy import and_, func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
from app.core.utils import generate_uuid, parse_datetime
|
||||
from app.models.models import (
|
||||
BizCellProduct,
|
||||
BizCellSampleType,
|
||||
@@ -970,14 +970,12 @@ def set_task_inputs(
|
||||
|
||||
|
||||
def _parse_optional_date(val):
|
||||
"""解析可选日期字符串"""
|
||||
"""解析可选日期字符串(兼容 YYYY-MM-DD 与 ISO 8601)"""
|
||||
if not val:
|
||||
return None
|
||||
from dateutil.parser import parse as _parse_dt
|
||||
try:
|
||||
return _parse_dt(str(val))
|
||||
except Exception:
|
||||
return None
|
||||
if isinstance(val, datetime):
|
||||
return val
|
||||
return parse_datetime(str(val).strip())
|
||||
|
||||
|
||||
def _create_occupancy_for_product(db: Session, product: BizCellProduct, tenant_no: str, user_id: Optional[str]):
|
||||
@@ -1089,7 +1087,7 @@ def set_task_outputs(
|
||||
product.update_time = now
|
||||
db.add(product)
|
||||
if product.storage_slot_id:
|
||||
occupancy_svc.remove_occupancy_by_item(db, "cell_product", cp_id, user_id=user_id)
|
||||
occupancy_svc.remove_by_item(db, "cell_product", cp_id, user_id=user_id)
|
||||
old_row.del_flag = 1
|
||||
old_row.update_by = user_id
|
||||
old_row.update_time = now
|
||||
@@ -1111,7 +1109,7 @@ def set_task_outputs(
|
||||
db.add(product)
|
||||
if old_slot_id != new_slot_id:
|
||||
if old_slot_id:
|
||||
occupancy_svc.remove_occupancy_by_item(db, "cell_product", cp_id, user_id=user_id)
|
||||
occupancy_svc.remove_by_item(db, "cell_product", cp_id, user_id=user_id)
|
||||
if new_slot_id:
|
||||
_create_occupancy_for_product(db, product, tenant_no, user_id)
|
||||
old_row = old_cp_map[cp_id]
|
||||
|
||||
@@ -800,6 +800,64 @@ def create_sample_reception(
|
||||
user_id=user_id,
|
||||
create_dept_id=create_dept_id or "",
|
||||
)
|
||||
from app.crud import storage_release_review as srr_svc
|
||||
from app.models.models import BizCollectionProtocol, BizProtocolSample
|
||||
from app.models.research_donation import BizResearchDonation, BizResearchDonationSample
|
||||
|
||||
_srr_protocol_no = ""
|
||||
_srr_sample_name = ""
|
||||
_src = getattr(obj, "reception_source", "cell_storage") or "cell_storage"
|
||||
if _src == "research_donation":
|
||||
_d = (
|
||||
db.query(BizResearchDonation)
|
||||
.filter(
|
||||
BizResearchDonation.id == (obj.research_donation_id or ""),
|
||||
BizResearchDonation.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if _d:
|
||||
_srr_protocol_no = _d.donation_no or ""
|
||||
_ds = (
|
||||
db.query(BizResearchDonationSample)
|
||||
.filter(
|
||||
BizResearchDonationSample.id == (obj.donation_sample_id or ""),
|
||||
BizResearchDonationSample.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if _ds:
|
||||
_srr_sample_name = _ds.sample_code or ""
|
||||
else:
|
||||
_p = (
|
||||
db.query(BizCollectionProtocol)
|
||||
.filter(
|
||||
BizCollectionProtocol.id == obj.collection_protocol_id,
|
||||
BizCollectionProtocol.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if _p:
|
||||
_srr_protocol_no = _p.protocol_no or ""
|
||||
_s = (
|
||||
db.query(BizProtocolSample)
|
||||
.filter(
|
||||
BizProtocolSample.id == obj.protocol_sample_id,
|
||||
BizProtocolSample.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if _s:
|
||||
_srr_sample_name = _s.sample_code or ""
|
||||
srr_svc.create_review_from_reception(
|
||||
db,
|
||||
reception=obj,
|
||||
protocol_no=_srr_protocol_no,
|
||||
sample_name=_srr_sample_name,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
create_dept_id=create_dept_id or "",
|
||||
)
|
||||
db.commit()
|
||||
db.refresh(obj)
|
||||
return obj
|
||||
|
||||
@@ -0,0 +1,633 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from sqlalchemy import func, or_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
from app.models.models import (
|
||||
BizCollectionProtocol,
|
||||
BizProductionTask,
|
||||
BizProductionWorkOrder,
|
||||
BizProtocolPartyCustomer,
|
||||
BizProtocolSample,
|
||||
BizProtocolSampleTransport,
|
||||
BizQcTask,
|
||||
BizQcWorkOrder,
|
||||
BizSampleReception,
|
||||
BizStorageReleaseReview,
|
||||
SysUser,
|
||||
)
|
||||
from app.models.research_donation import (
|
||||
BizResearchDonation,
|
||||
BizResearchDonationParty,
|
||||
BizResearchDonationSample,
|
||||
BizResearchDonationSampleTransport,
|
||||
)
|
||||
|
||||
_RECEPTION_SOURCE_LABELS = {
|
||||
"cell_storage": "细胞存储",
|
||||
"research_donation": "科研捐赠",
|
||||
}
|
||||
_REVIEW_CONCLUSION_LABELS = {"tg": "通过", "btg": "不通过"}
|
||||
_STORAGE_STATUS_LABELS = {"drk": "待入库", "yrk": "已入库"}
|
||||
|
||||
|
||||
def _fmt_dt(t: Optional[datetime]) -> str:
|
||||
return t.strftime("%Y-%m-%d %H:%M:%S") if t else ""
|
||||
|
||||
|
||||
def total_pages(total: int, page_size: int) -> int:
|
||||
if total <= 0:
|
||||
return 0
|
||||
return int(math.ceil(total / float(page_size)))
|
||||
|
||||
|
||||
def _row_to_dict(r: BizStorageReleaseReview) -> Dict[str, Any]:
|
||||
return {
|
||||
"id": r.id,
|
||||
"sampleCode": r.sample_code or "",
|
||||
"receptionSource": r.reception_source or "",
|
||||
"receptionSourceLabel": _RECEPTION_SOURCE_LABELS.get(r.reception_source or "", r.reception_source or ""),
|
||||
"protocolId": r.protocol_id or "",
|
||||
"receptionId": r.reception_id or "",
|
||||
"protocolNo": r.protocol_no or "",
|
||||
"sampleName": r.sample_name or "",
|
||||
"reviewConclusion": r.review_conclusion or "",
|
||||
"reviewConclusionLabel": _REVIEW_CONCLUSION_LABELS.get(r.review_conclusion or "", r.review_conclusion or ""),
|
||||
"reviewRemark": r.review_remark or "",
|
||||
"reviewAttachments": r.review_attachments or "",
|
||||
"reviewBy": r.review_by or "",
|
||||
"reviewByName": r.review_by_name or "",
|
||||
"reviewTime": _fmt_dt(r.review_time),
|
||||
"storageStatus": r.storage_status or "drk",
|
||||
"storageStatusLabel": _STORAGE_STATUS_LABELS.get(r.storage_status or "drk", r.storage_status or "drk"),
|
||||
"storageBy": r.storage_by or "",
|
||||
"storageByName": r.storage_by_name or "",
|
||||
"storageTime": _fmt_dt(r.storage_time),
|
||||
"remark": r.remark or "",
|
||||
"createTime": _fmt_dt(r.create_time),
|
||||
"updateTime": _fmt_dt(r.update_time),
|
||||
}
|
||||
|
||||
|
||||
def create_review_from_reception(
|
||||
db: Session,
|
||||
*,
|
||||
reception: BizSampleReception,
|
||||
protocol_no: str,
|
||||
sample_name: str,
|
||||
tenant_no: str,
|
||||
user_id: Optional[str] = None,
|
||||
create_dept_id: str = "",
|
||||
) -> BizStorageReleaseReview:
|
||||
source = getattr(reception, "reception_source", "cell_storage") or "cell_storage"
|
||||
if source == "research_donation":
|
||||
protocol_id = reception.research_donation_id or ""
|
||||
else:
|
||||
protocol_id = reception.collection_protocol_id or ""
|
||||
|
||||
obj = BizStorageReleaseReview(
|
||||
id=generate_uuid(),
|
||||
sample_code=reception.sample_code or "",
|
||||
reception_source=source,
|
||||
protocol_id=protocol_id,
|
||||
reception_id=reception.id,
|
||||
protocol_no=protocol_no or None,
|
||||
sample_name=sample_name or None,
|
||||
storage_status="drk",
|
||||
tenant_no=tenant_no,
|
||||
create_by=user_id,
|
||||
create_dept_id=create_dept_id or "",
|
||||
update_by=user_id,
|
||||
create_time=datetime.now(),
|
||||
update_time=datetime.now(),
|
||||
del_flag=0,
|
||||
)
|
||||
db.add(obj)
|
||||
db.flush()
|
||||
return obj
|
||||
|
||||
|
||||
def list_reviews(
|
||||
db: Session,
|
||||
*,
|
||||
tenant_no: str,
|
||||
page: int = 1,
|
||||
page_size: int = 10,
|
||||
reception_source: Optional[str] = None,
|
||||
review_conclusion: Optional[str] = None,
|
||||
storage_status: Optional[str] = None,
|
||||
keyword: Optional[str] = None,
|
||||
user_info=None,
|
||||
) -> Dict[str, Any]:
|
||||
q0 = db.query(BizStorageReleaseReview).filter(
|
||||
BizStorageReleaseReview.tenant_no == tenant_no,
|
||||
BizStorageReleaseReview.del_flag == 0,
|
||||
)
|
||||
rs = (reception_source or "").strip()
|
||||
if rs:
|
||||
q0 = q0.filter(BizStorageReleaseReview.reception_source == rs)
|
||||
rc = (review_conclusion or "").strip()
|
||||
if rc:
|
||||
q0 = q0.filter(BizStorageReleaseReview.review_conclusion == rc)
|
||||
ss = (storage_status or "").strip()
|
||||
if ss:
|
||||
q0 = q0.filter(BizStorageReleaseReview.storage_status == ss)
|
||||
kw = (keyword or "").strip()
|
||||
if kw:
|
||||
like = f"%{kw}%"
|
||||
q0 = q0.filter(
|
||||
or_(
|
||||
BizStorageReleaseReview.protocol_no.like(like),
|
||||
BizStorageReleaseReview.sample_code.like(like),
|
||||
BizStorageReleaseReview.sample_name.like(like),
|
||||
)
|
||||
)
|
||||
if user_info:
|
||||
from app.core.data_scope import apply_data_scope
|
||||
q0 = apply_data_scope(q0, BizStorageReleaseReview, user_info, db, tenant_no)
|
||||
total = q0.order_by(None).with_entities(func.count(BizStorageReleaseReview.id)).scalar() or 0
|
||||
page = max(1, page)
|
||||
page_size = min(max(1, page_size), 100)
|
||||
rows = (
|
||||
q0.order_by(BizStorageReleaseReview.create_time.desc())
|
||||
.offset((page - 1) * page_size)
|
||||
.limit(page_size)
|
||||
.all()
|
||||
)
|
||||
items = [_row_to_dict(r) for r in rows]
|
||||
return {
|
||||
"list": items,
|
||||
"total": int(total),
|
||||
"page": page,
|
||||
"pageSize": page_size,
|
||||
"totalPages": total_pages(int(total), page_size),
|
||||
}
|
||||
|
||||
|
||||
def _get_user_name(db: Session, user_id: str) -> str:
|
||||
u = db.query(SysUser).filter(SysUser.id == user_id).first()
|
||||
return (u.nick_name or "") if u else ""
|
||||
|
||||
|
||||
def get_review_detail(db: Session, review_id: str, tenant_no: str) -> Optional[Dict[str, Any]]:
|
||||
rev = (
|
||||
db.query(BizStorageReleaseReview)
|
||||
.filter(
|
||||
BizStorageReleaseReview.id == review_id,
|
||||
BizStorageReleaseReview.tenant_no == tenant_no,
|
||||
BizStorageReleaseReview.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not rev:
|
||||
return None
|
||||
|
||||
result: Dict[str, Any] = {"review": _row_to_dict(rev)}
|
||||
source = rev.reception_source or "cell_storage"
|
||||
|
||||
if source == "cell_storage":
|
||||
protocol_info = _build_cell_storage_protocol_info(db, rev)
|
||||
result["protocolInfo"] = protocol_info
|
||||
transport_info = _build_cell_storage_transport_info(db, rev)
|
||||
result["transportInfo"] = transport_info
|
||||
else:
|
||||
protocol_info = _build_research_donation_protocol_info(db, rev)
|
||||
result["protocolInfo"] = protocol_info
|
||||
transport_info = _build_research_donation_transport_info(db, rev)
|
||||
result["transportInfo"] = transport_info
|
||||
|
||||
reception_info = _build_reception_info(db, rev)
|
||||
result["receptionInfo"] = reception_info
|
||||
|
||||
qc_info = _build_qc_info(db, rev, tenant_no)
|
||||
result["qcInfo"] = qc_info
|
||||
|
||||
production_info = _build_production_info(db, rev, tenant_no)
|
||||
result["productionInfo"] = production_info
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _build_cell_storage_protocol_info(db: Session, rev: BizStorageReleaseReview) -> Dict[str, Any]:
|
||||
proto = (
|
||||
db.query(BizCollectionProtocol)
|
||||
.filter(
|
||||
BizCollectionProtocol.id == (rev.protocol_id or ""),
|
||||
BizCollectionProtocol.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not proto:
|
||||
return {}
|
||||
sample = (
|
||||
db.query(BizProtocolSample)
|
||||
.filter(
|
||||
BizProtocolSample.collection_protocol_id == proto.id,
|
||||
BizProtocolSample.sample_code == (rev.sample_code or ""),
|
||||
BizProtocolSample.tenant_no == rev.tenant_no,
|
||||
BizProtocolSample.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
party = (
|
||||
db.query(BizProtocolPartyCustomer)
|
||||
.filter(
|
||||
BizProtocolPartyCustomer.collection_protocol_id == proto.id,
|
||||
BizProtocolPartyCustomer.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
info: Dict[str, Any] = {
|
||||
"protocolNo": proto.protocol_no or "",
|
||||
"protocolStatus": getattr(proto, "protocol_status", "") or "",
|
||||
"motherName": "",
|
||||
"motherPhone": "",
|
||||
"fatherName": "",
|
||||
"fatherPhone": "",
|
||||
"newbornName": "",
|
||||
"sampleCode": rev.sample_code or "",
|
||||
"sampleTypeName": "",
|
||||
"collectAmount": None,
|
||||
"storageYears": None,
|
||||
}
|
||||
if sample:
|
||||
from app.crud.qc_work_order import _load_sample_type_name_map
|
||||
type_map = _load_sample_type_name_map(db, [sample.sample_type_id]) if sample.sample_type_id else {}
|
||||
info["sampleTypeName"] = type_map.get(sample.sample_type_id or "", "")
|
||||
info["collectAmount"] = float(sample.collect_amount) if sample.collect_amount else None
|
||||
info["storageYears"] = sample.storage_years
|
||||
if party:
|
||||
info["motherName"] = party.mother_name or ""
|
||||
info["motherPhone"] = party.mother_phone or ""
|
||||
info["fatherName"] = party.father_name or ""
|
||||
info["fatherPhone"] = party.father_phone or ""
|
||||
info["newbornName"] = party.newborn_name or ""
|
||||
return info
|
||||
|
||||
|
||||
def _build_research_donation_protocol_info(db: Session, rev: BizStorageReleaseReview) -> Dict[str, Any]:
|
||||
donation = (
|
||||
db.query(BizResearchDonation)
|
||||
.filter(
|
||||
BizResearchDonation.id == (rev.protocol_id or ""),
|
||||
BizResearchDonation.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not donation:
|
||||
return {}
|
||||
dsample = (
|
||||
db.query(BizResearchDonationSample)
|
||||
.filter(
|
||||
BizResearchDonationSample.research_donation_id == donation.id,
|
||||
BizResearchDonationSample.sample_code == (rev.sample_code or ""),
|
||||
BizResearchDonationSample.tenant_no == rev.tenant_no,
|
||||
BizResearchDonationSample.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
party = (
|
||||
db.query(BizResearchDonationParty)
|
||||
.filter(
|
||||
BizResearchDonationParty.research_donation_id == donation.id,
|
||||
BizResearchDonationParty.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
info: Dict[str, Any] = {
|
||||
"protocolNo": donation.donation_no or "",
|
||||
"protocolStatus": donation.donation_status or "",
|
||||
"motherName": "",
|
||||
"motherPhone": "",
|
||||
"fatherName": "",
|
||||
"fatherPhone": "",
|
||||
"newbornName": "",
|
||||
"sampleCode": rev.sample_code or "",
|
||||
"sampleTypeName": "",
|
||||
"collectAmount": None,
|
||||
"storageYears": None,
|
||||
}
|
||||
if dsample:
|
||||
from app.crud.qc_work_order import _load_sample_type_name_map
|
||||
type_map = _load_sample_type_name_map(db, [dsample.sample_type_id]) if dsample.sample_type_id else {}
|
||||
info["sampleTypeName"] = type_map.get(dsample.sample_type_id or "", "")
|
||||
info["collectAmount"] = float(dsample.collect_amount) if dsample.collect_amount else None
|
||||
info["storageYears"] = dsample.storage_years
|
||||
if party:
|
||||
info["motherName"] = party.mother_name or ""
|
||||
info["motherPhone"] = party.mother_phone or ""
|
||||
info["fatherName"] = party.father_name or ""
|
||||
info["fatherPhone"] = party.father_phone or ""
|
||||
info["newbornName"] = party.newborn_name or ""
|
||||
return info
|
||||
|
||||
|
||||
def _build_cell_storage_transport_info(db: Session, rev: BizStorageReleaseReview) -> Dict[str, Any]:
|
||||
sample = (
|
||||
db.query(BizProtocolSample)
|
||||
.filter(
|
||||
BizProtocolSample.sample_code == (rev.sample_code or ""),
|
||||
BizProtocolSample.collection_protocol_id == (rev.protocol_id or ""),
|
||||
BizProtocolSample.tenant_no == rev.tenant_no,
|
||||
BizProtocolSample.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not sample:
|
||||
return {}
|
||||
transport = (
|
||||
db.query(BizProtocolSampleTransport)
|
||||
.filter(
|
||||
BizProtocolSampleTransport.sample_id == sample.id,
|
||||
BizProtocolSampleTransport.collection_protocol_id == (rev.protocol_id or ""),
|
||||
BizProtocolSampleTransport.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not transport:
|
||||
return {}
|
||||
return {
|
||||
"transportType": transport.transport_type or "",
|
||||
"waybillNo": transport.waybill_no or "",
|
||||
"expressFee": float(transport.express_fee) if transport.express_fee else None,
|
||||
"etaTime": _fmt_dt(transport.eta_time),
|
||||
"carrierName": transport.carrier_name or "",
|
||||
"carrierPhone": transport.carrier_phone or "",
|
||||
}
|
||||
|
||||
|
||||
def _build_research_donation_transport_info(db: Session, rev: BizStorageReleaseReview) -> Dict[str, Any]:
|
||||
dsample = (
|
||||
db.query(BizResearchDonationSample)
|
||||
.filter(
|
||||
BizResearchDonationSample.sample_code == (rev.sample_code or ""),
|
||||
BizResearchDonationSample.research_donation_id == (rev.protocol_id or ""),
|
||||
BizResearchDonationSample.tenant_no == rev.tenant_no,
|
||||
BizResearchDonationSample.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not dsample:
|
||||
return {}
|
||||
transport = (
|
||||
db.query(BizResearchDonationSampleTransport)
|
||||
.filter(
|
||||
BizResearchDonationSampleTransport.sample_id == dsample.id,
|
||||
BizResearchDonationSampleTransport.research_donation_id == (rev.protocol_id or ""),
|
||||
BizResearchDonationSampleTransport.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not transport:
|
||||
return {}
|
||||
return {
|
||||
"transportType": transport.transport_type or "",
|
||||
"waybillNo": transport.waybill_no or "",
|
||||
"expressFee": float(transport.express_fee) if transport.express_fee else None,
|
||||
"etaTime": _fmt_dt(transport.eta_time),
|
||||
"carrierName": transport.carrier_name or "",
|
||||
"carrierPhone": transport.carrier_phone or "",
|
||||
}
|
||||
|
||||
|
||||
def _build_reception_info(db: Session, rev: BizStorageReleaseReview) -> Dict[str, Any]:
|
||||
rec = (
|
||||
db.query(BizSampleReception)
|
||||
.filter(
|
||||
BizSampleReception.id == (rev.reception_id or ""),
|
||||
BizSampleReception.tenant_no == rev.tenant_no,
|
||||
BizSampleReception.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not rec:
|
||||
return {}
|
||||
return {
|
||||
"receptionTime": _fmt_dt(rec.reception_time),
|
||||
"handlerName": rec.handler_name or "",
|
||||
"receiveTemperature": rec.receive_temperature or "",
|
||||
"volumeMl": float(rec.volume_ml) if rec.volume_ml else None,
|
||||
"netWeightG": float(rec.net_weight_g) if rec.net_weight_g else None,
|
||||
"acceptConclusion": rec.accept_conclusion or "",
|
||||
}
|
||||
|
||||
|
||||
def _build_qc_info(db: Session, rev: BizStorageReleaseReview, tenant_no: str) -> Dict[str, Any]:
|
||||
wo = (
|
||||
db.query(BizQcWorkOrder)
|
||||
.filter(
|
||||
BizQcWorkOrder.sample_code == (rev.sample_code or ""),
|
||||
BizQcWorkOrder.tenant_no == tenant_no,
|
||||
BizQcWorkOrder.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not wo:
|
||||
return {}
|
||||
tasks = (
|
||||
db.query(BizQcTask)
|
||||
.filter(
|
||||
BizQcTask.work_order_id == wo.id,
|
||||
BizQcTask.tenant_no == tenant_no,
|
||||
BizQcTask.del_flag == 0,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
task_list = []
|
||||
_task_type_labels = {"sysz": "实验室自检", "dsfjc": "第三方检测"}
|
||||
_task_status_labels = {"dzx": "待执行", "jxz": "进行中", "ywc": "已完成"}
|
||||
_conclusion_labels = {"tg": "通过", "btg": "不通过"}
|
||||
for t in tasks:
|
||||
task_list.append({
|
||||
"id": t.id,
|
||||
"taskType": t.task_type or "",
|
||||
"taskTypeLabel": _task_type_labels.get(t.task_type or "", t.task_type or ""),
|
||||
"taskStatus": t.task_status or "",
|
||||
"taskStatusLabel": _task_status_labels.get(t.task_status or "", t.task_status or ""),
|
||||
"conclusion": getattr(t, "conclusion", "") or "",
|
||||
"conclusionLabel": _conclusion_labels.get(getattr(t, "conclusion", "") or "", getattr(t, "conclusion", "") or ""),
|
||||
})
|
||||
return {
|
||||
"workOrderNo": wo.work_order_no or "",
|
||||
"workOrderStatus": wo.work_order_status or "",
|
||||
"tasks": task_list,
|
||||
}
|
||||
|
||||
|
||||
def _build_production_info(db: Session, rev: BizStorageReleaseReview, tenant_no: str) -> Dict[str, Any]:
|
||||
pwo = (
|
||||
db.query(BizProductionWorkOrder)
|
||||
.filter(
|
||||
BizProductionWorkOrder.reception_id == (rev.reception_id or ""),
|
||||
BizProductionWorkOrder.tenant_no == tenant_no,
|
||||
BizProductionWorkOrder.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not pwo:
|
||||
return {}
|
||||
tasks = (
|
||||
db.query(BizProductionTask)
|
||||
.filter(
|
||||
BizProductionTask.work_order_id == pwo.id,
|
||||
BizProductionTask.tenant_no == tenant_no,
|
||||
BizProductionTask.del_flag == 0,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
task_list = []
|
||||
_task_status_labels = {"dpz": "待配置", "dzx": "待执行", "jxz": "执行中", "ywc": "已完成"}
|
||||
for t in tasks:
|
||||
task_list.append({
|
||||
"id": t.id,
|
||||
"taskNo": t.task_no or "",
|
||||
"taskName": t.task_name or "",
|
||||
"taskStatus": t.task_status or "",
|
||||
"taskStatusLabel": _task_status_labels.get(t.task_status or "", t.task_status or ""),
|
||||
"startedTime": _fmt_dt(t.started_time),
|
||||
"completedTime": _fmt_dt(t.completed_time),
|
||||
})
|
||||
return {
|
||||
"workOrderNo": pwo.work_order_no or "",
|
||||
"workOrderStatus": pwo.work_order_status or "",
|
||||
"tasks": task_list,
|
||||
}
|
||||
|
||||
|
||||
def submit_review(
|
||||
db: Session,
|
||||
review_id: str,
|
||||
*,
|
||||
review_conclusion: str,
|
||||
review_remark: Optional[str] = None,
|
||||
review_attachments: Optional[str] = None,
|
||||
tenant_no: str,
|
||||
user_id: str,
|
||||
) -> BizStorageReleaseReview:
|
||||
rev = (
|
||||
db.query(BizStorageReleaseReview)
|
||||
.filter(
|
||||
BizStorageReleaseReview.id == review_id,
|
||||
BizStorageReleaseReview.tenant_no == tenant_no,
|
||||
BizStorageReleaseReview.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not rev:
|
||||
raise ValueError("审核记录不存在")
|
||||
user_name = _get_user_name(db, user_id)
|
||||
rev.review_conclusion = review_conclusion
|
||||
rev.review_remark = (review_remark or "").strip() or None
|
||||
rev.review_attachments = (review_attachments or "").strip() or None
|
||||
rev.review_by = user_id
|
||||
rev.review_by_name = user_name
|
||||
rev.review_time = datetime.now()
|
||||
rev.update_by = user_id
|
||||
rev.update_time = datetime.now()
|
||||
db.add(rev)
|
||||
db.flush()
|
||||
return rev
|
||||
|
||||
|
||||
def batch_submit_review(
|
||||
db: Session,
|
||||
ids: List[str],
|
||||
*,
|
||||
review_conclusion: str,
|
||||
review_remark: Optional[str] = None,
|
||||
review_attachments: Optional[str] = None,
|
||||
tenant_no: str,
|
||||
user_id: str,
|
||||
) -> Dict[str, Any]:
|
||||
success = 0
|
||||
fail = 0
|
||||
for rid in ids:
|
||||
try:
|
||||
submit_review(
|
||||
db,
|
||||
rid,
|
||||
review_conclusion=review_conclusion,
|
||||
review_remark=review_remark,
|
||||
review_attachments=review_attachments,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
)
|
||||
success += 1
|
||||
except Exception:
|
||||
fail += 1
|
||||
return {"success": success, "fail": fail}
|
||||
|
||||
|
||||
def store_sample(
|
||||
db: Session,
|
||||
review_id: str,
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_id: str,
|
||||
) -> BizStorageReleaseReview:
|
||||
rev = (
|
||||
db.query(BizStorageReleaseReview)
|
||||
.filter(
|
||||
BizStorageReleaseReview.id == review_id,
|
||||
BizStorageReleaseReview.tenant_no == tenant_no,
|
||||
BizStorageReleaseReview.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not rev:
|
||||
raise ValueError("审核记录不存在")
|
||||
if rev.review_conclusion != "tg":
|
||||
raise ValueError("只有审核通过的记录才能执行入库操作")
|
||||
if rev.storage_status != "drk":
|
||||
raise ValueError("当前状态不允许入库操作")
|
||||
user_name = _get_user_name(db, user_id)
|
||||
rev.storage_status = "yrk"
|
||||
rev.storage_by = user_id
|
||||
rev.storage_by_name = user_name
|
||||
rev.storage_time = datetime.now()
|
||||
rev.update_by = user_id
|
||||
rev.update_time = datetime.now()
|
||||
db.add(rev)
|
||||
|
||||
if (rev.reception_source or "cell_storage") == "cell_storage":
|
||||
sample_code = (rev.sample_code or "").strip()
|
||||
if sample_code:
|
||||
ps = (
|
||||
db.query(BizProtocolSample)
|
||||
.filter(
|
||||
BizProtocolSample.sample_code == sample_code,
|
||||
BizProtocolSample.tenant_no == tenant_no,
|
||||
BizProtocolSample.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if ps:
|
||||
ps.sample_status = "yrk"
|
||||
ps.update_time = datetime.now()
|
||||
db.add(ps)
|
||||
|
||||
db.flush()
|
||||
return rev
|
||||
|
||||
|
||||
def batch_store_samples(
|
||||
db: Session,
|
||||
ids: List[str],
|
||||
*,
|
||||
tenant_no: str,
|
||||
user_id: str,
|
||||
) -> Dict[str, Any]:
|
||||
success = 0
|
||||
fail = 0
|
||||
for rid in ids:
|
||||
try:
|
||||
store_sample(db, rid, tenant_no=tenant_no, user_id=user_id)
|
||||
success += 1
|
||||
except Exception:
|
||||
fail += 1
|
||||
return {"success": success, "fail": fail}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L1"}, {"id": "api_init_rationale_1", "label": "API \u8def\u7531\u5305\uff08\u9aa8\u67b6\uff1a\u516c\u5171\u9274\u6743 + \u9759\u6001\u83dc\u5355 + \u8fd0\u8425\u7aef\u5360\u4f4d\uff09", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_public", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_menu", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_workflow", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_basic_info", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_collection_protocol", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_leave", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L8", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_operations", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_ethnic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_sample_reception", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L11", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_qc", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_research_donation", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L13", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L14", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_pilot_service", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L15", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_insurance_company", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_init_py", "target": "app_api_material", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py", "source_location": "L17", "weight": 1.0}, {"source": "usLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "label": "storage_release_review.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L1"}, {"id": "api_storage_release_review_get_tenant_no", "label": "_get_tenant_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L20"}, {"id": "api_storage_release_review_get_current_user_id", "label": "_get_current_user_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L30"}, {"id": "api_storage_release_review_list_storage_release_reviews", "label": "list_storage_release_reviews()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L38"}, {"id": "api_storage_release_review_get_storage_release_review_detail", "label": "get_storage_release_review_detail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L69"}, {"id": "api_storage_release_review_submit_single_review", "label": "submit_single_review()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L87"}, {"id": "api_storage_release_review_batch_review", "label": "batch_review()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L116"}, {"id": "api_storage_release_review_store_single_sample", "label": "store_single_sample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L144"}, {"id": "api_storage_release_review_batch_store_samples", "label": "batch_store_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L169"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "target": "fastapi", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "target": "sqlalchemy_orm", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "target": "app_core_database", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "target": "app_core_menu_guard", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L7", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "target": "app_core_response", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L8", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "target": "app_crud", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "target": "app_models_models", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_release_review_py", "target": "app_schemas_storage_release_review", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py", "source_location": "L11", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_api_storage_releLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "label": "main.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L1"}, {"id": "app_main_lifespan", "label": "lifespan()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L17"}, {"id": "app_main_mask_secret", "label": "_mask_secret()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L82"}, {"id": "app_main_root", "label": "root()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L104"}, {"id": "app_main_health_check", "label": "health_check()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L109"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "contextlib", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "logging", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "fastapi", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L5", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "fastapi_middleware_cors", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_core_config", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L8", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_core_database", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_core_api_request_log_middleware", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L10", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_core_api_crypto_middleware", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L11", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_models", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_api", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L13", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_main_lifespan", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L17", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_main_mask_secret", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L82", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_main_root", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L104", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_main_py", "target": "app_main_health_check", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L109", "weight": 1.0}], "raw_calls": [{"caller_nid": "app_main_lifespan", "callee": "ensure_initialized", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L21"}, {"caller_nid": "app_main_lifespan", "callee": "init_scheduled_tasks", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/main.py", "source_location": "L26"}, {"caller_nid": "app_main_lifespan", "callee": "warning", "soLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_schemas_storage_release_review_py", "label": "storage_release_review.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L1"}, {"id": "schemas_storage_release_review_storagereleasereviewlistquery", "label": "StorageReleaseReviewListQuery", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L6"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "schemas_storage_release_review_reviewsubmitbody", "label": "ReviewSubmitBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L17"}, {"id": "schemas_storage_release_review_batchreviewbody", "label": "BatchReviewBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L25"}, {"id": "schemas_storage_release_review_batchstorebody", "label": "BatchStoreBody", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L34"}, {"id": "schemas_storage_release_review_storagereleasereviewlistitem", "label": "StorageReleaseReviewListItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L40"}, {"id": "schemas_storage_release_review_storagereleasereviewdetail", "label": "StorageReleaseReviewDetail", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L63"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_storage_release_review_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_storage_release_review_py", "target": "pydantic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_storage_release_review_py", "target": "schemas_storage_release_review_storagereleasereviewlistquery", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L6", "weight": 1.0}, {"source": "schemas_storage_release_review_storagereleasereviewlistquery", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_storage_release_review_py", "target": "schemas_storage_release_review_reviewsubmitbody", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L17", "weight": 1.0}, {"source": "schemas_storage_release_review_reviewsubmitbody", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L17", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_storage_release_review_py", "target": "schemas_storage_release_review_batchreviewbody", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L25", "weight": 1.0}, {"source": "schemas_storage_release_review_batchreviewbody", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_storage_release_review_py", "target": "schemas_storage_release_review_batchstorebody", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L34", "weight": 1.0}, {"source": "schemas_storage_release_review_batchstorebody", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py", "source_location": "L34", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_models_models_py", "label": "models.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L1"}, {"id": "models_models_userrole", "label": "UserRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L10"}, {"id": "str", "label": "str", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_models_sysuser", "label": "SysUser", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L22"}, {"id": "base", "label": "Base", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "models_models_sysrole", "label": "SysRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L48"}, {"id": "models_models_sysuserrole", "label": "SysUserRole", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L68"}, {"id": "models_models_sysrolemenu", "label": "SysRoleMenu", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L87"}, {"id": "models_models_sysroledatascopedetail", "label": "SysRoleDataScopeDetail", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L106"}, {"id": "models_models_sysmenu", "label": "SysMenu", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L126"}, {"id": "models_models_sysdept", "label": "SysDept", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L152"}, {"id": "models_models_systenant", "label": "SysTenant", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L175"}, {"id": "models_models_syssite", "label": "SysSite", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L195"}, {"id": "models_models_syspost", "label": "SysPost", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L220"}, {"id": "models_models_sysuserpost", "label": "SysUserPost", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L240"}, {"id": "models_models_bizitemunit", "label": "BizItemUnit", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L259"}, {"id": "models_models_bizexpresscompany", "label": "BizExpressCompany", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L278"}, {"id": "models_models_bizcustomer", "label": "BizCustomer", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L297"}, {"id": "models_models_bizcellsampletype", "label": "BizCellSampleType", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L320"}, {"id": "models_models_bizcollectionregion", "label": "BizCollectionRegion", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L341"}, {"id": "models_models_bizcollectionhospital", "label": "BizCollectionHospital", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L359"}, {"id": "models_models_bizcollectionprotocolno", "label": "BizCollectionProtocolNo", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L378"}, {"id": "models_models_bizsamplecode", "label": "BizSampleCode", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L400"}, {"id": "models_models_sysdicttype", "label": "SysDictType", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L428"}, {"id": "models_models_sysethnic", "label": "SysEthnic", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L447"}, {"id": "models_models_sysdictdata", "label": "SysDictData", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location": "L466"}, {"id": "models_models_sysapirequestlog", "label": "SysApiRequestLog", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py", "source_location"Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_sample_reception_py", "label": "sample_reception.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L1"}, {"id": "crud_sample_reception_donation_sample_id_col", "label": "_donation_sample_id_col()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L34"}, {"id": "crud_sample_reception_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L39"}, {"id": "crud_sample_reception_dec", "label": "_dec()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L43"}, {"id": "crud_sample_reception_reception_to_dict", "label": "_reception_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L49"}, {"id": "crud_sample_reception_sample_detail_rows_query", "label": "_sample_detail_rows_query()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L73"}, {"id": "crud_sample_reception_donation_sample_detail_rows_query", "label": "_donation_sample_detail_rows_query()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L107"}, {"id": "crud_sample_reception_donation_sample_pending_query", "label": "_donation_sample_pending_query()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L141"}, {"id": "crud_sample_reception_donation_rows_to_sample_list_items", "label": "_donation_rows_to_sample_list_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L172"}, {"id": "crud_sample_reception_sample_pending_query", "label": "_sample_pending_query()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L253"}, {"id": "crud_sample_reception_rows_to_sample_list_items", "label": "_rows_to_sample_list_items()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L287"}, {"id": "crud_sample_reception_list_pending_protocol_samples", "label": "list_pending_protocol_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L374"}, {"id": "crud_sample_reception_list_received_protocol_samples", "label": "list_received_protocol_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L436"}, {"id": "crud_sample_reception_get_reception_by_id", "label": "get_reception_by_id()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L564"}, {"id": "crud_sample_reception_create_sample_reception", "label": "create_sample_reception()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L632"}, {"id": "crud_sample_reception_update_sample_reception", "label": "update_sample_reception()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L866"}, {"id": "crud_sample_reception_reception_detail_item", "label": "_reception_detail_item()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L949"}, {"id": "crud_sample_reception_reception_sample_code", "label": "_reception_sample_code()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L966"}, {"id": "crud_sample_reception_get_receptions_map_for_samples", "label": "get_receptions_map_for_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L981"}, {"id": "crud_sample_reception_get_receptions_map_for_donation_samples", "label": "get_receptions_map_for_donation_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_location": "L1026"}, {"id": "crud_sample_reception_rationale_35", "label": "\u6350\u8d60\u6837\u672c ID \u4e0e biz_sample_reception.donation_sample_id \u6bd4\u8f83\u65f6\u7edf\u4e00\u6392\u5e8f\u89c4\u5219\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py", "source_locationLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_schemas_cell_product_py", "label": "cell_product.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L1"}, {"id": "schemas_cell_product_coerce_optional_datetime", "label": "_coerce_optional_datetime()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L9"}, {"id": "schemas_cell_product_cellproductbatchitem", "label": "CellProductBatchItem", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L23"}, {"id": "basemodel", "label": "BaseModel", "file_type": "code", "source_file": "", "source_location": ""}, {"id": "schemas_cell_product_cellproductcreate", "label": "CellProductCreate", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L32"}, {"id": "schemas_cell_product_coerce_date_fields", "label": "coerce_date_fields()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L56"}, {"id": "schemas_cell_product_validate_viability", "label": "validate_viability()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L61"}, {"id": "schemas_cell_product_validate_passage", "label": "validate_passage()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L68"}, {"id": "schemas_cell_product_cellproductupdate", "label": "CellProductUpdate", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L77"}, {"id": "schemas_cell_product_validate_status", "label": "validate_status()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L120"}, {"id": "schemas_cell_product_cellproductlistquery", "label": "CellProductListQuery", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L127"}, {"id": "schemas_cell_product_rationale_10", "label": "\u517c\u5bb9\u524d\u7aef date-picker \u7684 YYYY-MM-DD \u4e0e\u5b8c\u6574\u65e5\u671f\u65f6\u95f4\u5b57\u7b26\u4e32\u3002", "file_type": "rationale", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L10"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_cell_product_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_cell_product_py", "target": "typing", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_cell_product_py", "target": "pydantic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L4", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_cell_product_py", "target": "app_core_utils", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L6", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_cell_product_py", "target": "schemas_cell_product_coerce_optional_datetime", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L9", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_cell_product_py", "target": "schemas_cell_product_cellproductbatchitem", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L23", "weight": 1.0}, {"source": "schemas_cell_product_cellproductbatchitem", "target": "basemodel", "relation": "inherits", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L23", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_schemas_cell_product_py", "target": "schemas_cell_product_cellproductcreate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py", "source_location": "L32", "weigLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_py", "label": "production_task.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L1"}, {"id": "crud_production_task_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L43"}, {"id": "crud_production_task_generate_task_no", "label": "_generate_task_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L47"}, {"id": "crud_production_task_task_to_dict", "label": "_task_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L61"}, {"id": "crud_production_task_step_to_dict", "label": "_step_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L83"}, {"id": "crud_production_task_step_reagent_to_dict", "label": "_step_reagent_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L103"}, {"id": "crud_production_task_step_consumable_to_dict", "label": "_step_consumable_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L118"}, {"id": "crud_production_task_step_output_to_dict", "label": "_step_output_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L133"}, {"id": "crud_production_task_step_data_collection_to_dict", "label": "_step_data_collection_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L144"}, {"id": "crud_production_task_task_input_to_dict", "label": "_task_input_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L155"}, {"id": "crud_production_task_task_output_to_dict", "label": "_task_output_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L177"}, {"id": "crud_production_task_soft_delete_sub", "label": "_soft_delete_sub()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L202"}, {"id": "crud_production_task_clone_step_reagents", "label": "_clone_step_reagents()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L215"}, {"id": "crud_production_task_clone_step_consumables", "label": "_clone_step_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L233"}, {"id": "crud_production_task_clone_step_outputs", "label": "_clone_step_outputs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L251"}, {"id": "crud_production_task_clone_step_data_collections", "label": "_clone_step_data_collections()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L268"}, {"id": "crud_production_task_clone_route_to_task", "label": "_clone_route_to_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L284"}, {"id": "crud_production_task_create_production_task", "label": "create_production_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L317"}, {"id": "crud_production_task_list_production_tasks", "label": "list_production_tasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L359"}, {"id": "crud_production_task_get_production_task_detail", "label": "get_production_task_detail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L479"}, {"id": "crud_production_task_update_production_task", "label": "update_production_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L569"}, {"id": "crud_production_task_update_task_status", "label": "update_task_status()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L594"}, {"id": "crud_production_task_add_task_step", "label": "add_task_step()", "file_type": Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_storage_release_review_py", "label": "storage_release_review.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L1"}, {"id": "crud_storage_release_review_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L39"}, {"id": "crud_storage_release_review_total_pages", "label": "total_pages()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L43"}, {"id": "crud_storage_release_review_row_to_dict", "label": "_row_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L49"}, {"id": "crud_storage_release_review_create_review_from_reception", "label": "create_review_from_reception()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L77"}, {"id": "crud_storage_release_review_list_reviews", "label": "list_reviews()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L115"}, {"id": "crud_storage_release_review_get_user_name", "label": "_get_user_name()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L172"}, {"id": "crud_storage_release_review_get_review_detail", "label": "get_review_detail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L177"}, {"id": "crud_storage_release_review_build_cell_storage_protocol_info", "label": "_build_cell_storage_protocol_info()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L216"}, {"id": "crud_storage_release_review_build_research_donation_protocol_info", "label": "_build_research_donation_protocol_info()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L273"}, {"id": "crud_storage_release_review_build_cell_storage_transport_info", "label": "_build_cell_storage_transport_info()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L330"}, {"id": "crud_storage_release_review_build_research_donation_transport_info", "label": "_build_research_donation_transport_info()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L364"}, {"id": "crud_storage_release_review_build_reception_info", "label": "_build_reception_info()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L398"}, {"id": "crud_storage_release_review_build_qc_info", "label": "_build_qc_info()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L420"}, {"id": "crud_storage_release_review_build_production_info", "label": "_build_production_info()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L462"}, {"id": "crud_storage_release_review_submit_review", "label": "submit_review()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L502"}, {"id": "crud_storage_release_review_batch_submit_review", "label": "batch_submit_review()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L537"}, {"id": "crud_storage_release_review_store_sample", "label": "store_sample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L566"}, {"id": "crud_storage_release_review_batch_store_samples", "label": "batch_store_samples()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L618"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_storage_release_review_py", "target": "math", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_backend_app_crud_storage_release_review_py", "target": "datetime", "relation": "imports_from", "confidence": "EXTRALine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_backend_app_crud_production_task_py", "label": "production_task.py", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L1"}, {"id": "crud_production_task_fmt_dt", "label": "_fmt_dt()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L43"}, {"id": "crud_production_task_generate_task_no", "label": "_generate_task_no()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L47"}, {"id": "crud_production_task_task_to_dict", "label": "_task_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L61"}, {"id": "crud_production_task_step_to_dict", "label": "_step_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L83"}, {"id": "crud_production_task_step_reagent_to_dict", "label": "_step_reagent_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L103"}, {"id": "crud_production_task_step_consumable_to_dict", "label": "_step_consumable_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L118"}, {"id": "crud_production_task_step_output_to_dict", "label": "_step_output_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L133"}, {"id": "crud_production_task_step_data_collection_to_dict", "label": "_step_data_collection_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L144"}, {"id": "crud_production_task_task_input_to_dict", "label": "_task_input_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L155"}, {"id": "crud_production_task_task_output_to_dict", "label": "_task_output_to_dict()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L177"}, {"id": "crud_production_task_soft_delete_sub", "label": "_soft_delete_sub()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L202"}, {"id": "crud_production_task_clone_step_reagents", "label": "_clone_step_reagents()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L215"}, {"id": "crud_production_task_clone_step_consumables", "label": "_clone_step_consumables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L233"}, {"id": "crud_production_task_clone_step_outputs", "label": "_clone_step_outputs()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L251"}, {"id": "crud_production_task_clone_step_data_collections", "label": "_clone_step_data_collections()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L268"}, {"id": "crud_production_task_clone_route_to_task", "label": "_clone_route_to_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L284"}, {"id": "crud_production_task_create_production_task", "label": "create_production_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L317"}, {"id": "crud_production_task_list_production_tasks", "label": "list_production_tasks()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L359"}, {"id": "crud_production_task_get_production_task_detail", "label": "get_production_task_detail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L479"}, {"id": "crud_production_task_update_production_task", "label": "update_production_task()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L569"}, {"id": "crud_production_task_update_task_status", "label": "update_task_status()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py", "source_location": "L594"}, {"id": "crud_production_task_add_task_step", "label": "add_task_step()", "file_type": Line truncated
|
||||
+2
-1
@@ -10,7 +10,7 @@ from app.core.database import SessionLocal
|
||||
from app.core.api_request_log_middleware import ApiRequestLogMiddleware
|
||||
from app.core.api_crypto_middleware import ApiCryptoMiddleware
|
||||
from app.models import models # noqa: F401
|
||||
from app.api import public_router, menu_router, admin_router, workflow_router, basic_info_router, collection_protocol_router, leave_router, operations_router, ethnic_router, sample_reception_router, qc_router, research_donation_router, production_router, pilot_service_router, insurance_company_router, material_router
|
||||
from app.api import public_router, menu_router, admin_router, workflow_router, basic_info_router, collection_protocol_router, leave_router, operations_router, ethnic_router, sample_reception_router, qc_router, research_donation_router, production_router, pilot_service_router, insurance_company_router, material_router, storage_release_review_router
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@@ -74,6 +74,7 @@ app.include_router(production_router, prefix=settings.API_V1_PREFIX)
|
||||
app.include_router(pilot_service_router, prefix=settings.API_V1_PREFIX)
|
||||
app.include_router(insurance_company_router, prefix=settings.API_V1_PREFIX)
|
||||
app.include_router(material_router, prefix=settings.API_V1_PREFIX)
|
||||
app.include_router(storage_release_review_router, prefix=settings.API_V1_PREFIX)
|
||||
|
||||
logger = logging.getLogger("sqxp")
|
||||
|
||||
|
||||
@@ -761,6 +761,39 @@ class BizProtocolAudit(Base):
|
||||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
|
||||
|
||||
|
||||
class BizStorageReleaseReview(Base):
|
||||
"""入库放行审核 biz_storage_release_review"""
|
||||
|
||||
__tablename__ = "biz_storage_release_review"
|
||||
|
||||
id = Column(String(64), primary_key=True, comment="主键ID")
|
||||
sample_code = Column(String(64), nullable=False, comment="样本编码")
|
||||
reception_source = Column(String(32), nullable=False, comment="来源类型 cell_storage细胞存储 research_donation科研捐赠")
|
||||
protocol_id = Column(String(64), nullable=True, comment="协议单ID")
|
||||
reception_id = Column(String(64), nullable=True, comment="接收记录ID")
|
||||
protocol_no = Column(String(64), nullable=True, comment="协议编号快照")
|
||||
sample_name = Column(String(128), nullable=True, comment="样本名称")
|
||||
review_conclusion = Column(String(16), nullable=True, comment="审核结论 tg通过 btg不通过")
|
||||
review_remark = Column(String(512), nullable=True, comment="审核备注")
|
||||
review_attachments = Column(String(4000), nullable=True, comment="审核附件(逗号分隔URL)")
|
||||
review_by = Column(String(64), nullable=True, comment="审核人ID")
|
||||
review_by_name = Column(String(64), nullable=True, comment="审核人姓名")
|
||||
review_time = Column(DateTime, nullable=True, comment="审核时间")
|
||||
storage_status = Column(String(16), nullable=False, default="drk", comment="入库状态 drk待入库 yrk已入库")
|
||||
storage_by = Column(String(64), nullable=True, comment="入库操作人ID")
|
||||
storage_by_name = Column(String(64), nullable=True, comment="入库操作人姓名")
|
||||
storage_time = Column(DateTime, nullable=True, comment="入库时间")
|
||||
tenant_no = Column(String(64), nullable=False, default="", comment="租户编号")
|
||||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||||
remark = Column(String(512), nullable=True, comment="备注")
|
||||
create_by = Column(String(64), nullable=True, comment="创建人id")
|
||||
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
|
||||
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
|
||||
update_by = Column(String(64), nullable=True, comment="更新人id")
|
||||
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
|
||||
|
||||
|
||||
class BizProtocolPayment(Base):
|
||||
"""采集协议单-缴费主表 biz_protocol_payment"""
|
||||
|
||||
|
||||
@@ -118,8 +118,9 @@ class CellProductUpdate(BaseModel):
|
||||
@field_validator("product_status")
|
||||
@classmethod
|
||||
def validate_status(cls, v):
|
||||
if v is not None and v not in ("frozen", "sold", "used", "voided"):
|
||||
raise ValueError("状态必须为 frozen/sold/used/voided")
|
||||
allowed = ("frozen", "sold", "used", "voided", "in_production", "thawed")
|
||||
if v is not None and v not in allowed:
|
||||
raise ValueError(f"状态必须为 {'/'.join(allowed)}")
|
||||
return v
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class StorageReleaseReviewListQuery(BaseModel):
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
page: int = Field(1, ge=1)
|
||||
pageSize: int = Field(10, ge=1, le=100)
|
||||
keyword: Optional[str] = None
|
||||
receptionSource: Optional[str] = None
|
||||
reviewConclusion: Optional[str] = None
|
||||
storageStatus: Optional[str] = None
|
||||
|
||||
|
||||
class ReviewSubmitBody(BaseModel):
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
reviewConclusion: str = Field(..., description="tg/btg")
|
||||
reviewRemark: Optional[str] = None
|
||||
reviewAttachments: Optional[str] = None
|
||||
|
||||
|
||||
class BatchReviewBody(BaseModel):
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
ids: List[str] = Field(..., min_length=1)
|
||||
reviewConclusion: str = Field(..., description="tg/btg")
|
||||
reviewRemark: Optional[str] = None
|
||||
reviewAttachments: Optional[str] = None
|
||||
|
||||
|
||||
class BatchStoreBody(BaseModel):
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
ids: List[str] = Field(..., min_length=1)
|
||||
|
||||
|
||||
class StorageReleaseReviewListItem(BaseModel):
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
id: str
|
||||
sampleCode: str
|
||||
receptionSource: str
|
||||
protocolId: Optional[str] = None
|
||||
receptionId: Optional[str] = None
|
||||
protocolNo: Optional[str] = None
|
||||
sampleName: Optional[str] = None
|
||||
reviewConclusion: Optional[str] = None
|
||||
reviewRemark: Optional[str] = None
|
||||
reviewAttachments: Optional[str] = None
|
||||
reviewBy: Optional[str] = None
|
||||
reviewByName: Optional[str] = None
|
||||
reviewTime: Optional[str] = None
|
||||
storageStatus: str
|
||||
storageBy: Optional[str] = None
|
||||
storageByName: Optional[str] = None
|
||||
storageTime: Optional[str] = None
|
||||
createTime: Optional[str] = None
|
||||
|
||||
|
||||
class StorageReleaseReviewDetail(BaseModel):
|
||||
model_config = ConfigDict(populate_by_name=True, extra="allow")
|
||||
|
||||
review: Optional[dict] = None
|
||||
protocolInfo: Optional[dict] = None
|
||||
transportInfo: Optional[dict] = None
|
||||
receptionInfo: Optional[dict] = None
|
||||
qcInfo: Optional[dict] = None
|
||||
productionInfo: Optional[dict] = None
|
||||
File diff suppressed because it is too large.
Load diff
+4297
-2164
File diff suppressed because it is too large.
Load diff
@@ -1,12 +1,12 @@
|
||||
# Graph Report - app (2026-05-22)
|
||||
# Graph Report - app (2026-05-23)
|
||||
|
||||
## Corpus Check
|
||||
- 137 files · ~123,153 words
|
||||
- 140 files · ~125,737 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 2443 nodes · 9660 edges · 54 communities detected
|
||||
- Extraction: 42% EXTRACTED · 58% INFERRED · 0% AMBIGUOUS · INFERRED: 5649 edges (avg confidence: 0.63)
|
||||
- 2481 nodes · 9810 edges · 52 communities detected
|
||||
- Extraction: 42% EXTRACTED · 58% INFERRED · 0% AMBIGUOUS · INFERRED: 5728 edges (avg confidence: 0.63)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
@@ -56,26 +56,24 @@
|
||||
- [[_COMMUNITY_Community 43|Community 43]]
|
||||
- [[_COMMUNITY_Community 44|Community 44]]
|
||||
- [[_COMMUNITY_Community 45|Community 45]]
|
||||
- [[_COMMUNITY_Community 46|Community 46]]
|
||||
- [[_COMMUNITY_Community 47|Community 47]]
|
||||
- [[_COMMUNITY_Community 48|Community 48]]
|
||||
- [[_COMMUNITY_Community 49|Community 49]]
|
||||
- [[_COMMUNITY_Community 50|Community 50]]
|
||||
- [[_COMMUNITY_Community 51|Community 51]]
|
||||
- [[_COMMUNITY_Community 52|Community 52]]
|
||||
- [[_COMMUNITY_Community 53|Community 53]]
|
||||
- [[_COMMUNITY_Community 54|Community 54]]
|
||||
- [[_COMMUNITY_Community 55|Community 55]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `success_response()` - 385 edges
|
||||
2. `error_response()` - 308 edges
|
||||
1. `success_response()` - 391 edges
|
||||
2. `error_response()` - 314 edges
|
||||
3. `SysUser` - 218 edges
|
||||
4. `BizQcMaterialUsage` - 199 edges
|
||||
5. `BizMaterialStockLog` - 195 edges
|
||||
6. `BizQcTaskResult` - 195 edges
|
||||
7. `BizQcMaterial` - 195 edges
|
||||
8. `BizMaterial` - 192 edges
|
||||
9. `BizQcItemParam` - 190 edges
|
||||
10. `generate_uuid()` - 135 edges
|
||||
4. `BizQcMaterialUsage` - 201 edges
|
||||
5. `BizMaterialStockLog` - 197 edges
|
||||
6. `BizQcTaskResult` - 197 edges
|
||||
7. `BizQcMaterial` - 197 edges
|
||||
8. `BizMaterial` - 194 edges
|
||||
9. `BizQcItemParam` - 192 edges
|
||||
10. `generate_uuid()` - 136 edges
|
||||
|
||||
## Surprising Connections (you probably didn't know these)
|
||||
- `统一记录 API 请求/响应出入参的中间件` --uses--> `SysApiRequestLog` [INFERRED]
|
||||
@@ -92,261 +90,253 @@
|
||||
## Communities
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.02
|
||||
Nodes (432): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+424 more)
|
||||
Cohesion: 0.01
|
||||
Nodes (427): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+419 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.01
|
||||
Nodes (411): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+403 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (416): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+408 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.06
|
||||
Nodes (270): AuditCreateBody, BatchDetailIdsBody, BatchRefundFinanceSubmitBody, FinanceConfirmBody, FullmoonTodoInsuranceBody, ProtocolCreateBody, ProtocolUpdateBody, 细胞存储 — 采集协议单 / 审核 / 财务确认 / 分期预警 API (+262 more)
|
||||
Nodes (272): AuditCreateBody, BatchDetailIdsBody, BatchRefundFinanceSubmitBody, FinanceConfirmBody, FullmoonTodoInsuranceBody, ProtocolCreateBody, ProtocolUpdateBody, 细胞存储 — 采集协议单 / 审核 / 财务确认 / 分期预警 API (+264 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.03
|
||||
Nodes (96): list_roles_for_select(), _avatar_url_field(), _build_user_info_payload(), _jwt_user_payload(), logout(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, 与 web-admin `userInfo` 字段约定一致。 (+88 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (190): generate_uuid(), appointment_no_exists(), create_exhibition_appointment(), _format_visit_date(), _generate_appointment_no(), get_exhibition_appointment(), list_exhibition_appointments(), 展览预约 CRUD(biz_exhibition_appointment) (+182 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.03
|
||||
Nodes (118): generate_uuid(), batch_stock_in(), batch_stock_out(), _code_exists(), create_material(), _dec(), _fmt_dt(), get_material() (+110 more)
|
||||
Nodes (147): list_collection_regions(), _persist_protocol_sub_resources(), list_ethnics_for_select(), InsuranceCompanyCreateBody, InsuranceCompanyInvoiceCreateBody, InsuranceCompanyInvoiceUpdateBody, InsuranceCompanyUpdateBody, InsuranceProjectPriceCreateBody (+139 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.04
|
||||
Nodes (109): list_ethnics_for_select(), apply_data_scope(), _get_child_dept_ids(), get_user_dept_id(), _resolve_user_role_scopes(), delete(), get_multi_with_filters(), _add_months_yyyymmdd() (+101 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (99): list_roles_for_select(), _avatar_url_field(), _build_user_info_payload(), _jwt_user_payload(), logout(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线), 登出:吊销 Redis 会话(若已配置 REDIS_HOST)。, 与 web-admin `userInfo` 字段约定一致。 (+91 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.04
|
||||
Nodes (79): activate_channel_agreement(), cell_product_price_code_exists(), cell_product_spec_code_exists(), cell_product_type_code_exists(), _channel_agreement_price_exists(), _channel_agreement_price_item_key(), checkup_package_code_exists(), create_cell_product_price() (+71 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (96): _accept_conclusion_label(), _attach_reception_detail(), complete_production_work_order(), create_production_work_order_from_reception(), _enrich_wo_items(), _fmt_dt(), _generate_work_order_no(), get_production_work_order() (+88 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.06
|
||||
Nodes (79): urls_to_attachment_list(), _accept_conclusion_label(), _attach_reception_detail(), complete_production_work_order(), create_production_work_order_from_reception(), _enrich_wo_items(), _fmt_dt(), _generate_work_order_no() (+71 more)
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.07
|
||||
Nodes (67): AccessLogCreateBody, AccessLogUpdateBody, CleanRoomCreateBody, CleanRoomUpdateBody, ExhibitionAppointmentCreateBody, ExhibitionAppointmentUpdateBody, OccupancyCreateBody, 运营支撑 API(洁净室、进出记录、仓储设施、储位、展厅预约) (+59 more)
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.05
|
||||
Nodes (26): BusinessException, InvalidStatusTransitionException, log_delivery_operation(), log_operation(), log_order_operation(), log_settlement_operation(), log_user_operation(), OrderLockedException (+18 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.06
|
||||
Nodes (54): get_designer_data(), ProcessDefCreateBody, ProcessDefUpdateBody, PublishVersionBody, 审批意见:前端传 commentText,兼容 comment。, StartInstanceBody, TaskActionBody, list_results_by_task() (+46 more)
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.27
|
||||
Nodes (58): ConsumableCreateBody, ConsumablePackCreateBody, ConsumablePackUpdateBody, ConsumableUpdateBody, OutputTypeCreateBody, OutputTypeUpdateBody, PackItemInput, ProcessCategoryCreateBody (+50 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.09
|
||||
Nodes (58): batch_delete_research_donations(), change_donation_status(), convert_from_storage(), convert_to_storage(), create_research_donation(), delete_research_donation(), _get_current_user_id(), get_research_donation_detail() (+50 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.05
|
||||
Nodes (50): get_designer_data(), ApiRequestLogMiddleware, _claims_from_auth(), _format_body_bytes(), _persist_log(), 统一记录 API 请求/响应出入参的中间件, _redact_obj(), _schedule_persist_log() (+42 more)
|
||||
Cohesion: 0.06
|
||||
Nodes (36): BaseHTTPMiddleware, parse_admin_request_json(), 运营 Web(/web/admin)JSON 请求体解析。 与 ApiCryptoMiddleware 配合:中间件未改写 body 时,在路由内解密 `, 读取 JSON body;dict 时走 resolve_request_payload(明文 / 密文 / data 包装)。 Returns:, ApiCryptoError, _b64url_decode(), _b64url_encode(), decrypt_json() (+28 more)
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.1
|
||||
Nodes (46): attachments_to_urls(), create_collection_protocol(), _pop_attachment_urls(), _active_link_query(), assert_biz_no_available(), create_convert_link(), get_convert_partner(), get_partner_sample_codes() (+38 more)
|
||||
Cohesion: 0.12
|
||||
Nodes (11): get_wechat_pay_client(), get_wechat_pay_v2(), 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.ht, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total, 申请退款 Args: out_trade_no: 商户订单号 total_amount, 小程序调起支付的二次签名参数(与公众号 JSAPI 规则一致), 统一下单 (V2 API) Args: out_trade_no: 商户订单号 tot, 生成签名 V3 (SHA256 with RSA) (+3 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.11
|
||||
Nodes (28): get_next_cell_product_code(), _build_facility_path_label(), build_split_product_codes(), create_cell_product(), create_cell_products_batch(), create_cell_products_by_quantity(), delete_cell_product(), _enrich_product_dicts() (+20 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.12
|
||||
Nodes (10): get_wechat_pay_client(), get_wechat_pay_v2(), 微信支付工具模块 支持微信支付 V3 API 文档参考: https://pay.weixin.qq.com/docs/v3/apis/index.ht, 统一下单接口 (V3) Args: out_trade_no: 商户订单号 total, 小程序调起支付的二次签名参数(与公众号 JSAPI 规则一致), 统一下单 (V2 API) Args: out_trade_no: 商户订单号 tot, 生成签名 V3 (SHA256 with RSA), wechat_pay() (+2 more)
|
||||
Cohesion: 0.21
|
||||
Nodes (19): _consumable_row_to_dict(), create_process(), _create_sub_consumable(), _create_sub_data_collection(), _create_sub_output(), _create_sub_reagent(), _data_collection_row_to_dict(), get_process() (+11 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.15
|
||||
Nodes (20): BaseHTTPMiddleware, ApiCryptoError, _b64url_decode(), _b64url_encode(), decrypt_json(), derive_key(), encrypt_json(), gen_nonce_hex() (+12 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.18
|
||||
Nodes (18): company_code_exists(), create_company(), get_company(), list_companies(), list_company_options(), _row_to_dict(), soft_delete_company(), update_company() (+10 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.26
|
||||
Nodes (16): InsuranceCompanyCreateBody, InsuranceCompanyInvoiceCreateBody, InsuranceCompanyInvoiceUpdateBody, InsuranceCompanyUpdateBody, InsuranceProjectPriceCreateBody, InsuranceProjectPriceUpdateBody, _list_insurance_companies(), 保险公司及险种价格管理 API(保险公司 + 开票信息 + 险种价格) (+8 more)
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.17
|
||||
Nodes (18): create_storage_slot(), update_storage_slot(), batch_create_slots(), collect_descendant_ids(), create_facility(), create_slot(), get_children(), get_facility() (+10 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.2
|
||||
Nodes (14): deduct_for_task(), _display_remark(), list_usages(), list_usages_by_task(), _parse_receiver_purpose_from_remark(), 查询质检任务关联的物料使用记录(task_complete)。, 撤销任务此前 task_complete 扣减,便于重新填报物料使用。, 兼容旧数据:领用人/用途曾拼接在 remark 中。 (+6 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.25
|
||||
Nodes (13): batch_issue_protocol_nos(), batch_revoke_protocol_nos(), create_protocol_no(), get_protocol_no(), list_protocol_nos(), _parse_issued_time(), protocol_no_exists(), 采集协议号池 CRUD(biz_collection_protocol_no) (+5 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.23
|
||||
Nodes (12): create_collection_region(), create_hospital(), get_collection_region(), get_hospital(), _hospital_names_by_region_ids(), _hospital_to_dict(), list_collection_regions(), list_hospitals() (+4 more)
|
||||
Cohesion: 0.26
|
||||
Nodes (12): batch_issue_sample_codes(), batch_revoke_sample_codes(), create_sample_code(), get_sample_code(), list_sample_codes(), _parse_issued_time(), 样本编码池 CRUD(biz_sample_code), _resolve_issued_user_display_name() (+4 more)
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.22
|
||||
Nodes (12): create_access_log(), create_clean_room(), get_access_log(), get_clean_room(), list_access_logs(), list_clean_rooms(), room_code_exists(), _row_to_dict() (+4 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.24
|
||||
Nodes (9): 微信消息通知服务 支持微信小程序模板消息推送 使用方式: from app.core.wechat_message import Wech, 发送模板消息 Args: openid: 用户openid templ, send_delivery_notice(), send_delivery_warning(), send_order_status(), send_payment_success(), send_refund_result(), send_urge_notification() (+1 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.23
|
||||
Nodes (12): Config, ProductBase, ProductCategoryBase, ProductCategoryCreate, ProductCategoryResponse, ProductCreate, ProductResponse, ProductUnitBase (+4 more)
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.29
|
||||
Nodes (11): batch_issue_sample_codes(), batch_revoke_sample_codes(), create_sample_code(), get_sample_code(), list_sample_codes(), _parse_issued_time(), _resolve_issued_user_display_name(), _row_to_dict() (+3 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.31
|
||||
Nodes (11): batch_issue_protocol_nos(), batch_revoke_protocol_nos(), create_protocol_no(), get_protocol_no(), list_protocol_nos(), _parse_issued_time(), protocol_no_exists(), _resolve_issued_user_display_name() (+3 more)
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.3
|
||||
Nodes (10): create_consumable_pack(), get_consumable_pack(), get_pack_detail(), _item_row_to_dict(), list_all_packs(), list_consumable_packs(), pack_code_exists(), _row_to_dict() (+2 more)
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.27
|
||||
Nodes (11): _build_tree(), _cascade_soft_delete(), _collect_with_ancestors(), create_dept(), get_dept(), list_depts(), list_depts_tree(), 保留匹配节点及其全部上级,供关键词检索后仍能正确组树。 (+3 more)
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.29
|
||||
Nodes (9): _build_tree(), _cascade_soft_delete(), create_menu(), get_menu(), list_menus(), list_menus_tree(), _row_to_dict(), soft_delete_menu() (+1 more)
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.31
|
||||
Nodes (8): consumable_code_exists(), create_consumable(), get_consumable(), list_consumables(), 耗材 CRUD(biz_consumable), _row_to_dict(), soft_delete_consumable(), update_consumable()
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.31
|
||||
Nodes (8): create_sop_document(), get_sop_document(), list_sop_documents(), SOP文档 CRUD(biz_sop_document), _row_to_dict(), soft_delete_sop_document(), sop_code_exists(), update_sop_document()
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.31
|
||||
Nodes (8): create_express_company(), express_code_exists(), get_express_company(), list_express_companies(), 快递公司 CRUD(biz_express_company), _row_to_dict(), soft_delete_express_company(), update_express_company()
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.38
|
||||
Nodes (9): create_check_item(), get_check_item(), item_code_exists(), list_active_check_items(), list_check_items(), _order_query(), _row_to_dict(), soft_delete_check_item() (+1 more)
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.27
|
||||
Nodes (8): create_dict_data(), get_dict_data(), list_dict_data(), list_dict_data_options(), 字典数据 CRUD(sys_dict_data), _row_to_dict(), soft_delete_dict_data(), update_dict_data()
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 0.31
|
||||
Nodes (8): category_code_exists(), create_process_category(), get_process_category(), list_process_categories(), 工艺工序分类 CRUD(biz_process_category), _row_to_dict(), soft_delete_process_category(), update_process_category()
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.31
|
||||
Nodes (8): create_item_unit(), get_item_unit(), list_item_units(), 物品单位 CRUD(biz_item_unit), _row_to_dict(), soft_delete_item_unit(), unit_code_exists(), update_item_unit()
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.31
|
||||
Nodes (8): create_customer(), get_customer(), id_card_taken(), list_customers(), 客户 CRUD(biz_customer), _row_to_dict(), soft_delete_customer(), update_customer()
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.31
|
||||
Nodes (8): create_cell_sample_type(), get_cell_sample_type(), list_cell_sample_types(), 细胞样本类型 CRUD(biz_cell_sample_type), _row_to_dict(), soft_delete_cell_sample_type(), type_code_exists(), update_cell_sample_type()
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.31
|
||||
Nodes (8): create_output_type(), get_output_type(), list_output_types(), 工艺产出物类型 CRUD(biz_output_type), _row_to_dict(), soft_delete_output_type(), type_code_exists(), update_output_type()
|
||||
|
||||
### Community 37 - "Community 37"
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.36
|
||||
Nodes (7): create_post(), get_post(), list_posts(), post_code_exists(), _row_to_dict(), soft_delete_post(), update_post()
|
||||
|
||||
### Community 38 - "Community 38"
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 0.36
|
||||
Nodes (7): create_tenant(), get_tenant(), list_tenants(), _row_to_dict(), soft_delete_tenant(), tenant_no_exists(), update_tenant()
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
### Community 37 - "Community 37"
|
||||
Cohesion: 0.36
|
||||
Nodes (7): create_reagent(), get_reagent(), list_reagents(), reagent_code_exists(), _row_to_dict(), soft_delete_reagent(), update_reagent()
|
||||
|
||||
### Community 40 - "Community 40"
|
||||
### Community 38 - "Community 38"
|
||||
Cohesion: 0.36
|
||||
Nodes (7): create_site(), get_site(), list_sites(), _row_to_dict(), site_code_exists(), soft_delete_site(), update_site()
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
### Community 39 - "Community 39"
|
||||
Cohesion: 0.29
|
||||
Nodes (2): BaseModel, SoftDeleteModel
|
||||
|
||||
### Community 42 - "Community 42"
|
||||
### Community 40 - "Community 40"
|
||||
Cohesion: 0.38
|
||||
Nodes (6): derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。 约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断; 若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
|
||||
|
||||
### Community 43 - "Community 43"
|
||||
### Community 41 - "Community 41"
|
||||
Cohesion: 0.5
|
||||
Nodes (3): BaseSettings, Config, Settings
|
||||
|
||||
### Community 44 - "Community 44"
|
||||
### Community 42 - "Community 42"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): _auto_fill_create_dept_id(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID
|
||||
|
||||
### Community 45 - "Community 45"
|
||||
### Community 43 - "Community 43"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,供后端菜单模式联调)
|
||||
|
||||
### Community 46 - "Community 46"
|
||||
### Community 44 - "Community 44"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): 请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传
|
||||
|
||||
### Community 47 - "Community 47"
|
||||
### Community 45 - "Community 45"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)
|
||||
|
||||
### Community 50 - "Community 50"
|
||||
### Community 48 - "Community 48"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): 发送订单状态通知 Args: openid: 用户openid ord
|
||||
|
||||
### Community 51 - "Community 51"
|
||||
### Community 49 - "Community 49"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): 发送支付成功通知 Args: openid: 用户openid ord
|
||||
|
||||
### Community 52 - "Community 52"
|
||||
### Community 50 - "Community 50"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): 发送配送通知 Args: openid: 配送员openid task
|
||||
|
||||
### Community 53 - "Community 53"
|
||||
### Community 51 - "Community 51"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): 发送配送时效预警 Args: openid: 配送员openid ta
|
||||
|
||||
### Community 54 - "Community 54"
|
||||
### Community 52 - "Community 52"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): 发送退款结果通知 Args: openid: 用户openid ord
|
||||
|
||||
### Community 55 - "Community 55"
|
||||
### Community 53 - "Community 53"
|
||||
Cohesion: 1.0
|
||||
Nodes (1): 发送催单通知给配送员 Args: deliver_openid: 配送员openid
|
||||
|
||||
## Knowledge Gaps
|
||||
- **90 isolated node(s):** `刷新会话 TTL(refreshToken 续期;已登录用户每次 API 校验通过后滑动续期)`, `写入 refreshToken(用于 one-time 使用/吊销)`, `根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。` (+85 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **Thin community `Community 41`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
|
||||
- **Thin community `Community 39`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 44`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
|
||||
- **Thin community `Community 42`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 45`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,供后端菜单模式联调)`
|
||||
- **Thin community `Community 43`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,供后端菜单模式联调)`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 46`** (2 nodes): `context.py`, `请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
|
||||
- **Thin community `Community 44`** (2 nodes): `context.py`, `请求级别的用户上下文变量 通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 47`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
|
||||
- **Thin community `Community 45`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 50`** (1 nodes): `发送订单状态通知 Args: openid: 用户openid ord`
|
||||
- **Thin community `Community 48`** (1 nodes): `发送订单状态通知 Args: openid: 用户openid ord`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 51`** (1 nodes): `发送支付成功通知 Args: openid: 用户openid ord`
|
||||
- **Thin community `Community 49`** (1 nodes): `发送支付成功通知 Args: openid: 用户openid ord`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 52`** (1 nodes): `发送配送通知 Args: openid: 配送员openid task`
|
||||
- **Thin community `Community 50`** (1 nodes): `发送配送通知 Args: openid: 配送员openid task`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 53`** (1 nodes): `发送配送时效预警 Args: openid: 配送员openid ta`
|
||||
- **Thin community `Community 51`** (1 nodes): `发送配送时效预警 Args: openid: 配送员openid ta`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 54`** (1 nodes): `发送退款结果通知 Args: openid: 用户openid ord`
|
||||
- **Thin community `Community 52`** (1 nodes): `发送退款结果通知 Args: openid: 用户openid ord`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 55`** (1 nodes): `发送催单通知给配送员 Args: deliver_openid: 配送员openid`
|
||||
- **Thin community `Community 53`** (1 nodes): `发送催单通知给配送员 Args: deliver_openid: 配送员openid`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `generate_uuid()` connect `Community 4` to `Community 0`, `Community 1`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 11`, `Community 12`, `Community 13`, `Community 16`, `Community 18`, `Community 19`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 34`, `Community 35`, `Community 36`, `Community 37`, `Community 38`, `Community 39`, `Community 40`?**
|
||||
_High betweenness centrality (0.098) - this node is a cross-community bridge._
|
||||
- **Why does `SysUser` connect `Community 1` to `Community 2`, `Community 3`, `Community 8`, `Community 10`, `Community 17`, `Community 18`?**
|
||||
_High betweenness centrality (0.074) - this node is a cross-community bridge._
|
||||
- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 3`, `Community 5`, `Community 11`, `Community 45`, `Community 13`, `Community 17`?**
|
||||
_High betweenness centrality (0.065) - this node is a cross-community bridge._
|
||||
- **Are the 384 inferred relationships involving `success_response()` (e.g. with `web_login_rsa_public_key()` and `web_login()`) actually correct?**
|
||||
_`success_response()` has 384 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 356 inferred relationships involving `str` (e.g. with `extract_login_credentials()` and `._make_request()`) actually correct?**
|
||||
_`str` has 356 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 307 inferred relationships involving `error_response()` (e.g. with `_json_error_response()` and `web_login()`) actually correct?**
|
||||
_`error_response()` has 307 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Why does `generate_uuid()` connect `Community 3` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 6`, `Community 8`, `Community 10`, `Community 11`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 27`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 34`, `Community 35`, `Community 36`, `Community 37`, `Community 38`?**
|
||||
_High betweenness centrality (0.104) - this node is a cross-community bridge._
|
||||
- **Why does `SysUser` connect `Community 0` to `Community 2`, `Community 4`, `Community 5`, `Community 8`, `Community 9`, `Community 10`, `Community 17`, `Community 18`, `Community 19`?**
|
||||
_High betweenness centrality (0.086) - this node is a cross-community bridge._
|
||||
- **Why does `success_response()` connect `Community 1` to `Community 0`, `Community 4`, `Community 5`, `Community 8`, `Community 10`, `Community 43`, `Community 13`, `Community 16`?**
|
||||
_High betweenness centrality (0.064) - this node is a cross-community bridge._
|
||||
- **Are the 390 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
|
||||
_`success_response()` has 390 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 363 inferred relationships involving `str` (e.g. with `extract_login_credentials()` and `._make_request()`) actually correct?**
|
||||
_`str` has 363 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 313 inferred relationships involving `error_response()` (e.g. with `_json_error_response()` and `list_storage_release_reviews()`) actually correct?**
|
||||
_`error_response()` has 313 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 216 inferred relationships involving `SysUser` (e.g. with `数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤) data_scope 值: 1 = 全部数据 2 = 本部门数据 3` and `从 SysUser 读取用户的 dept_id。`) actually correct?**
|
||||
_`SysUser` has 216 INFERRED edges - model-reasoned connections that need verification._
|
||||
@@ -57,12 +57,12 @@
|
||||
</div>
|
||||
<div id="legend"></div>
|
||||
</div>
|
||||
<div id="stats">2443 nodes · 9660 edges · 60 communities</div>
|
||||
<div id="stats">2481 nodes · 9810 edges · 58 communities</div>
|
||||
</div>
|
||||
<script>
|
||||
const RAW_NODES = [{"id": "init_py", "label": "__init__.py", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 48, "community_name": "Community 48", "source_file": "__init__.py", "file_type": "code", "degree": 0}, {"id": "main_py", "label": "main.py", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "main.py", "community": 3, "community_name": "Community 3", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_lifespan", "label": "lifespan()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "lifespan()", "community": 3, "community_name": "Community 3", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_mask_secret", "label": "_mask_secret()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "_mask_secret()", "community": 3, "community_name": "Community 3", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_root", "label": "root()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "root()", "community": 3, "community_name": "Community 3", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_health_check", "label": "health_check()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "health_check()", "community": 3, "community_name": "Community 3", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "tasks_init_py", "label": "__init__.py", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 3, "community_name": "Community 3", "source_file": "tasks/__init__.py", "file_type": "code", "degree": 2}, {"id": "tasks_init_init_scheduled_tasks", "label": "init_scheduled_tasks()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "init_scheduled_tasks()", "community": 3, "community_name": "Community 3", "source_file": "tasks/__init__.py", "file_type": "code", "degree": 2}, {"id": "tasks_init_shutdown_scheduler", "label": "shutdown_scheduler()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "shutdown_scheduler()", "community": 3, "community_name": "Community 3", "source_file": "tasks/__init__.py", "file_type": "code", "degree": 2}, {"id": "core_auth_py", "label": "auth.py", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 11.4, "font": {"size": 0, "color": "#ffffff"}, "title": "auth.py", "community": 3, "community_name": "Community 3", "source_file": "core/auth.py", "file_type": "code", "degree": 18}, {"id": "core_auth_redis_session_enabled", "label": "_redis_session_enabled()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "_redis_session_enabled()", "community": 3, "community_name": "Community 3", "source_file": "core/auth.py", "file_type": "code", "degree": 2}, {"id": "core_auth_get_redis_client", "label": "_get_redis_client()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.7, "font": {"size": 0, "color": "#ffffff"}, "title": "_get_redis_client()", "community": 3, "community_name": "Community 3", "source_file": "core/auth.py", "file_type": "code", "degree": 9}, {"id": "core_auth_session_key", "label": "_session_key()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "_session_key()", "community": 3, "community_name": "Community 3", "source_file": "core/auth.py", "file_type": "code", "degree": 5}, {"id": "core_auth_refresh_key", "label": "_refresh_key()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "boLine truncated
|
||||
const RAW_EDGES = [{"from": "main_py", "to": "app_main_lifespan", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_mask_secret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_root", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_health_check", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_lifespan", "to": "core_login_crypto_ensure_initialized", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_init_scheduled_tasks", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_shutdown_scheduler", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "tasks_init_py", "to": "tasks_init_init_scheduled_tasks", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tasks_init_py", "to": "tasks_init_shutdown_scheduler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_redis_session_enabled", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_redis_client", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_session_key", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_refresh_key", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_store_session", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_revoke_session", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_extend_session", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_is_session_valid", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_store_refresh_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_is_refresh_token_valid", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_revoke_refresh_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_create_access_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_create_refresh_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_decode_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_current_user_by_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_current_user", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "Line truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 448}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 437}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 275}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 131}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 127}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 122}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 87}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 84}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 71}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 69}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 65}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 65}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 48}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 31}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 29}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 23}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 20}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 17}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 15}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 14}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 13}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 13}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 13}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 13}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 12}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 12}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 11}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 10}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 10}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 10}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 10}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 10}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 10}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 10}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 10}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 10}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 10}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 9}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 9}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 9}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 9}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 8}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 7}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 4}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 4}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 3}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 2}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 2}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 1}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 1}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 1}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 1}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 1}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 1}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 1}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 1}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 1}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 1}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 1}];
|
||||
const RAW_NODES = [{"id": "init_py", "label": "__init__.py", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 46, "community_name": "Community 46", "source_file": "__init__.py", "file_type": "code", "degree": 0}, {"id": "main_py", "label": "main.py", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "main.py", "community": 5, "community_name": "Community 5", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_lifespan", "label": "lifespan()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "lifespan()", "community": 5, "community_name": "Community 5", "source_file": "main.py", "file_type": "code", "degree": 4}, {"id": "app_main_mask_secret", "label": "_mask_secret()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "_mask_secret()", "community": 5, "community_name": "Community 5", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_root", "label": "root()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "root()", "community": 5, "community_name": "Community 5", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "app_main_health_check", "label": "health_check()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "health_check()", "community": 5, "community_name": "Community 5", "source_file": "main.py", "file_type": "code", "degree": 1}, {"id": "tasks_init_py", "label": "__init__.py", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "__init__.py", "community": 5, "community_name": "Community 5", "source_file": "tasks/__init__.py", "file_type": "code", "degree": 2}, {"id": "tasks_init_init_scheduled_tasks", "label": "init_scheduled_tasks()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "init_scheduled_tasks()", "community": 5, "community_name": "Community 5", "source_file": "tasks/__init__.py", "file_type": "code", "degree": 2}, {"id": "tasks_init_shutdown_scheduler", "label": "shutdown_scheduler()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "shutdown_scheduler()", "community": 5, "community_name": "Community 5", "source_file": "tasks/__init__.py", "file_type": "code", "degree": 2}, {"id": "core_auth_py", "label": "auth.py", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 11.4, "font": {"size": 0, "color": "#ffffff"}, "title": "auth.py", "community": 5, "community_name": "Community 5", "source_file": "core/auth.py", "file_type": "code", "degree": 18}, {"id": "core_auth_redis_session_enabled", "label": "_redis_session_enabled()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "_redis_session_enabled()", "community": 5, "community_name": "Community 5", "source_file": "core/auth.py", "file_type": "code", "degree": 2}, {"id": "core_auth_get_redis_client", "label": "_get_redis_client()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.7, "font": {"size": 0, "color": "#ffffff"}, "title": "_get_redis_client()", "community": 5, "community_name": "Community 5", "source_file": "core/auth.py", "file_type": "code", "degree": 9}, {"id": "core_auth_session_key", "label": "_session_key()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "_session_key()", "community": 5, "community_name": "Community 5", "source_file": "core/auth.py", "file_type": "code", "degree": 5}, {"id": "core_auth_refresh_key", "label": "_refresh_key()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "boLine truncated
|
||||
const RAW_EDGES = [{"from": "main_py", "to": "app_main_lifespan", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_mask_secret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_root", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_py", "to": "app_main_health_check", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_main_lifespan", "to": "core_login_crypto_ensure_initialized", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_init_scheduled_tasks", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "app_main_lifespan", "to": "tasks_init_shutdown_scheduler", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "tasks_init_py", "to": "tasks_init_init_scheduled_tasks", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "tasks_init_py", "to": "tasks_init_shutdown_scheduler", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_redis_session_enabled", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_redis_client", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_session_key", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_refresh_key", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_store_session", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_revoke_session", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_extend_session", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_is_session_valid", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_store_refresh_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_is_refresh_token_valid", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_revoke_refresh_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_create_access_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_create_refresh_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_decode_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_current_user_by_token", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "to": "core_auth_get_current_user", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_py", "Line truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 454}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 428}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 277}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 207}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 165}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 128}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 102}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 68}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 68}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 65}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 61}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 49}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 31}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 31}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 21}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 20}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 19}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 15}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 15}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 14}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 14}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 13}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 13}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 12}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 12}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 11}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 10}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 10}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 10}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 10}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 10}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 10}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 10}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 10}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 10}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 9}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 9}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 9}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 9}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 8}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 7}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 4}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 4}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 3}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 2}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 2}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 1}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 1}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 1}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 1}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 1}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 1}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 1}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 1}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 1}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 1}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 1}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 1}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 0}];
|
||||
|
||||
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
|
||||
function esc(s) {
|
||||
|
||||
+6997
-4855
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/__init__.py": 1778578257.989357,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/main.py": 1779230556.547043,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/main.py": 1779491848.22292,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/tasks/__init__.py": 1778579841.437791,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/auth.py": 1779245791.6811426,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/api_crypto.py": 1778578258.039314,
|
||||
@@ -25,11 +25,12 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/core/wechat_message.py": 1778578258.0458734,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/material.py": 1779283385.245847,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_param.py": 1779022399.0175831,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py": 1779438524.3441086,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/models.py": 1779491653.3192608,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/__init__.py": 1779418058.876012,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/research_donation.py": 1779177348.5945773,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/qc_material.py": 1779284411.5179992,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/models/base.py": 1778578258.0821667,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/storage_release_review.py": 1779491680.5729434,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/process_route.py": 1779367496.1718137,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/auth.py": 1778578258.0939043,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/delivery.py": 1778578258.0949655,
|
||||
@@ -49,7 +50,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/order.py": 1778578258.0960286,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/__init__.py": 1778578258.0864499,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/order_adjust.py": 1778578258.0963318,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py": 1779341969.2484393,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/cell_product.py": 1779505698.9461272,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/menu.py": 1778578258.0957444,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/attachment.py": 1778763456.6244864,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/category_parameter.py": 1778578258.0944862,
|
||||
@@ -60,6 +61,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/product_review.py": 1778578258.098316,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/category.py": 1778578258.0941947,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/schemas/leave.py": 1778666088.7680702,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_release_review.py": 1779491764.493821,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_task.py": 1779332373.211639,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_route.py": 1779367598.0383394,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_code.py": 1779279460.690872,
|
||||
@@ -76,7 +78,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/tenant.py": 1779265611.0532296,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/consumable.py": 1779265707.1662717,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sop_document.py": 1779265721.9928973,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py": 1779438653.4400022,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_task.py": 1779506354.67238,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_third_party_item.py": 1779265661.482352,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/reagent.py": 1779265718.524432,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol.py": 1779329214.3583212,
|
||||
@@ -102,7 +104,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/pilot_service.py": 1779310971.8304002,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/customer.py": 1778733031.447253,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/workflow.py": 1779265993.677624,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py": 1779265759.1550276,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception.py": 1779491879.266598,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/consumable_pack.py": 1779265715.416005,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_material.py": 1779284415.64655,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/collection_protocol_no.py": 1779279043.1571646,
|
||||
@@ -115,12 +117,13 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/leave.py": 1779265839.500242,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/output_type.py": 1779265703.7363207,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/crud/exhibition_appointment.py": 1778801075.5211918,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/storage_release_review.py": 1779491799.0764315,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/public.py": 1779313409.0685387,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/material.py": 1779283386.9305117,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/qc.py": 1779332040.5250597,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/collection_protocol.py": 1779265813.1605325,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/basic_info.py": 1779280842.8447173,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py": 1779230551.0712175,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/__init__.py": 1779491831.2748232,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/operations.py": 1779418318.1375492,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/menu.py": 1779263225.4964793,
|
||||
"/Users/wuzilong/work/00code/zkxw/backend/app/api/admin.py": 1779265690.8423955,
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
-- ============================================================
|
||||
-- 入库放行审核 biz_storage_release_review
|
||||
-- Date: 2026-05-23
|
||||
-- ============================================================
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for biz_storage_release_review
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `biz_storage_release_review`;
|
||||
CREATE TABLE `biz_storage_release_review` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键ID',
|
||||
`sample_code` VARCHAR(64) NOT NULL COMMENT '样本编码(关联键)',
|
||||
`reception_source` VARCHAR(32) NOT NULL COMMENT '来源:cell_storage=采集/research_donation=捐赠',
|
||||
`protocol_id` VARCHAR(64) DEFAULT NULL COMMENT '协议单ID',
|
||||
`reception_id` VARCHAR(64) DEFAULT NULL COMMENT '样本接收记录ID',
|
||||
`protocol_no` VARCHAR(64) DEFAULT NULL COMMENT '协议编号',
|
||||
`sample_name` VARCHAR(128) DEFAULT NULL COMMENT '样本名称',
|
||||
`review_conclusion` VARCHAR(16) DEFAULT NULL COMMENT '审核结论:tg=通过/btg=不通过/NULL=待审核',
|
||||
`review_remark` VARCHAR(512) DEFAULT NULL COMMENT '审核备注',
|
||||
`review_attachments` VARCHAR(4000) DEFAULT NULL COMMENT '审核附件(逗号分隔URL)',
|
||||
`review_by` VARCHAR(64) DEFAULT NULL COMMENT '审核人ID',
|
||||
`review_by_name` VARCHAR(64) DEFAULT NULL COMMENT '审核人姓名',
|
||||
`review_time` DATETIME DEFAULT NULL COMMENT '审核时间',
|
||||
`storage_status` VARCHAR(16) NOT NULL DEFAULT 'drk' COMMENT '入库状态:drk=待入库/yrk=已入库',
|
||||
`storage_by` VARCHAR(64) DEFAULT NULL COMMENT '入库操作人ID',
|
||||
`storage_by_name` VARCHAR(64) DEFAULT NULL COMMENT '入库操作人姓名',
|
||||
`storage_time` DATETIME DEFAULT NULL COMMENT '入库时间',
|
||||
`tenant_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '租户编号',
|
||||
`order_num` INT(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` VARCHAR(512) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` VARCHAR(64) DEFAULT NULL COMMENT '创建人ID',
|
||||
`create_dept_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新人ID',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` TINYINT(4) NOT NULL DEFAULT 0 COMMENT '删除标记:0-正常 1-已删除',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_srr_sample` (`tenant_no`, `sample_code`, `del_flag`),
|
||||
KEY `idx_srr_protocol` (`tenant_no`, `protocol_id`, `del_flag`),
|
||||
KEY `idx_srr_status` (`tenant_no`, `review_conclusion`, `storage_status`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入库放行审核';
|
||||
|
||||
-- ----------------------------
|
||||
-- Menu entry: 入库放行审核 (under 质量检测)
|
||||
-- ----------------------------
|
||||
-- 父菜单: 质量检测 id=70000000000000000000000000000001
|
||||
-- 新菜单 ID: 70000000000000000000000000000011 (递增)
|
||||
INSERT INTO `sys_menu` (`id`, `menu_name`, `parent_id`, `menu_type`, `path`, `component`, `perms`, `is_frame`, `visible`, `status`, `tenant_no`, `order_num`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag`)
|
||||
VALUES ('70000000000000000000000000000011', '入库放行审核', '70000000000000000000000000000001', 'C', 'storage-release-review', '/quality-inspection/storage-release-review/index', 'biz:storageReleaseReview:list', 1, '1', '1', 'zkxw', 10, NULL, NULL, '', NOW(), NULL, NOW(), 0);
|
||||
@@ -0,0 +1,843 @@
|
||||
# 入库放行审核 实施计划
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 在质检任务菜单下新增"入库放行审核"功能,实现样本级别的放行审核和入库操作。
|
||||
|
||||
**Architecture:** 新建独立审核表 `biz_storage_release_review`,在样本接收完成时自动创建审核记录。后端新建完整的 Model → Schema → CRUD → API 路由链路。前端参照 `protocol-audit` 模式构建列表页 + 查看弹窗 + 审核弹窗。
|
||||
|
||||
**Tech Stack:** FastAPI + SQLAlchemy + Pydantic v2(后端),Vue 3 + Element Plus + TypeScript(前端)
|
||||
|
||||
**Design Spec:** `docs/superpowers/specs/2026-05-23-storage-release-review-design.md`
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
| 操作 | 文件路径 | 职责 |
|
||||
|------|----------|------|
|
||||
| 新增 | `docs/sql/biz_storage_release_review.sql` | DDL + 菜单 SQL |
|
||||
| 修改 | `backend/app/models/models.py` | 新增 BizStorageReleaseReview 模型 |
|
||||
| 新增 | `backend/app/schemas/storage_release_review.py` | Pydantic schemas |
|
||||
| 新增 | `backend/app/crud/storage_release_review.py` | CRUD + 聚合查询 |
|
||||
| 新增 | `backend/app/api/storage_release_review.py` | API 路由 |
|
||||
| 修改 | `backend/app/api/__init__.py` | 注册新路由 |
|
||||
| 修改 | `backend/app/main.py` | 注册新路由 |
|
||||
| 修改 | `backend/app/crud/sample_reception.py` | 接收完成时自动创建审核记录 |
|
||||
| 新增 | `web-admin/src/api/storage-release-review.ts` | 前端 API 模块 |
|
||||
| 新增 | `web-admin/src/views/quality-inspection/storage-release-review/index.vue` | 列表页 |
|
||||
| 新增 | `web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue` | 查看弹窗 |
|
||||
| 新增 | `web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue` | 审核弹窗 |
|
||||
| 修改 | `web-admin/src/router/modules/sqxp.ts` | 添加路由 |
|
||||
| 修改 | `web-admin/src/utils/status-tag-registry.ts` | 注册状态标签 |
|
||||
|
||||
---
|
||||
|
||||
## Task 1: SQL DDL + 菜单 SQL
|
||||
|
||||
**Files:**
|
||||
- Create: `docs/sql/biz_storage_release_review.sql`
|
||||
|
||||
- [ ] **Step 1: 创建 SQL 文件**
|
||||
|
||||
创建 `docs/sql/biz_storage_release_review.sql`,包含建表 DDL 和菜单插入语句:
|
||||
|
||||
```sql
|
||||
-- 入库放行审核表
|
||||
CREATE TABLE `biz_storage_release_review` (
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键ID',
|
||||
`sample_code` VARCHAR(64) NOT NULL COMMENT '样本编码(关联键)',
|
||||
`reception_source` VARCHAR(32) NOT NULL COMMENT '来源:cell_storage=采集/research_donation=捐赠',
|
||||
`protocol_id` VARCHAR(64) DEFAULT NULL COMMENT '协议单ID',
|
||||
`reception_id` VARCHAR(64) DEFAULT NULL COMMENT '样本接收记录ID',
|
||||
`protocol_no` VARCHAR(64) DEFAULT NULL COMMENT '协议编号',
|
||||
`sample_name` VARCHAR(128) DEFAULT NULL COMMENT '样本名称',
|
||||
`review_conclusion` VARCHAR(16) DEFAULT NULL COMMENT '审核结论:tg=通过/btg=不通过/NULL=待审核',
|
||||
`review_remark` VARCHAR(512) DEFAULT NULL COMMENT '审核备注',
|
||||
`review_attachments` VARCHAR(4000) DEFAULT NULL COMMENT '审核附件(逗号分隔URL)',
|
||||
`review_by` VARCHAR(64) DEFAULT NULL COMMENT '审核人ID',
|
||||
`review_by_name` VARCHAR(64) DEFAULT NULL COMMENT '审核人姓名',
|
||||
`review_time` DATETIME DEFAULT NULL COMMENT '审核时间',
|
||||
`storage_status` VARCHAR(16) NOT NULL DEFAULT 'drk' COMMENT '入库状态:drk=待入库/yrk=已入库',
|
||||
`storage_by` VARCHAR(64) DEFAULT NULL COMMENT '入库操作人ID',
|
||||
`storage_by_name` VARCHAR(64) DEFAULT NULL COMMENT '入库操作人姓名',
|
||||
`storage_time` DATETIME DEFAULT NULL COMMENT '入库时间',
|
||||
`tenant_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '租户编号',
|
||||
`order_num` INT(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` VARCHAR(512) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` VARCHAR(64) DEFAULT NULL COMMENT '创建人ID',
|
||||
`create_dept_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新人ID',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` TINYINT(4) NOT NULL DEFAULT 0 COMMENT '删除标记:0-正常 1-已删除',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_srr_sample` (`tenant_no`, `sample_code`, `del_flag`),
|
||||
KEY `idx_srr_protocol` (`tenant_no`, `protocol_id`, `del_flag`),
|
||||
KEY `idx_srr_status` (`tenant_no`, `review_conclusion`, `storage_status`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入库放行审核';
|
||||
```
|
||||
|
||||
菜单 SQL 需要在 `sys_menu` 表中插入一条记录,参照现有质检任务子菜单的 ID 规则(如 `70000000000000000000000000000008` 为质检任务)。新菜单 ID 使用 `70000000000000000000000000000011`(递增),parent_id 为质量检测节点 `70000000000000000000000000000001`。
|
||||
|
||||
- [ ] **Step 2: 在数据库中执行建表语句**
|
||||
|
||||
在开发环境的 MySQL 中执行 DDL,确认表创建成功。
|
||||
|
||||
- [ ] **Step 3: 验证**
|
||||
|
||||
Run: 检查表是否创建成功
|
||||
```sql
|
||||
SHOW CREATE TABLE biz_storage_release_review;
|
||||
```
|
||||
Expected: 表结构正确,索引存在
|
||||
|
||||
---
|
||||
|
||||
## Task 2: 后端 Model(SQLAlchemy)
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/app/models/models.py`
|
||||
|
||||
**模板参照:** 同文件中 `BizProtocolAudit` 类(line 743-761)
|
||||
|
||||
- [ ] **Step 1: 在 models.py 中新增 BizStorageReleaseReview 模型**
|
||||
|
||||
在 `BizProtocolAudit` 类定义之后(约 line 762)添加:
|
||||
|
||||
```python
|
||||
class BizStorageReleaseReview(Base):
|
||||
"""入库放行审核 biz_storage_release_review"""
|
||||
|
||||
__tablename__ = "biz_storage_release_review"
|
||||
|
||||
id = Column(String(64), primary_key=True, comment="主键ID")
|
||||
sample_code = Column(String(64), nullable=False, comment="样本编码")
|
||||
reception_source = Column(String(32), nullable=False, comment="来源:cell_storage/research_donation")
|
||||
protocol_id = Column(String(64), nullable=True, comment="协议单ID")
|
||||
reception_id = Column(String(64), nullable=True, comment="样本接收记录ID")
|
||||
protocol_no = Column(String(64), nullable=True, comment="协议编号")
|
||||
sample_name = Column(String(128), nullable=True, comment="样本名称")
|
||||
review_conclusion = Column(String(16), nullable=True, comment="审核结论:tg/btg/NULL")
|
||||
review_remark = Column(String(512), nullable=True, comment="审核备注")
|
||||
review_attachments = Column(String(4000), nullable=True, comment="审核附件(逗号分隔URL)")
|
||||
review_by = Column(String(64), nullable=True, comment="审核人ID")
|
||||
review_by_name = Column(String(64), nullable=True, comment="审核人姓名")
|
||||
review_time = Column(DateTime, nullable=True, comment="审核时间")
|
||||
storage_status = Column(String(16), nullable=False, default="drk", comment="入库状态:drk/yrk")
|
||||
storage_by = Column(String(64), nullable=True, comment="入库操作人ID")
|
||||
storage_by_name = Column(String(64), nullable=True, comment="入库操作人姓名")
|
||||
storage_time = Column(DateTime, nullable=True, comment="入库时间")
|
||||
tenant_no = Column(String(64), nullable=False, default="", comment="租户编号")
|
||||
order_num = Column(Integer, nullable=True, comment="排序号")
|
||||
remark = Column(String(512), nullable=True, comment="备注")
|
||||
create_by = Column(String(64), nullable=True, comment="创建人ID")
|
||||
create_dept_id = Column(String(64), nullable=False, default="", comment="创建人部门ID")
|
||||
create_time = Column(DateTime, nullable=False, default=datetime.now, comment="创建时间")
|
||||
update_by = Column(String(64), nullable=True, comment="更新人ID")
|
||||
update_time = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
del_flag = Column(SmallInteger, nullable=False, default=0, comment="删除标记")
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证无语法错误**
|
||||
|
||||
Run: `cd backend && python -c "from app.models.models import BizStorageReleaseReview; print('OK')"`
|
||||
Expected: `OK`
|
||||
|
||||
---
|
||||
|
||||
## Task 3: 后端 Schema(Pydantic)
|
||||
|
||||
**Files:**
|
||||
- Create: `backend/app/schemas/storage_release_review.py`
|
||||
|
||||
**模板参照:** 同目录下的其他 schema 文件,如 `order.py`(含 Base/Create/Update/Response 模式)
|
||||
|
||||
- [ ] **Step 1: 创建 Schema 文件**
|
||||
|
||||
创建 `backend/app/schemas/storage_release_review.py`,包含:
|
||||
|
||||
```python
|
||||
"""入库放行审核 Pydantic schemas"""
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class StorageReleaseReviewBase(BaseModel):
|
||||
sample_code: str = Field(..., description="样本编码")
|
||||
reception_source: str = Field(..., description="来源:cell_storage/research_donation")
|
||||
protocol_id: Optional[str] = Field(None, description="协议单ID")
|
||||
reception_id: Optional[str] = Field(None, description="样本接收记录ID")
|
||||
protocol_no: Optional[str] = Field(None, description="协议编号")
|
||||
sample_name: Optional[str] = Field(None, description="样本名称")
|
||||
|
||||
|
||||
class StorageReleaseReviewListQuery(BaseModel):
|
||||
"""列表查询参数"""
|
||||
page: int = Field(1, ge=1)
|
||||
pageSize: int = Field(20, ge=1, le=200)
|
||||
reception_source: Optional[str] = Field(None, description="来源筛选")
|
||||
review_conclusion: Optional[str] = Field(None, description="审核结论筛选")
|
||||
storage_status: Optional[str] = Field(None, description="入库状态筛选")
|
||||
keyword: Optional[str] = Field(None, description="关键词搜索")
|
||||
|
||||
|
||||
class ReviewSubmitBody(BaseModel):
|
||||
"""审核提交请求体"""
|
||||
review_conclusion: str = Field(..., description="审核结论:tg/btg")
|
||||
review_remark: Optional[str] = Field(None, description="审核备注")
|
||||
review_attachments: Optional[str] = Field(None, description="审核附件(逗号分隔URL)")
|
||||
|
||||
|
||||
class BatchReviewBody(BaseModel):
|
||||
"""批量审核请求体"""
|
||||
ids: List[str] = Field(..., description="审核记录ID列表")
|
||||
review_conclusion: str = Field(..., description="审核结论:tg/btg")
|
||||
review_remark: Optional[str] = Field(None, description="审核备注")
|
||||
review_attachments: Optional[str] = Field(None, description="审核附件(逗号分隔URL)")
|
||||
|
||||
|
||||
class BatchStoreBody(BaseModel):
|
||||
"""批量入库请求体"""
|
||||
ids: List[str] = Field(..., description="入库记录ID列表")
|
||||
|
||||
|
||||
class StorageReleaseReviewListItem(BaseModel):
|
||||
"""列表项"""
|
||||
id: str
|
||||
sample_code: str
|
||||
reception_source: str
|
||||
protocol_no: Optional[str] = None
|
||||
sample_name: Optional[str] = None
|
||||
review_conclusion: Optional[str] = None
|
||||
review_remark: Optional[str] = None
|
||||
review_attachments: Optional[str] = None
|
||||
review_by_name: Optional[str] = None
|
||||
review_time: Optional[datetime] = None
|
||||
storage_status: str = "drk"
|
||||
storage_by_name: Optional[str] = None
|
||||
storage_time: Optional[datetime] = None
|
||||
create_time: Optional[datetime] = None
|
||||
|
||||
|
||||
class StorageReleaseReviewDetail(BaseModel):
|
||||
"""详情(含聚合信息)"""
|
||||
review: Dict[str, Any] = {}
|
||||
protocol_info: Optional[Dict[str, Any]] = None
|
||||
transport_info: Optional[Dict[str, Any]] = None
|
||||
reception_info: Optional[Dict[str, Any]] = None
|
||||
qc_info: Optional[Dict[str, Any]] = None
|
||||
production_info: Optional[Dict[str, Any]] = None
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证导入无错误**
|
||||
|
||||
Run: `cd backend && python -c "from app.schemas.storage_release_review import StorageReleaseReviewBase; print('OK')"`
|
||||
Expected: `OK`
|
||||
|
||||
---
|
||||
|
||||
## Task 4: 后端 CRUD
|
||||
|
||||
**Files:**
|
||||
- Create: `backend/app/crud/storage_release_review.py`
|
||||
|
||||
**模板参照:**
|
||||
- `backend/app/crud/qc_work_order.py` — 跨表聚合查询模式(含 `_RECEPTION_SOURCE_LABELS`、`urls_to_attachment_list`)
|
||||
- `backend/app/crud/sample_reception.py` — 租户隔离 + 分页模式(`_page_payload`)
|
||||
|
||||
- [ ] **Step 1: 创建 CRUD 文件**
|
||||
|
||||
创建 `backend/app/crud/storage_release_review.py`,实现以下函数:
|
||||
|
||||
```python
|
||||
"""入库放行审核 CRUD"""
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from sqlalchemy import and_, func, or_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
from app.models.models import (
|
||||
BizStorageReleaseReview,
|
||||
BizSampleReception,
|
||||
BizCollectionProtocol,
|
||||
BizProtocolSample,
|
||||
BizProtocolSampleTransport,
|
||||
BizProtocolPartyCustomer,
|
||||
BizQcWorkOrder,
|
||||
BizQcTask,
|
||||
BizProductionWorkOrder,
|
||||
BizProductionTask,
|
||||
SysUser,
|
||||
)
|
||||
from app.models.research_donation import (
|
||||
BizResearchDonation,
|
||||
BizResearchDonationSample,
|
||||
BizResearchDonationSampleTransport,
|
||||
BizResearchDonationParty,
|
||||
)
|
||||
```
|
||||
|
||||
**核心函数清单:**
|
||||
|
||||
1. `create_review_from_reception(db, *, reception, protocol_no, sample_name, tenant_no, user_id, create_dept_id) -> BizStorageReleaseReview`
|
||||
- 在样本接收完成时调用
|
||||
- INSERT 一条新记录,review_conclusion=NULL, storage_status='drk'
|
||||
- 冗余写入 sample_code, reception_source, protocol_id, reception_id, protocol_no, sample_name
|
||||
|
||||
2. `list_reviews(db, *, tenant_no, page, page_size, reception_source=None, review_conclusion=None, storage_status=None, keyword=None) -> dict`
|
||||
- 分页查询 `biz_storage_release_review`
|
||||
- 支持筛选和关键词搜索(protocol_no / sample_code / sample_name)
|
||||
- 默认排序 create_time DESC
|
||||
- 返回 `{list, total, page, pageSize, totalPages}` 格式
|
||||
|
||||
3. `get_review_detail(db, review_id, tenant_no) -> dict`
|
||||
- 聚合查询全链路信息
|
||||
- 根据 reception_source JOIN 不同的协议表和运输表
|
||||
- JOIN sample_reception 获取接收信息
|
||||
- JOIN qc_work_order + qc_task 获取质检信息
|
||||
- JOIN production_work_order + production_task 获取生产信息
|
||||
- 返回结构: `{review, protocol_info, transport_info, reception_info, qc_info, production_info}`
|
||||
|
||||
4. `submit_review(db, review_id, *, review_conclusion, review_remark, review_attachments, tenant_no, user_id) -> dict`
|
||||
- 验证记录存在 + 租户隔离
|
||||
- UPDATE review_conclusion, review_remark, review_attachments, review_by, review_by_name, review_time
|
||||
- 支持反复修改
|
||||
|
||||
5. `batch_submit_review(db, ids, *, review_conclusion, review_remark, review_attachments, tenant_no, user_id) -> dict`
|
||||
- 批量 UPDATE,逐条处理
|
||||
- 返回成功数量
|
||||
|
||||
6. `store_sample(db, review_id, *, tenant_no, user_id) -> dict`
|
||||
- 前置条件:review_conclusion='tg' AND storage_status='drk'
|
||||
- UPDATE storage_status='yrk', storage_by, storage_by_name, storage_time
|
||||
- 同步 UPDATE biz_protocol_sample.sample_status='yrk'(仅采集协议,通过 sample_code + tenant_no 关联)
|
||||
|
||||
7. `batch_store_samples(db, ids, *, tenant_no, user_id) -> dict`
|
||||
- 批量入库,逐条检查前置条件
|
||||
- 返回成功/失败数量
|
||||
|
||||
**辅助函数:**
|
||||
|
||||
```python
|
||||
_RECEPTION_SOURCE_LABELS = {"cell_storage": "采集协议", "research_donation": "捐赠协议"}
|
||||
_CONCLUSION_LABELS = {"tg": "通过", "btg": "不通过"}
|
||||
_STORAGE_STATUS_LABELS = {"drk": "待入库", "yrk": "已入库"}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证导入无错误**
|
||||
|
||||
Run: `cd backend && python -c "from app.crud.storage_release_review import list_reviews; print('OK')"`
|
||||
Expected: `OK`
|
||||
|
||||
---
|
||||
|
||||
## Task 5: 后端 API 路由
|
||||
|
||||
**Files:**
|
||||
- Create: `backend/app/api/storage_release_review.py`
|
||||
- Modify: `backend/app/api/__init__.py`
|
||||
- Modify: `backend/app/main.py`
|
||||
|
||||
**模板参照:** `backend/app/api/sample_reception.py` — 路由定义模式、`_get_tenant_no`、`_get_current_user_id`、`success_response`、`error_response`、`_page_payload`
|
||||
|
||||
- [ ] **Step 1: 创建 API 路由文件**
|
||||
|
||||
创建 `backend/app/api/storage_release_review.py`,参照 `sample_reception.py` 的模式:
|
||||
|
||||
```python
|
||||
"""入库放行审核 API"""
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.auth import require_admin_or_super_admin
|
||||
from app.schemas.storage_release_review import (
|
||||
ReviewSubmitBody,
|
||||
BatchReviewBody,
|
||||
BatchStoreBody,
|
||||
)
|
||||
from app.crud import storage_release_review as svc
|
||||
# ... 导入辅助函数
|
||||
|
||||
router = APIRouter(prefix="/storage-release-reviews", tags=["入库放行审核"])
|
||||
```
|
||||
|
||||
**端点清单:**
|
||||
|
||||
```python
|
||||
@router.get("")
|
||||
async def list_storage_release_reviews(
|
||||
page: int = Query(1), pageSize: int = Query(20),
|
||||
reception_source: str = Query(None),
|
||||
review_conclusion: str = Query(None),
|
||||
storage_status: str = Query(None),
|
||||
keyword: str = Query(None),
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
"""分页列表"""
|
||||
...
|
||||
|
||||
@router.get("/{review_id}/detail")
|
||||
async def get_storage_release_review_detail(
|
||||
review_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
"""详情(聚合全链路)"""
|
||||
...
|
||||
|
||||
@router.post("/{review_id}/review")
|
||||
async def submit_review(
|
||||
review_id: str, body: ReviewSubmitBody,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
"""提交/修改审核"""
|
||||
...
|
||||
|
||||
@router.post("/batch-review")
|
||||
async def batch_review(
|
||||
body: BatchReviewBody,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
"""批量审核"""
|
||||
...
|
||||
|
||||
@router.post("/{review_id}/store")
|
||||
async def store_sample(
|
||||
review_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
"""样本入库"""
|
||||
...
|
||||
|
||||
@router.post("/batch-store")
|
||||
async def batch_store(
|
||||
body: BatchStoreBody,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_admin_or_super_admin),
|
||||
):
|
||||
"""批量入库"""
|
||||
...
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 注册路由**
|
||||
|
||||
修改 `backend/app/api/__init__.py`:
|
||||
```python
|
||||
from app.api.storage_release_review import router as storage_release_review_router
|
||||
# 添加到 __all__ 列表
|
||||
```
|
||||
|
||||
修改 `backend/app/main.py`:
|
||||
```python
|
||||
# 导入行添加
|
||||
from app.api import ..., storage_release_review_router
|
||||
# 注册行添加
|
||||
app.include_router(storage_release_review_router, prefix=settings.API_V1_PREFIX)
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 验证路由注册成功**
|
||||
|
||||
Run: `cd backend && python -c "from app.main import app; routes = [r.path for r in app.routes if hasattr(r, 'path')]; print([r for r in routes if 'storage' in r])"`
|
||||
Expected: 包含 `/api/storage-release-reviews` 相关路径
|
||||
|
||||
- [ ] **Step 4: 验证 Swagger 文档**
|
||||
|
||||
启动后端服务,访问 `/docs`,确认"入库放行审核"分组下有 6 个端点。
|
||||
|
||||
---
|
||||
|
||||
## Task 6: 后端触发器 — 样本接收时自动创建审核记录
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/app/crud/sample_reception.py`
|
||||
|
||||
**修改位置:** `create_sample_reception` 函数中,`conclusion == "js"` 分支内(约 line 784-802),在创建 QC 工单和生产工单之后添加审核记录创建逻辑。
|
||||
|
||||
- [ ] **Step 1: 在 sample_reception.py 的 create_sample_reception 函数中添加触发逻辑**
|
||||
|
||||
在 line 802(`prod_wo_svc.create_production_work_order_from_reception(...)` 之后)添加:
|
||||
|
||||
```python
|
||||
# 自动创建入库放行审核记录
|
||||
from app.crud import storage_release_review as srr_svc
|
||||
|
||||
# 获取协议编号和样本名称
|
||||
_protocol_no = ""
|
||||
_sample_name = ""
|
||||
if source == "cell_storage":
|
||||
cp = (
|
||||
db.query(BizCollectionProtocol)
|
||||
.filter(BizCollectionProtocol.id == collection_protocol_id)
|
||||
.first()
|
||||
)
|
||||
if cp:
|
||||
_protocol_no = cp.protocol_no or ""
|
||||
if sample:
|
||||
_sample_name = sample.sample_name or sample.sample_code or ""
|
||||
else:
|
||||
rd = (
|
||||
db.query(BizResearchDonation)
|
||||
.filter(BizResearchDonation.id == _research_donation_id)
|
||||
.first()
|
||||
)
|
||||
if rd:
|
||||
_protocol_no = rd.protocol_no or ""
|
||||
if dsample:
|
||||
_sample_name = dsample.sample_name or dsample.sample_code or ""
|
||||
|
||||
srr_svc.create_review_from_reception(
|
||||
db,
|
||||
reception=obj,
|
||||
protocol_no=_protocol_no,
|
||||
sample_name=_sample_name,
|
||||
tenant_no=tenant_no,
|
||||
user_id=user_id,
|
||||
create_dept_id=create_dept_id or "",
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证**
|
||||
|
||||
Run: `cd backend && python -c "from app.crud.sample_reception import create_sample_reception; print('OK')"`
|
||||
Expected: `OK`
|
||||
|
||||
---
|
||||
|
||||
## Task 7: 前端 API 模块
|
||||
|
||||
**Files:**
|
||||
- Create: `web-admin/src/api/storage-release-review.ts`
|
||||
|
||||
**模板参照:** `web-admin/src/api/qc-task.ts` 或 `web-admin/src/api/collection-protocol.ts`
|
||||
|
||||
- [ ] **Step 1: 创建前端 API 文件**
|
||||
|
||||
创建 `web-admin/src/api/storage-release-review.ts`:
|
||||
|
||||
```typescript
|
||||
import request from '@/utils/request'
|
||||
|
||||
const BASE = '/api/admin/storage-release-reviews'
|
||||
|
||||
/** 分页列表 */
|
||||
export function fetchStorageReleaseReviewList(params: {
|
||||
page: number
|
||||
pageSize: number
|
||||
receptionSource?: string
|
||||
reviewConclusion?: string
|
||||
storageStatus?: string
|
||||
keyword?: string
|
||||
}) {
|
||||
return request({ url: BASE, method: 'get', params })
|
||||
}
|
||||
|
||||
/** 详情(聚合全链路) */
|
||||
export function fetchStorageReleaseReviewDetail(id: string) {
|
||||
return request({ url: `${BASE}/${id}/detail`, method: 'get' })
|
||||
}
|
||||
|
||||
/** 提交/修改审核 */
|
||||
export function submitStorageReleaseReview(
|
||||
id: string,
|
||||
data: {
|
||||
reviewConclusion: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
},
|
||||
) {
|
||||
return request({ url: `${BASE}/${id}/review`, method: 'post', data })
|
||||
}
|
||||
|
||||
/** 批量审核 */
|
||||
export function batchReviewStorageRelease(data: {
|
||||
ids: string[]
|
||||
reviewConclusion: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
}) {
|
||||
return request({ url: `${BASE}/batch-review`, method: 'post', data })
|
||||
}
|
||||
|
||||
/** 样本入库 */
|
||||
export function storeSample(id: string) {
|
||||
return request({ url: `${BASE}/${id}/store`, method: 'post' })
|
||||
}
|
||||
|
||||
/** 批量入库 */
|
||||
export function batchStoreSample(data: { ids: string[] }) {
|
||||
return request({ url: `${BASE}/batch-store`, method: 'post', data })
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 8: 前端状态标签注册
|
||||
|
||||
**Files:**
|
||||
- Modify: `web-admin/src/utils/status-tag-registry.ts`
|
||||
|
||||
- [ ] **Step 1: 添加入库放行审核的状态标签**
|
||||
|
||||
在 `status-tag-registry.ts` 中新增:
|
||||
|
||||
```typescript
|
||||
// 入库放行审核结论
|
||||
storageReleaseConclusion: {
|
||||
tg: { label: '通过', type: 'success' },
|
||||
btg: { label: '不通过', type: 'danger' },
|
||||
} as Record<string, { label: string; type: string }>,
|
||||
|
||||
// 入库状态
|
||||
storageStatus: {
|
||||
drk: { label: '待入库', type: 'warning' },
|
||||
yrk: { label: '已入库', type: 'success' },
|
||||
} as Record<string, { label: string; type: string }>,
|
||||
```
|
||||
|
||||
同时添加协议类别标签:
|
||||
|
||||
```typescript
|
||||
// 协议类别
|
||||
receptionSource: {
|
||||
cell_storage: { label: '采集协议', type: 'primary' },
|
||||
research_donation: { label: '捐赠协议', type: 'success' },
|
||||
} as Record<string, { label: string; type: string }>,
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 9: 前端列表页
|
||||
|
||||
**Files:**
|
||||
- Create: `web-admin/src/views/quality-inspection/storage-release-review/index.vue`
|
||||
- Modify: `web-admin/src/router/modules/sqxp.ts`
|
||||
|
||||
**模板参照(严格遵循):**
|
||||
- `web-admin/src/views/cell-storage/protocol-audit/index.vue` — 列表页结构(筛选 + 多选 + 批量审核工具栏)
|
||||
- `web-admin/src/views/quality-inspection/qc-task/index.vue` — 质检任务列表(状态标签、操作按钮条件显示)
|
||||
- `web-admin/src/views/system/unit/index.vue` — 标准列表页模板
|
||||
|
||||
**UI 规范约束(来自 AGENTS.md):**
|
||||
- 列表面板标题用 `panel-top` + `h2.title-bar-heading`,显示绿色竖线
|
||||
- 工具栏:"已选 N 项"在前,然后主操作按钮(只有一个 `type="primary"`),不用 `space-between`
|
||||
- 表格操作列 `fixed="right"`,`min-width >= 260px`
|
||||
- 状态字段用 `<el-tag>` + 语义颜色
|
||||
- 排序字段默认不展示
|
||||
|
||||
- [ ] **Step 1: 在 sqxp.ts 中添加路由**
|
||||
|
||||
在质量检测(QualityInspection)路由组的 children 数组末尾(`qc-material-usage` 之后)添加:
|
||||
|
||||
```typescript
|
||||
{
|
||||
id: '70000000000000000000000000000011',
|
||||
name: 'QualityInspectionStorageReleaseReview',
|
||||
path: 'storage-release-review',
|
||||
component: '/quality-inspection/storage-release-review/index',
|
||||
meta: {
|
||||
title: '入库放行审核',
|
||||
keepAlive: true,
|
||||
menuCode: 'biz:storageReleaseReview:list',
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 创建列表页组件**
|
||||
|
||||
创建 `web-admin/src/views/quality-inspection/storage-release-review/index.vue`。
|
||||
|
||||
核心结构:
|
||||
|
||||
**筛选区域:**
|
||||
- 协议类别(el-select):全部 / 采集协议(cell_storage) / 捐赠协议(research_donation)
|
||||
- 审核状态(el-select):全部 / 待审核(null) / 通过(tg) / 不通过(btg)
|
||||
- 入库状态(el-select):全部 / 待入库(drk) / 已入库(yrk)
|
||||
- 搜索关键词(el-input)
|
||||
|
||||
**工具栏:**
|
||||
- 已选 N 项
|
||||
- [批量审核](type="primary")
|
||||
- [批量入库](plain)
|
||||
|
||||
**表格列(el-table):**
|
||||
- type=selection
|
||||
- 协议编号 (protocol_no, width=160)
|
||||
- 样本编码 (sample_code, width=140)
|
||||
- 样本名称 (sample_name, width=140)
|
||||
- 协议类别 (reception_source → el-tag: primary/success, width=100)
|
||||
- 审核结论 (review_conclusion → el-tag: null→info"待审核", tg→success, btg→danger, width=100)
|
||||
- 入库状态 (storage_status → el-tag: drk→warning"待入库", yrk→success"已入库", width=100)
|
||||
- 审核人 (review_by_name, width=100)
|
||||
- 审核时间 (review_time, width=170)
|
||||
- 操作 (fixed=right, min-width=260)
|
||||
- [查看] — 始终
|
||||
- [审核] — 始终
|
||||
- [样本入库] — 仅 review_conclusion==='tg' && storage_status==='drk'
|
||||
|
||||
**组件交互:**
|
||||
- 点击[查看] → 打开 `StorageReleaseViewDialog`
|
||||
- 点击[审核] → 打开 `StorageReleaseReviewDialog`
|
||||
- 点击[样本入库] → 确认弹窗 → 调用 `storeSample` API → 刷新列表
|
||||
- 批量审核 → 打开 `StorageReleaseReviewDialog`(批量模式)
|
||||
- 批量入库 → 确认弹窗 → 调用 `batchStoreSample` API → 刷新列表
|
||||
|
||||
- [ ] **Step 3: 验证页面加载**
|
||||
|
||||
启动前端 dev server,访问 `/#/quality-inspection/storage-release-review`,确认页面渲染正常,表格显示。
|
||||
|
||||
---
|
||||
|
||||
## Task 10: 前端查看弹窗
|
||||
|
||||
**Files:**
|
||||
- Create: `web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue`
|
||||
|
||||
**模板参照:**
|
||||
- `web-admin/src/components/business/qc-task-view-dialog/index.vue` — 多分区信息展示弹窗
|
||||
- `web-admin/src/components/business/sample-qc-info-cards/index.vue` — QC 信息卡片组件
|
||||
- `web-admin/src/components/business/system-view-dialog/index.vue` — 通用查看弹窗
|
||||
|
||||
**UI 规范约束:**
|
||||
- Dialog forms: `label-position="top"`
|
||||
- 无 backdrop blur (`backdrop-filter: none`)
|
||||
- 使用 `.dialog-form-layout` 布局
|
||||
|
||||
- [ ] **Step 1: 创建查看弹窗组件**
|
||||
|
||||
创建 `StorageReleaseViewDialog.vue`,props:
|
||||
```typescript
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
reviewId: string
|
||||
}>()
|
||||
const emit = defineEmits(['update:visible'])
|
||||
```
|
||||
|
||||
加载详情数据:调用 `fetchStorageReleaseReviewDetail(reviewId)`
|
||||
|
||||
**内部分区(每个分区用 `▎` 标题栏样式):**
|
||||
|
||||
1. **▎基础信息** — 协议编号、协议类别(el-tag)、样本编码、样本名称、客户姓名、联系电话
|
||||
2. **▎运输信息** — 运输方式、运输温度、运输时长、运输公司
|
||||
3. **▎接收信息** — 接收时间、接收人、接收结论(el-tag: js→success/bj→danger)、检查项表格
|
||||
4. **▎质检信息** — 使用 `SampleQcInfoCards` 或自定义卡片展示工单+任务列表
|
||||
5. **▎生产信息** — 生产工单编号、工单状态(el-tag)、任务列表(名称+状态el-tag)
|
||||
6. **▎放行审核** — 审核结论(el-tag)、审核人、审核时间、审核备注、审核附件(预览/下载)
|
||||
|
||||
所有区域只读展示。
|
||||
|
||||
底部按钮:[关闭]
|
||||
|
||||
- [ ] **Step 2: 在列表页中引入并测试**
|
||||
|
||||
在 `index.vue` 中引入 `StorageReleaseViewDialog`,点击[查看]时传入 reviewId 并打开弹窗。
|
||||
|
||||
---
|
||||
|
||||
## Task 11: 前端审核弹窗
|
||||
|
||||
**Files:**
|
||||
- Create: `web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue`
|
||||
|
||||
**模板参照:**
|
||||
- `web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue` — 审核弹窗模式(结论 radio + 备注 textarea + 批量支持)
|
||||
- `web-admin/src/views/research-donation/list/DonationAuditDialog.vue` — 捐赠审核弹窗(同模式)
|
||||
|
||||
**UI 规范约束:**
|
||||
- Dialog forms: `label-position="top"`
|
||||
- 无 backdrop blur
|
||||
- 附件上传使用卡片化布局
|
||||
|
||||
- [ ] **Step 1: 创建审核弹窗组件**
|
||||
|
||||
创建 `StorageReleaseReviewDialog.vue`,props:
|
||||
```typescript
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
reviewIds: string[] // 支持单条和批量
|
||||
}>()
|
||||
const emit = defineEmits(['update:visible', 'submitted'])
|
||||
```
|
||||
|
||||
**模式切换:**
|
||||
- `reviewIds.length === 1` → 单条模式:上方展示详情信息(同查看弹窗)+ 下方审核表单
|
||||
- `reviewIds.length > 1` → 批量模式:仅显示审核表单
|
||||
|
||||
**审核表单:**
|
||||
- 审核结论: `el-radio-group`
|
||||
- `el-radio value="tg"` 通过
|
||||
- `el-radio value="btg"` 不通过
|
||||
- 备注: `el-input type="textarea" :rows="3"`
|
||||
- 附件: 使用现有上传组件,参照 `ProtocolFinanceConfirmFields` 的模式
|
||||
- 上传触发为卡片(140×96px, 虚线边框 `#a7f3d0`)
|
||||
- inline 排列在已上传附件同一行
|
||||
- 选择文件后立即上传
|
||||
|
||||
底部按钮:[取消] [确定]
|
||||
|
||||
**提交逻辑:**
|
||||
- 单条:调用 `submitStorageReleaseReview(id, data)`
|
||||
- 批量:调用 `batchReviewStorageReview({ ids, ...data })`
|
||||
- 成功后 emit `submitted`,父组件刷新列表
|
||||
|
||||
- [ ] **Step 2: 在列表页中引入并测试**
|
||||
|
||||
在 `index.vue` 中引入 `StorageReleaseReviewDialog`,点击[审核]或[批量审核]时传入 reviewIds 并打开弹窗。
|
||||
|
||||
---
|
||||
|
||||
## Task 12: 端到端验证
|
||||
|
||||
- [ ] **Step 1: 后端 API 验证**
|
||||
|
||||
启动后端,通过 Swagger `/docs` 测试所有 6 个端点:
|
||||
1. GET 列表 — 返回分页数据
|
||||
2. GET 详情 — 返回聚合信息
|
||||
3. POST 审核 — 更新结论
|
||||
4. POST 批量审核 — 批量更新
|
||||
5. POST 入库 — 更新状态
|
||||
6. POST 批量入库 — 批量更新状态
|
||||
|
||||
- [ ] **Step 2: 前端功能验证**
|
||||
|
||||
1. 创建一个采集协议单 → 添加样本 → 样本接收(结论=接收)
|
||||
2. 确认"入库放行审核"列表中出现该样本
|
||||
3. 点击[查看] → 确认所有信息区域正确展示
|
||||
4. 点击[审核] → 填写结论和备注 → 提交 → 确认列表更新
|
||||
5. 点击[样本入库] → 确认状态变为"已入库"
|
||||
6. 测试批量审核和批量入库
|
||||
7. 测试修改已审核的记录
|
||||
|
||||
- [ ] **Step 3: 更新知识图谱**
|
||||
|
||||
Run: `./scripts/update-graph.sh all`
|
||||
Expected: 图谱增量更新完成
|
||||
|
||||
- [ ] **Step 4: 提交**
|
||||
|
||||
```bash
|
||||
git add docs/sql/biz_storage_release_review.sql
|
||||
git add backend/app/models/models.py
|
||||
git add backend/app/schemas/storage_release_review.py
|
||||
git add backend/app/crud/storage_release_review.py
|
||||
git add backend/app/api/storage_release_review.py
|
||||
git add backend/app/api/__init__.py
|
||||
git add backend/app/main.py
|
||||
git add backend/app/crud/sample_reception.py
|
||||
git add web-admin/src/api/storage-release-review.ts
|
||||
git add web-admin/src/views/quality-inspection/storage-release-review/
|
||||
git add web-admin/src/router/modules/sqxp.ts
|
||||
git add web-admin/src/utils/status-tag-registry.ts
|
||||
git commit -m "feat: add storage release review module (入库放行审核)"
|
||||
```
|
||||
@@ -0,0 +1,404 @@
|
||||
# 入库放行审核 — 设计文档
|
||||
|
||||
> 日期:2026-05-23
|
||||
> 状态:待审阅
|
||||
> 模块:质检任务 → 入库放行审核
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
### 现状
|
||||
|
||||
当前系统流程:采集协议单/捐赠协议单录入 → 样本接收 → 入库质检工单(含实验室自检和第三方检测)→ 入库生产工单 → 细胞产品入库。
|
||||
|
||||
### 需求
|
||||
|
||||
客户要求在上述流程中增加一个"入库放行审核"环节。所有采集协议单和捐赠协议单的样本,在经过接收、质检、生产后,需经过放行审核通过后才算真正通过并存入库中。
|
||||
|
||||
### 目标
|
||||
|
||||
在【质检任务】菜单下新增【入库放行审核】子菜单,提供:
|
||||
|
||||
1. 样本级别的放行审核列表(采集协议 + 捐赠协议混合展示)
|
||||
2. 查看每个样本的全链路信息(基础、运输、接收、质检、生产)
|
||||
3. 审核操作(通过/不通过 + 备注 + 附件),支持批量
|
||||
4. 样本入库操作,支持批量
|
||||
5. 审核后可继续修改结论、备注、附件
|
||||
|
||||
## 2. 需求确认记录
|
||||
|
||||
| 维度 | 决定 |
|
||||
|------|------|
|
||||
| 审核粒度 | 样本级别 — 每个样本一行,独立审核 |
|
||||
| 显示时机 | 样本接收后(accept_conclusion=js)即显示在列表中 |
|
||||
| 结论选项 | 通过 / 不通过(tg / btg) |
|
||||
| 审核后动作 | 仅记录结论;另设"样本入库"按钮单独触发入库 |
|
||||
| 入库动作 | 仅更新样本状态,不涉及库位分配 |
|
||||
| 批量操作 | 支持批量审核 + 批量入库 |
|
||||
| 审核可修改 | 审核完成后可继续修改结论、备注、附件 |
|
||||
|
||||
## 3. 数据模型
|
||||
|
||||
### 3.1 新增表 `biz_storage_release_review`
|
||||
|
||||
```sql
|
||||
CREATE TABLE `biz_storage_release_review` (
|
||||
-- 主键
|
||||
`id` VARCHAR(64) NOT NULL COMMENT '主键ID',
|
||||
|
||||
-- 业务字段
|
||||
`sample_code` VARCHAR(64) NOT NULL COMMENT '样本编码(关联键)',
|
||||
`reception_source` VARCHAR(32) NOT NULL COMMENT '来源:cell_storage=采集/research_donation=捐赠',
|
||||
`protocol_id` VARCHAR(64) DEFAULT NULL COMMENT '协议单ID(冗余加速查询)',
|
||||
`reception_id` VARCHAR(64) DEFAULT NULL COMMENT '样本接收记录ID',
|
||||
`protocol_no` VARCHAR(64) DEFAULT NULL COMMENT '协议编号(冗余展示用)',
|
||||
`sample_name` VARCHAR(128) DEFAULT NULL COMMENT '样本名称(冗余展示用)',
|
||||
|
||||
-- 审核字段
|
||||
`review_conclusion` VARCHAR(16) DEFAULT NULL COMMENT '审核结论:tg=通过/btg=不通过/NULL=待审核',
|
||||
`review_remark` VARCHAR(512) DEFAULT NULL COMMENT '审核备注',
|
||||
`review_attachments` VARCHAR(4000) DEFAULT NULL COMMENT '审核附件(逗号分隔URL)',
|
||||
`review_by` VARCHAR(64) DEFAULT NULL COMMENT '审核人ID',
|
||||
`review_by_name` VARCHAR(64) DEFAULT NULL COMMENT '审核人姓名',
|
||||
`review_time` DATETIME DEFAULT NULL COMMENT '审核时间',
|
||||
|
||||
-- 入库字段
|
||||
`storage_status` VARCHAR(16) NOT NULL DEFAULT 'drk' COMMENT '入库状态:drk=待入库/yrk=已入库',
|
||||
`storage_by` VARCHAR(64) DEFAULT NULL COMMENT '入库操作人ID',
|
||||
`storage_by_name` VARCHAR(64) DEFAULT NULL COMMENT '入库操作人姓名',
|
||||
`storage_time` DATETIME DEFAULT NULL COMMENT '入库时间',
|
||||
|
||||
-- 公共字段
|
||||
`tenant_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '租户编号',
|
||||
`order_num` INT(11) DEFAULT NULL COMMENT '排序号',
|
||||
`remark` VARCHAR(512) DEFAULT NULL COMMENT '备注',
|
||||
`create_by` VARCHAR(64) DEFAULT NULL COMMENT '创建人ID',
|
||||
`create_dept_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新人ID',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`del_flag` TINYINT(4) NOT NULL DEFAULT 0 COMMENT '删除标记:0-正常 1-已删除',
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_srr_sample` (`tenant_no`, `sample_code`, `del_flag`),
|
||||
KEY `idx_srr_protocol` (`tenant_no`, `protocol_id`, `del_flag`),
|
||||
KEY `idx_srr_status` (`tenant_no`, `review_conclusion`, `storage_status`, `del_flag`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='入库放行审核';
|
||||
```
|
||||
|
||||
### 3.2 跨表关联
|
||||
|
||||
```
|
||||
biz_storage_release_review
|
||||
├─ sample_code + tenant_no → biz_protocol_sample / biz_research_donation_sample
|
||||
├─ protocol_id → biz_collection_protocol / biz_research_donation(基础信息)
|
||||
├─ sample_code → biz_protocol_sample_transport / biz_research_donation_sample_transport(运输信息)
|
||||
├─ reception_id → biz_sample_reception + biz_sample_reception_check_result(接收信息)
|
||||
├─ sample_code → biz_qc_work_order + biz_qc_task(质检信息)
|
||||
└─ sample_code → biz_production_work_order + biz_production_task(生产信息)
|
||||
```
|
||||
|
||||
### 3.3 数据流转
|
||||
|
||||
```
|
||||
样本接收完成 (accept_conclusion=js)
|
||||
→ 自动 INSERT biz_storage_release_review (review_conclusion=NULL, storage_status=drk)
|
||||
→ 冗余写入 protocol_no, sample_name, protocol_id, reception_id, reception_source
|
||||
|
||||
审核人提交审核
|
||||
→ UPDATE review_conclusion/review_remark/review_attachments/review_by/review_by_name/review_time
|
||||
→ 可反复修改(每次 UPDATE 覆盖)
|
||||
|
||||
点击"样本入库"
|
||||
→ UPDATE storage_status=yrk, storage_by, storage_by_name, storage_time
|
||||
→ 同步 UPDATE biz_protocol_sample.sample_status = 'yrk'(仅采集协议样本)
|
||||
```
|
||||
|
||||
### 3.4 状态组合
|
||||
|
||||
| review_conclusion | storage_status | 列表标签 | 可用操作 |
|
||||
|---|---|---|---|
|
||||
| NULL | drk | 待审核 | 查看、审核 |
|
||||
| tg | drk | 审核通过 | 查看、审核(修改)、样本入库 |
|
||||
| btg | drk | 审核不通过 | 查看、审核(修改) |
|
||||
| tg | yrk | 已入库 | 查看、审核(修改) |
|
||||
| btg | yrk | 不通过-已入库 | 查看、审核(修改)— 异常状态,理论上不出现 |
|
||||
|
||||
## 4. 后端设计
|
||||
|
||||
### 4.1 新增/修改文件清单
|
||||
|
||||
| 操作 | 文件 | 职责 |
|
||||
|------|------|------|
|
||||
| 新增 | `backend/app/schemas/storage_release_review.py` | Pydantic schemas |
|
||||
| 新增 | `backend/app/crud/storage_release_review.py` | CRUD + 聚合查询 |
|
||||
| 新增 | `backend/app/api/storage_release_review.py` | API 路由 |
|
||||
| 修改 | `backend/app/models/models.py` | 新增 BizStorageReleaseReview 模型 |
|
||||
| 修改 | `backend/app/api/__init__.py` | 注册新路由 |
|
||||
| 修改 | `backend/app/main.py` | 注册新路由 |
|
||||
| 修改 | `backend/app/crud/sample_reception.py` | 样本接收完成时自动创建审核记录 |
|
||||
|
||||
### 4.2 API 端点
|
||||
|
||||
| 方法 | 路径 | 功能 |
|
||||
|------|------|------|
|
||||
| GET | `/api/admin/storage-release-reviews` | 分页列表 |
|
||||
| GET | `/api/admin/storage-release-reviews/{id}/detail` | 详情(聚合全链路) |
|
||||
| POST | `/api/admin/storage-release-reviews/{id}/review` | 提交/修改审核 |
|
||||
| POST | `/api/admin/storage-release-reviews/batch-review` | 批量审核 |
|
||||
| POST | `/api/admin/storage-release-reviews/{id}/store` | 样本入库 |
|
||||
| POST | `/api/admin/storage-release-reviews/batch-store` | 批量入库 |
|
||||
|
||||
### 4.3 列表接口
|
||||
|
||||
`GET /api/admin/storage-release-reviews`
|
||||
|
||||
请求参数:
|
||||
- `page` / `pageSize` — 分页
|
||||
- `reception_source` — 筛选协议类别(cell_storage / research_donation)
|
||||
- `review_conclusion` — 筛选审核结论(tg / btg / null)
|
||||
- `storage_status` — 筛选入库状态(drk / yrk)
|
||||
- `keyword` — 模糊搜索(协议编号、样本编码、样本名称)
|
||||
- `tenant_no` — 租户隔离(从当前用户提取)
|
||||
|
||||
返回:分页列表,默认排序 `create_time DESC`。
|
||||
|
||||
### 4.4 详情接口
|
||||
|
||||
`GET /api/admin/storage-release-reviews/{id}/detail`
|
||||
|
||||
聚合查询逻辑:
|
||||
1. 读取 `biz_storage_release_review` 记录本身
|
||||
2. 根据 `reception_source` 决定 JOIN 哪张协议主表:
|
||||
- `cell_storage` → `biz_collection_protocol` + `biz_protocol_sample` + `biz_protocol_sample_transport`
|
||||
- `research_donation` → `biz_research_donation` + `biz_research_donation_sample` + `biz_research_donation_sample_transport`
|
||||
3. JOIN `biz_sample_reception` + `biz_sample_reception_check_result`(接收信息)
|
||||
4. JOIN `biz_qc_work_order` + `biz_qc_task` + `biz_qc_task_result`(质检信息)
|
||||
5. JOIN `biz_production_work_order` + `biz_production_task`(生产信息)
|
||||
|
||||
返回结构:
|
||||
```json
|
||||
{
|
||||
"review": { "...审核记录本身" },
|
||||
"protocolInfo": { "...协议基础信息" },
|
||||
"transportInfo": { "...运输信息" },
|
||||
"receptionInfo": { "...接收信息+检查项" },
|
||||
"qcInfo": {
|
||||
"workOrder": { "...工单信息" },
|
||||
"tasks": [{ "...任务列表含结论" }]
|
||||
},
|
||||
"productionInfo": {
|
||||
"workOrder": { "...生产工单" },
|
||||
"tasks": [{ "...生产任务" }]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 审核接口
|
||||
|
||||
`POST /api/admin/storage-release-reviews/{id}/review`
|
||||
|
||||
请求体:
|
||||
```json
|
||||
{
|
||||
"review_conclusion": "tg",
|
||||
"review_remark": "样本质量合格",
|
||||
"review_attachments": "url1,url2"
|
||||
}
|
||||
```
|
||||
|
||||
逻辑:
|
||||
- 验证记录存在 + 租户隔离
|
||||
- UPDATE review_conclusion, review_remark, review_attachments, review_by, review_by_name, review_time
|
||||
- 若 storage_status=yrk 且结论改为 btg,保持 storage_status 不变(入库动作不可通过改结论回退)
|
||||
|
||||
`POST /api/admin/storage-release-reviews/batch-review`
|
||||
|
||||
请求体:
|
||||
```json
|
||||
{
|
||||
"ids": ["id1", "id2"],
|
||||
"review_conclusion": "tg",
|
||||
"review_remark": "批量审核通过",
|
||||
"review_attachments": ""
|
||||
}
|
||||
```
|
||||
|
||||
### 4.6 入库接口
|
||||
|
||||
`POST /api/admin/storage-release-reviews/{id}/store`
|
||||
|
||||
前置条件:`review_conclusion = tg` 且 `storage_status = drk`
|
||||
|
||||
逻辑:
|
||||
- UPDATE storage_status=yrk, storage_by, storage_by_name, storage_time
|
||||
- 同步更新 `biz_protocol_sample.sample_status = 'yrk'`(采集协议样本,通过 sample_code + tenant_no 关联)
|
||||
|
||||
`POST /api/admin/storage-release-reviews/batch-store`
|
||||
|
||||
请求体:
|
||||
```json
|
||||
{ "ids": ["id1", "id2"] }
|
||||
```
|
||||
|
||||
前置条件:所有记录 review_conclusion=tg 且 storage_status=drk。
|
||||
|
||||
### 4.7 自动创建触发点
|
||||
|
||||
在 `backend/app/crud/sample_reception.py` 中,样本接收完成逻辑(accept_conclusion 设为 js 时):
|
||||
- INSERT `biz_storage_release_review`
|
||||
- 冗余写入 sample_code, reception_source, protocol_id, reception_id, protocol_no, sample_name
|
||||
- 初始状态:review_conclusion=NULL, storage_status=drk
|
||||
|
||||
## 5. 前端设计
|
||||
|
||||
### 5.1 新增/修改文件清单
|
||||
|
||||
| 操作 | 文件 | 职责 |
|
||||
|------|------|------|
|
||||
| 新增 | `web-admin/src/api/storage-release-review.ts` | API 调用模块 |
|
||||
| 新增 | `web-admin/src/views/quality-inspection/storage-release-review/index.vue` | 列表页 |
|
||||
| 新增 | `web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue` | 查看弹窗 |
|
||||
| 新增 | `web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue` | 审核弹窗 |
|
||||
| 修改 | `web-admin/src/router/modules/sqxp.ts` | 在质检任务路由组下添加子路由 |
|
||||
|
||||
### 5.2 路由配置
|
||||
|
||||
在 `sqxp.ts` 的质检任务(QualityInspection)路由组下添加:
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: '/quality-inspection/storage-release-review',
|
||||
name: 'StorageReleaseReview',
|
||||
component: () => import('@views/quality-inspection/storage-release-review/index.vue'),
|
||||
meta: {
|
||||
title: '入库放行审核',
|
||||
// 菜单图标仅一级显示
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 列表页 (`index.vue`)
|
||||
|
||||
参照模板:`protocol-audit/index.vue`(含批量审核的列表页)
|
||||
|
||||
**筛选栏:**
|
||||
- 协议类别(下拉:全部 / 采集协议 / 捐赠协议)
|
||||
- 审核状态(下拉:全部 / 待审核 / 通过 / 不通过)
|
||||
- 入库状态(下拉:全部 / 待入库 / 已入库)
|
||||
- 搜索关键词(输入框:协议编号/样本编码/样本名称)
|
||||
|
||||
**工具栏:**
|
||||
- 左侧:已选 N 项
|
||||
- 右侧:[批量审核] [批量入库] — 仅一条按钮为 type="primary"
|
||||
|
||||
**表格列:**
|
||||
|
||||
| 列 | 字段 | 渲染 | 宽度 |
|
||||
|---|---|---|---|
|
||||
| 选择框 | — | el-table-column type=selection | 50 |
|
||||
| 协议编号 | protocol_no | 文本 | 160 |
|
||||
| 样本编码 | sample_code | 文本 | 140 |
|
||||
| 样本名称 | sample_name | 文本 | 140 |
|
||||
| 协议类别 | reception_source | el-tag: cell_storage→primary, research_donation→success | 100 |
|
||||
| 审核结论 | review_conclusion | el-tag: tg→success, btg→danger, null→info("待审核") | 100 |
|
||||
| 入库状态 | storage_status | el-tag: drk→warning("待入库"), yrk→success("已入库") | 100 |
|
||||
| 审核人 | review_by_name | 文本 | 100 |
|
||||
| 审核时间 | review_time | 日期时间 | 170 |
|
||||
| 操作 | — | fixed=right, min-width=260 | 260+ |
|
||||
|
||||
**操作按钮:**
|
||||
- 查看 — 始终显示
|
||||
- 审核 — 始终显示
|
||||
- 样本入库 — 仅 review_conclusion=tg 且 storage_status=drk 时显示
|
||||
|
||||
### 5.4 查看弹窗 (`StorageReleaseViewDialog.vue`)
|
||||
|
||||
参照模板:`qc-task-view-dialog/index.vue`(多分区信息展示弹窗)
|
||||
|
||||
布局:使用 `SystemViewDialog` 或自定义 el-dialog,内部分为 5 个信息区域:
|
||||
|
||||
1. **▎基础信息** — 协议编号、协议类别、样本编码、样本名称、客户姓名、联系电话
|
||||
2. **▎运输信息** — 运输方式、运输温度、运输时长、运输公司
|
||||
3. **▎接收信息** — 接收时间、接收人、接收结论(el-tag: js→success, bj→danger)、检查项表格
|
||||
4. **▎质检信息** — 工单编号、工单状态(el-tag)、任务列表(各任务名称 + 结论 el-tag)
|
||||
5. **▎生产信息** — 生产工单编号、工单状态(el-tag)、任务列表(各任务名称 + 状态 el-tag)
|
||||
6. **▎放行审核** — 审核结论(el-tag)、审核人、审核时间、审核备注、审核附件(文件列表,支持预览/下载)
|
||||
|
||||
所有区域均为只读展示。
|
||||
|
||||
### 5.5 审核弹窗 (`StorageReleaseReviewDialog.vue`)
|
||||
|
||||
参照模板:`ProtocolAuditDialog.vue`(审核弹窗)
|
||||
|
||||
上半部分:与查看弹窗完全相同(只读展示 5 个信息区域)
|
||||
|
||||
下半部分(表单区域):
|
||||
- 审核结论:el-radio-group(通过 / 不通过)
|
||||
- 备注:el-input type=textarea
|
||||
- 附件:使用 `ProtocolFinanceConfirmFields` 或类似上传组件
|
||||
|
||||
底部按钮:[取消] [确定]
|
||||
|
||||
**批量审核**:不展示上半部分信息,仅显示审核表单(结论 + 备注 + 附件)。参照 `ProtocolAuditDialog` 批量模式。
|
||||
|
||||
### 5.6 可复用组件
|
||||
|
||||
考虑将信息展示区域(基础信息 + 运输信息 + 接收信息 + 质检信息 + 生产信息)抽取为共享子组件,供查看弹窗和审核弹窗复用。
|
||||
|
||||
候选组件:
|
||||
- `SampleQcInfoCards` — 已有,可复用质检信息展示
|
||||
- 新建 `StorageReleaseInfoSection.vue` — 统一封装 5 个信息区域的只读展示
|
||||
|
||||
### 5.7 状态标签注册
|
||||
|
||||
在 `status-tag-registry.ts` 中新增:
|
||||
|
||||
```typescript
|
||||
storageReleaseConclusion: {
|
||||
tg: { label: '通过', type: 'success' },
|
||||
btg: { label: '不通过', type: 'danger' },
|
||||
null: { label: '待审核', type: 'info' },
|
||||
},
|
||||
storageStatus: {
|
||||
drk: { label: '待入库', type: 'warning' },
|
||||
yrk: { label: '已入库', type: 'success' },
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 菜单配置
|
||||
|
||||
在系统管理的静态菜单树中,质检任务节点下添加:
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 菜单名称 | 入库放行审核 |
|
||||
| 路由路径 | /quality-inspection/storage-release-review |
|
||||
| 组件路径 | quality-inspection/storage-release-review/index |
|
||||
| 菜单类型 | 菜单(C) |
|
||||
| 上级菜单 | 质检任务 |
|
||||
| 排序 | 在现有质检子菜单之后 |
|
||||
| 图标 | 无(二级菜单不显示图标) |
|
||||
|
||||
## 7. 权限设计
|
||||
|
||||
使用现有角色-菜单权限体系:
|
||||
- 新增菜单节点后,由管理员在角色管理中分配权限
|
||||
- 数据权限遵循现有数据范围规则(全部/本部门/本部门及以下/仅本人/自定义)
|
||||
|
||||
## 8. 边界与约束
|
||||
|
||||
### 不做的事
|
||||
- 不涉及库位分配逻辑
|
||||
- 不涉及工作流引擎(使用简单直审模式)
|
||||
- 不改变现有样本接收、质检、生产工单的逻辑
|
||||
- 不新增独立的权限角色(复用现有菜单权限体系)
|
||||
|
||||
### 异常处理
|
||||
- 批量审核时,部分记录可能已被审核 → 覆盖更新(以最新提交为准)
|
||||
- 批量入库时,跳过不满足条件的记录(结论非 tg 或已入库),返回成功/失败数量
|
||||
- 修改审核结论时,已入库的记录不回退入库状态
|
||||
|
||||
### 附件处理
|
||||
- 遵循现有模式:`review_attachments` 字段存储逗号分隔的 MinIO URL
|
||||
- 上传使用现有 `fetchUploadAttachment` 接口
|
||||
- 附件业务类型:`storage-release-review`
|
||||
@@ -261,6 +261,25 @@ Web 管理端弹窗 SHALL 遵循规范:表单 `label-position="top"`;遮罩
|
||||
- **WHEN** PR 新增列表页或弹窗中包含排序字段
|
||||
- **THEN** 审查者 SHALL 确认产品需求中有明确排序要求,否则 SHALL 要求移除
|
||||
|
||||
### Requirement: 筛选区关键词字段排第一
|
||||
|
||||
`web-admin` 列表页筛选区(`.filter-block` 内 `.filter-grid`)若包含**关键词**模糊检索字段(`label` 为「关键词」或等效语义、`el-input` 绑定 `keyword` 等),该字段 SHALL 作为 `.filter-grid` 中**第一个** `el-form-item`,位于下拉筛选、状态筛选、日期范围等条件字段之前。落地参考:`#/demo/unit`、`#/demo/user` 及质检模块「入库放行审核」页。
|
||||
|
||||
#### Scenario: 新增含关键词的列表页
|
||||
|
||||
- **WHEN** 在列表页筛选区同时提供关键词输入与下拉/日期等筛选条件
|
||||
- **THEN** 「关键词」对应的 `el-form-item` SHALL 位于 `.filter-grid` 首位
|
||||
|
||||
#### Scenario: 入库放行审核筛选区
|
||||
|
||||
- **WHEN** 用户打开质检模块「入库放行审核」列表页
|
||||
- **THEN** 筛选区 `.filter-grid` 中第一个字段 SHALL 为「关键词」,其后依次为协议类别、审核结论、入库状态等条件
|
||||
|
||||
#### Scenario: Code review 筛选区字段顺序
|
||||
|
||||
- **WHEN** PR 新增或修改列表页 `.filter-block` 筛选字段
|
||||
- **THEN** 审查者 SHALL 确认存在关键词字段时其位于 `.filter-grid` 第一位
|
||||
|
||||
### Requirement: 附件上传卡片化布局
|
||||
|
||||
`web-admin` 弹窗表单中的附件上传与展示 SHALL 采用**卡片化布局**,实现与交互以 `web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue` 中「协议附件」为权威参考;储存证等业务弹窗(如 `web-admin/src/views/cell-storage/storage-cert/index.vue`)SHALL 复用同一套 DOM 结构、类名与样式规则,仅调整 `el-form-item` 的 `label` 与上传 `folder` 等业务参数。
|
||||
@@ -655,3 +674,30 @@ const viewSections = computed<ViewSection[]>(() => [
|
||||
- **WHEN** PR 修改列表页或查看弹窗中的状态/结论/类型字段展示
|
||||
- **THEN** 审查者 SHALL 确认使用 `<el-tag>` 组件(表格列)或 `tagType` 属性(SystemViewDialog),SHALL NOT 出现纯文本状态展示或自定义 `<span class="tag">`
|
||||
|
||||
### Requirement: 嵌套弹窗与子弹窗内列表风格一致
|
||||
|
||||
`web-admin` 中所有界面(含主列表页、编辑弹窗、查看弹窗、子弹窗、侧栏详情面板等)内的 `el-table` 列表 SHALL 与标准列表页(如 `#/demo/unit`、`#/demo/order`)保持一致的表格视觉规格,**不得**因处于弹窗或嵌套区域而缩小字号、压缩行高或改用 `size="small"` 等紧凑样式。
|
||||
|
||||
具体规则:
|
||||
|
||||
1. **字号与行高**:表格正文 SHALL 使用规范默认 **14px** 字号与标准行高(与 `.list-table` / 主列表一致);SHALL NOT 为弹窗内表格单独设置更小 `font-size` 或通过 `size="small"` / `size="mini"` 缩小表格。
|
||||
2. **表格类名**:业务列表(含弹窗内嵌列表)SHALL 使用 `.list-table`(可与 `.inner-table` 等场景类叠加),以复用全局表格 Token 与行高,而非在 scoped 样式中覆盖为紧凑尺寸。
|
||||
3. **只读查看**:查看详情、任务详情等只读场景中的子列表 SHALL 以纯文本/Tag 展示数据,SHALL NOT 保留可编辑控件;同时 SHALL NOT 隐藏操作列后改用更小表格尺寸补偿空间。
|
||||
4. **高度**:弹窗内列表可通过 `height` / `max-height` 控制可视区域高度以便滚动,但 SHALL NOT 以缩小字体或行高换取更多行展示。
|
||||
|
||||
#### Scenario: 任务详情弹窗内嵌列表
|
||||
|
||||
- **WHEN** 用户在生产任务「查看详情」弹窗中查看原材料、产出或步骤详情中的试剂/耗材等子列表
|
||||
- **THEN** 表格字号与行高 SHALL 与主列表页一致(14px 标准规格)
|
||||
- **THEN** 只读模式下 SHALL NOT 显示添加、删除等编辑操作按钮
|
||||
|
||||
#### Scenario: 新增弹窗内嵌业务列表
|
||||
|
||||
- **WHEN** 在 `el-dialog` 或子弹窗中新增 `el-table` 展示业务数据
|
||||
- **THEN** 实现 SHALL 使用 `.list-table` 且 SHALL NOT 设置 `size="small"` 或自定义更小字号/行高以适配弹窗
|
||||
|
||||
#### Scenario: Code review 弹窗内表格
|
||||
|
||||
- **WHEN** PR 在弹窗、侧栏详情或嵌套面板中新增或修改 `el-table`
|
||||
- **THEN** 审查者 SHALL 确认未使用 `size="small"` / 紧凑字号覆盖,且列表风格与主列表页一致
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,11 +1,11 @@
|
||||
# Graph Report - src (2026-05-22)
|
||||
# Graph Report - src (2026-05-23)
|
||||
|
||||
## Corpus Check
|
||||
- Large corpus: 584 files · ~308,363 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
- Large corpus: 588 files · ~312,880 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
|
||||
## Summary
|
||||
- 1444 nodes · 1806 edges · 40 communities detected
|
||||
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 399 edges (avg confidence: 0.8)
|
||||
- 1461 nodes · 1826 edges · 42 communities detected
|
||||
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 406 edges (avg confidence: 0.8)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
@@ -32,10 +32,10 @@
|
||||
- [[_COMMUNITY_Community 20|Community 20]]
|
||||
- [[_COMMUNITY_Community 21|Community 21]]
|
||||
- [[_COMMUNITY_Community 22|Community 22]]
|
||||
- [[_COMMUNITY_Community 23|Community 23]]
|
||||
- [[_COMMUNITY_Community 24|Community 24]]
|
||||
- [[_COMMUNITY_Community 25|Community 25]]
|
||||
- [[_COMMUNITY_Community 26|Community 26]]
|
||||
- [[_COMMUNITY_Community 28|Community 28]]
|
||||
- [[_COMMUNITY_Community 27|Community 27]]
|
||||
- [[_COMMUNITY_Community 29|Community 29]]
|
||||
- [[_COMMUNITY_Community 30|Community 30]]
|
||||
- [[_COMMUNITY_Community 31|Community 31]]
|
||||
@@ -44,17 +44,19 @@
|
||||
- [[_COMMUNITY_Community 34|Community 34]]
|
||||
- [[_COMMUNITY_Community 35|Community 35]]
|
||||
- [[_COMMUNITY_Community 36|Community 36]]
|
||||
- [[_COMMUNITY_Community 37|Community 37]]
|
||||
- [[_COMMUNITY_Community 38|Community 38]]
|
||||
- [[_COMMUNITY_Community 39|Community 39]]
|
||||
- [[_COMMUNITY_Community 40|Community 40]]
|
||||
- [[_COMMUNITY_Community 41|Community 41]]
|
||||
- [[_COMMUNITY_Community 42|Community 42]]
|
||||
- [[_COMMUNITY_Community 43|Community 43]]
|
||||
- [[_COMMUNITY_Community 45|Community 45]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `get()` - 155 edges
|
||||
1. `get()` - 157 edges
|
||||
2. `WebSocketClient` - 26 edges
|
||||
3. `push()` - 20 edges
|
||||
4. `withFullscreenLoading()` - 16 edges
|
||||
3. `push()` - 19 edges
|
||||
4. `withFullscreenLoading()` - 17 edges
|
||||
5. `MenuProcessor` - 16 edges
|
||||
6. `VersionManager` - 15 edges
|
||||
7. `handleDynamicRoutes()` - 15 edges
|
||||
@@ -63,58 +65,58 @@
|
||||
10. `StorageConfig` - 10 edges
|
||||
|
||||
## Surprising Connections (you probably didn't know these)
|
||||
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
|
||||
views/system/site/index.vue → utils/fullscreen-loading.ts
|
||||
- `handleBatchDeleteHospital()` --calls--> `withFullscreenLoading()` [INFERRED]
|
||||
views/basic-info/collection-region/index.vue → utils/fullscreen-loading.ts
|
||||
- `handleUnauthorizedResponse()` --calls--> `fetchRefreshToken()` [INFERRED]
|
||||
utils/http/index.ts → api/auth.ts
|
||||
- `fetchLeaveList()` --calls--> `get()` [INFERRED]
|
||||
api/leave.ts → views/production/process-route/index.vue
|
||||
- `fetchLeaveDetail()` --calls--> `get()` [INFERRED]
|
||||
api/leave.ts → views/production/process-route/index.vue
|
||||
- `fetchSampleReceptionCheckItemList()` --calls--> `get()` [INFERRED]
|
||||
api/sample-reception-check-item.ts → views/production/process-route/index.vue
|
||||
- `fetchSampleReceptionCheckItemsActive()` --calls--> `get()` [INFERRED]
|
||||
api/sample-reception-check-item.ts → views/production/process-route/index.vue
|
||||
|
||||
## Communities
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.01
|
||||
Nodes (82): fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchCellSampleTypeList(), fetchChannelAgreementPrices() (+74 more)
|
||||
Nodes (78): fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchCellSampleTypeList(), fetchChannelAgreementPrices() (+70 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.03
|
||||
Nodes (58): fetchBatchGenerateProtocolNos(), fetchBatchGenerateSampleCodes(), fetchBatchIssueProtocolNos(), fetchBatchIssueSampleCodes(), fetchCollectionProtocolNoList(), fetchCreateCollectionProtocolNo(), fetchCreateSampleCode(), fetchDeleteCollectionProtocolNo() (+50 more)
|
||||
Nodes (56): fetchBatchGenerateProtocolNos(), fetchBatchGenerateSampleCodes(), fetchBatchIssueProtocolNos(), fetchBatchIssueSampleCodes(), fetchCollectionProtocolNoList(), fetchCreateCollectionProtocolNo(), fetchCreateSampleCode(), fetchDeleteCollectionProtocolNo() (+48 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.04
|
||||
Nodes (29): fetchGetMenuList(), fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), clickTab(), handleTouchEnd(), setTransition() (+21 more)
|
||||
Nodes (20): clickTab(), handleTouchEnd(), setTransition(), ComponentLoader, IframeRouteManager, RoutePermissionValidator, RouteTransformer, useCommon() (+12 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.04
|
||||
Nodes (25): fetchGetMenuList(), fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), MenuProcessor, RouteRegistry, useAppMode() (+17 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.05
|
||||
Nodes (33): fetchDeptTree(), fetchPostBoundUserIds(), FireworkSystem, reload(), formatUserLabel(), loadDeptData(), onPersonSelectChange(), subscribeTokenRefresh() (+25 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.04
|
||||
Nodes (18): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents(), fetchAllSopDocuments(), fetchConsumableList() (+10 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.05
|
||||
Nodes (39): fetchAccessLogList(), fetchCleanRoomList(), fetchCreateAccessLog(), fetchCreateCleanRoom(), fetchCreateSite(), fetchDeleteAccessLog(), fetchDeleteCleanRoom(), fetchDeleteSite() (+31 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.09
|
||||
Nodes (5): handleReset(), handleSearch(), WebSocketClient, TableCache, emit()
|
||||
Cohesion: 0.05
|
||||
Nodes (40): fetchAccessLogList(), fetchCleanRoomList(), fetchCreateAccessLog(), fetchCreateCleanRoom(), fetchCreateSite(), fetchDeleteAccessLog(), fetchDeleteCleanRoom(), fetchDeleteSite() (+32 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.05
|
||||
Nodes (18): fetchAttachmentDownloadUrl(), fetchOrderAdjusts(), fetchOrderDetail(), fetchOrderItems(), fetchOrderList(), fetchPriceVersionDetail(), fetchSystemConfig(), handleReceptionAttachmentDownload() (+10 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (35): disableDevTools(), handleLock(), handleUnlock(), isMobile(), toLogin(), base64ToBytes(), bytesToBase64(), decryptJson() (+27 more)
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.09
|
||||
Nodes (31): toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes() (+23 more)
|
||||
Nodes (5): handleReset(), handleSearch(), WebSocketClient, TableCache, emit()
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.08
|
||||
Nodes (12): ComponentLoader, IframeRouteManager, RouteTransformer, getFirstMenuPath(), getFirstSecondLevelMenuPath(), isIframe(), isNavigableMenuItem(), isWorkbenchMenuItem() (+4 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (18): fetchAttachmentDownloadUrl(), fetchOrderAdjusts(), fetchOrderDetail(), fetchOrderItems(), fetchOrderList(), fetchPriceVersionDetail(), fetchSystemConfig(), handleReceptionAttachmentDownload() (+10 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.09
|
||||
@@ -129,25 +131,25 @@ Cohesion: 0.09
|
||||
Nodes (9): fetchGetAllUpgradeLogs(), fetchGetChinaMapData(), fetchGetComments(), fetchGetCurrentVersion(), fetchGetLatestUpgradeLog(), fetchGetSystemConfig(), fetchGetUpgradeLogs(), systemUpgrade() (+1 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.08
|
||||
Nodes (10): collectIds(), useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), themeAnimation() (+2 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.1
|
||||
Nodes (16): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), postorder(), preorder(), applyTreeExpand(), handleAdd() (+8 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.1
|
||||
Nodes (13): fetchEthnicList(), addSample(), applyDonationBundleToForm(), confirmBatchTransportApply(), createEmptyTransport(), ensureDonationProtocolNoInOptions(), ensureEthnicOptionInList(), handleOpenForAdd() (+5 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.1
|
||||
Nodes (18): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchHospitalList(), fetchUpdateCollectionRegion(), fetchUpdateHospital() (+10 more)
|
||||
Nodes (19): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchHospitalList(), fetchUpdateCollectionRegion(), fetchUpdateHospital() (+11 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.1
|
||||
Nodes (12): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), collectIds(), handleAdd(), handleDelete(), handleReset() (+4 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.13
|
||||
Nodes (16): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), postorder(), preorder(), applyTreeExpand(), handleAdd() (+8 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.14
|
||||
Nodes (9): auditorDisplay(), dash(), expressName(), formatYm(), formatYmd(), sampleTypeName(), siteName(), unitName() (+1 more)
|
||||
@@ -168,112 +170,124 @@ Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), cr
|
||||
Cohesion: 0.27
|
||||
Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.18
|
||||
Nodes (11): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), handleAdd(), handleDelete(), handleReset(), handleSearch() (+3 more)
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.17
|
||||
Nodes (3): fetchResearchDonationAuditList(), fetchResearchDonationDetail(), fetchResearchDonationList()
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.18
|
||||
Nodes (5): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), useCeremony(), useTheme()
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.18
|
||||
Nodes (3): fetchMaterialList(), fetchMaterialOptions(), fetchMaterialStockLogList()
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.2
|
||||
Nodes (2): fetchQcMaterialList(), fetchQcMaterialOptions()
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.39
|
||||
Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 0.36
|
||||
Nodes (1): TableHeightCalculator
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): logApiCryptoBootInfo(), maskSecret()
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.29
|
||||
Nodes (2): fetchLeaveDetail(), fetchLeaveList()
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.47
|
||||
Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchSampleReceptionCheckItemList(), fetchSampleReceptionCheckItemsActive()
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchQcThirdPartyItemList(), fetchQcThirdPartyItemsActive()
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchQcThirdPartyCompanyList(), fetchQcThirdPartyCompanyOptions()
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
### Community 37 - "Community 37"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchQcLabTestItemList(), fetchQcLabTestItemsActive()
|
||||
|
||||
### Community 38 - "Community 38"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchQcTaskDetail(), fetchQcTaskList()
|
||||
|
||||
### Community 38 - "Community 38"
|
||||
### Community 40 - "Community 40"
|
||||
Cohesion: 0.53
|
||||
Nodes (4): isEtaTodayOrEarlier(), parseEtaLocalDate(), sampleReceptionRowClassName(), startOfLocalDay()
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
### Community 41 - "Community 41"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): focusInput(), handleKeydown()
|
||||
|
||||
### Community 40 - "Community 40"
|
||||
### Community 42 - "Community 42"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): if(), logSidebarMenu()
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
### Community 43 - "Community 43"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): getSettingDefaults(), resetToDefaults()
|
||||
|
||||
### Community 43 - "Community 43"
|
||||
### Community 45 - "Community 45"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
|
||||
|
||||
## Knowledge Gaps
|
||||
- **Thin community `Community 29`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
|
||||
- **Thin community `Community 27`** (10 nodes): `fetchBatchStockInQcMaterial()`, `fetchBatchStockOutQcMaterial()`, `fetchCreateQcMaterial()`, `fetchDeleteQcMaterial()`, `fetchQcMaterialList()`, `fetchQcMaterialOptions()`, `fetchStockInQcMaterial()`, `fetchStockOutQcMaterial()`, `fetchUpdateQcMaterial()`, `qc-material.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 30`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
|
||||
- **Thin community `Community 30`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 31`** (7 nodes): `fetchCreateLeave()`, `fetchDeleteLeave()`, `fetchLeaveDetail()`, `fetchLeaveList()`, `fetchSubmitLeave()`, `fetchUpdateLeave()`, `leave.ts`
|
||||
- **Thin community `Community 31`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 33`** (6 nodes): `fetchCreateSampleReceptionCheckItem()`, `fetchDeleteSampleReceptionCheckItem()`, `fetchSampleReceptionCheckItemList()`, `fetchSampleReceptionCheckItemsActive()`, `fetchUpdateSampleReceptionCheckItem()`, `sample-reception-check-item.ts`
|
||||
- **Thin community `Community 32`** (7 nodes): `fetchCreateLeave()`, `fetchDeleteLeave()`, `fetchLeaveDetail()`, `fetchLeaveList()`, `fetchSubmitLeave()`, `fetchUpdateLeave()`, `leave.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 34`** (6 nodes): `fetchCreateQcThirdPartyItem()`, `fetchDeleteQcThirdPartyItem()`, `fetchQcThirdPartyItemList()`, `fetchQcThirdPartyItemsActive()`, `fetchUpdateQcThirdPartyItem()`, `qc-third-party-item.ts`
|
||||
- **Thin community `Community 34`** (6 nodes): `fetchCreateSampleReceptionCheckItem()`, `fetchDeleteSampleReceptionCheckItem()`, `fetchSampleReceptionCheckItemList()`, `fetchSampleReceptionCheckItemsActive()`, `fetchUpdateSampleReceptionCheckItem()`, `sample-reception-check-item.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 35`** (6 nodes): `fetchCreateQcThirdPartyCompany()`, `fetchDeleteQcThirdPartyCompany()`, `fetchQcThirdPartyCompanyList()`, `fetchQcThirdPartyCompanyOptions()`, `fetchUpdateQcThirdPartyCompany()`, `qc-third-party-company.ts`
|
||||
- **Thin community `Community 35`** (6 nodes): `fetchCreateQcThirdPartyItem()`, `fetchDeleteQcThirdPartyItem()`, `fetchQcThirdPartyItemList()`, `fetchQcThirdPartyItemsActive()`, `fetchUpdateQcThirdPartyItem()`, `qc-third-party-item.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 36`** (6 nodes): `fetchCompleteQcTask()`, `fetchQcTaskDetail()`, `fetchQcTaskList()`, `fetchStartQcTask()`, `fetchUpdateQcTaskAttachments()`, `qc-task.ts`
|
||||
- **Thin community `Community 36`** (6 nodes): `fetchCreateQcThirdPartyCompany()`, `fetchDeleteQcThirdPartyCompany()`, `fetchQcThirdPartyCompanyList()`, `fetchQcThirdPartyCompanyOptions()`, `fetchUpdateQcThirdPartyCompany()`, `qc-third-party-company.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 39`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
|
||||
- **Thin community `Community 37`** (6 nodes): `fetchCreateQcLabTestItem()`, `fetchDeleteQcLabTestItem()`, `fetchQcLabTestItemList()`, `fetchQcLabTestItemsActive()`, `fetchUpdateQcLabTestItem()`, `qc-lab-test-item.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 40`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
|
||||
- **Thin community `Community 38`** (6 nodes): `fetchCompleteQcTask()`, `fetchQcTaskDetail()`, `fetchQcTaskList()`, `fetchStartQcTask()`, `fetchUpdateQcTaskAttachments()`, `qc-task.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 41`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
- **Thin community `Community 41`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 43`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
|
||||
- **Thin community `Community 42`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 43`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 45`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 7`, `Community 11`, `Community 12`, `Community 14`, `Community 15`, `Community 16`, `Community 18`, `Community 19`, `Community 20`, `Community 23`, `Community 25`, `Community 26`, `Community 31`, `Community 33`, `Community 34`, `Community 35`, `Community 36`?**
|
||||
_High betweenness centrality (0.263) - this node is a cross-community bridge._
|
||||
- **Why does `push()` connect `Community 3` to `Community 0`, `Community 2`, `Community 6`, `Community 7`, `Community 12`, `Community 13`, `Community 15`?**
|
||||
_High betweenness centrality (0.093) - this node is a cross-community bridge._
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 9`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 24`, `Community 26`, `Community 27`, `Community 32`, `Community 34`, `Community 35`, `Community 36`, `Community 37`, `Community 38`?**
|
||||
_High betweenness centrality (0.266) - this node is a cross-community bridge._
|
||||
- **Why does `push()` connect `Community 4` to `Community 0`, `Community 2`, `Community 8`, `Community 9`, `Community 12`, `Community 13`?**
|
||||
_High betweenness centrality (0.085) - this node is a cross-community bridge._
|
||||
- **Why does `fetchGetLatestUpgradeLog()` connect `Community 12` to `Community 0`?**
|
||||
_High betweenness centrality (0.042) - this node is a cross-community bridge._
|
||||
- **Are the 154 inferred relationships involving `get()` (e.g. with `buildTreePathLabelFromFlat()` and `alignFqPaymentDetailsForDisplay()`) actually correct?**
|
||||
_`get()` has 154 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 19 inferred relationships involving `push()` (e.g. with `fetchAllPagedRecords()` and `calcInstallmentDueAmounts()`) actually correct?**
|
||||
_`push()` has 19 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 15 inferred relationships involving `withFullscreenLoading()` (e.g. with `.close()` and `handleSubmit()`) actually correct?**
|
||||
_`withFullscreenLoading()` has 15 INFERRED edges - model-reasoned connections that need verification._
|
||||
_High betweenness centrality (0.043) - this node is a cross-community bridge._
|
||||
- **Are the 156 inferred relationships involving `get()` (e.g. with `buildTreePathLabelFromFlat()` and `alignFqPaymentDetailsForDisplay()`) actually correct?**
|
||||
_`get()` has 156 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 18 inferred relationships involving `push()` (e.g. with `fetchAllPagedRecords()` and `calcInstallmentDueAmounts()`) actually correct?**
|
||||
_`push()` has 18 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 16 inferred relationships involving `withFullscreenLoading()` (e.g. with `.close()` and `handleSubmit()`) actually correct?**
|
||||
_`withFullscreenLoading()` has 16 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Should `Community 0` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.01 - nodes in this community are weakly interconnected._
|
||||
@@ -57,12 +57,12 @@
|
||||
</div>
|
||||
<div id="legend"></div>
|
||||
</div>
|
||||
<div id="stats">1444 nodes · 1806 edges · 241 communities</div>
|
||||
<div id="stats">1461 nodes · 1826 edges · 245 communities</div>
|
||||
</div>
|
||||
<script>
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 30, "community_name": "Community 30", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 30, "community_name": "Community 30", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 30, "community_name": "Community 30", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 30, "community_name": "Community 30", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 30, "community_name": "Community 30", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 30, "community_name": "Community 30", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 76, "community_name": "Community 76", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 77, "community_name": "Community 77", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 17, "community_name": "Community 17", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.7, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 17, "community_name": "Community 17", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 17, "community_name": "Community 17", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 17, "community_name": "Community 17", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 17, "community_name": "Community 17", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective", "laLine truncated
|
||||
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 242}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 89}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 74}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 53}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 53}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 50}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 48}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 44}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 39}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 39}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 36}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 35}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 31}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 30}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 28}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 27}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 26}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 22}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 20}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 20}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 19}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 17}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 16}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 15}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 13}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 12}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 11}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 9}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 8}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 8}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 7}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 7}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 6}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 6}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 6}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 6}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 6}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 6}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 6}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 5}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 4}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 3}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 3}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 3}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 3}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 3}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 3}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 3}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 3}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 3}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 3}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 2}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 2}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 2}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 2}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 2}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 2}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 2}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 2}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 2}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 2}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 2}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 2}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 2}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 2}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 2}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 2}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 2}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 2}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "count": 2}, {"cidLine truncated
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 31, "community_name": "Community 31", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 31, "community_name": "Community 31", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 31, "community_name": "Community 31", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 31, "community_name": "Community 31", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 31, "community_name": "Community 31", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 31, "community_name": "Community 31", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 79, "community_name": "Community 79", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 80, "community_name": "Community 80", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 16, "community_name": "Community 16", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.7, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 16, "community_name": "Community 16", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 16, "community_name": "Community 16", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 16, "community_name": "Community 16", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 16, "community_name": "Community 16", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective", "laLine truncated
|
||||
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 226}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 94}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 65}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 62}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 53}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 53}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 51}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 48}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 48}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 44}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 36}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 35}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 31}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 27}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 27}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 25}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 22}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 21}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 20}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 20}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 19}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 17}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 16}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 13}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 12}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 11}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 11}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 10}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 9}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 8}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 8}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 7}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 7}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 6}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 6}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 6}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 6}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 6}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 6}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 6}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 6}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 5}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 4}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 3}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 3}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 3}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 3}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 3}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 3}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 3}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 3}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 3}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 3}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 2}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 2}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 2}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 2}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 2}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 2}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 2}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 2}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 2}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 2}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 2}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 2}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 2}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 2}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 2}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 2}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "count": 2}, {"ciLine truncated
|
||||
|
||||
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
|
||||
function esc(s) {
|
||||
|
||||
+4896
-4638
File diff suppressed because it is too large.
Load diff
@@ -17,8 +17,8 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1778582068.0254738,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1778582068.026978,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1778582068.0246184,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1779436742.3021445,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1779436716.3383653,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1779506249.5668056,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1779502098.7595692,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1778582068.0293276,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1778775532.895603,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1778582068.004279,
|
||||
@@ -43,7 +43,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fullscreen-loading.ts": 1779117454.818312,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/router.ts": 1778582068.0393815,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-labels.ts": 1779199125.4894068,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1779307306.6558833,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1779491563.5260131,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fetch-paged-list.ts": 1778775532.895782,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sort-order.ts": 1778775532.8963482,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/tree-path.ts": 1778800847.103174,
|
||||
@@ -85,7 +85,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/table/tableUtils.ts": 1778582068.0454955,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/status.ts": 1778582068.036632,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/crypto.ts": 1778582068.0353796,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts": 1779312712.0018501,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts": 1779463885.9788811,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/error.ts": 1778582068.0356607,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/socket/index.ts": 1778582068.0397933,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/router/sidebar-menu-filter.ts": 1778582068.0391986,
|
||||
@@ -138,7 +138,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/SectionTitle.vue": 1778582067.979562,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/BoxStyleSettings.vue": 1778582067.9784102,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-settings-panel/widget/ContainerSettings.vue": 1778582067.9787772,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue": 1779249882.5565445,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue": 1779503703.859143,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-chat-window/index.vue": 1778600486.966086,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-page-content/index.vue": 1778582067.9749346,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/index.vue": 1778582067.9713962,
|
||||
@@ -194,6 +194,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-reception-check-item.ts": 1779007909.2933023,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production.ts": 1779171206.8677726,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-third-party-item.ts": 1779017836.101254,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts": 1779492302.440177,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/workflow.ts": 1778729873.096252,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts": 1779341251.2626827,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts": 1779332042.258692,
|
||||
@@ -268,20 +269,20 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/dict/index.vue": 1779259163.7560437,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/leave/index.vue": 1779257191.2768743,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/output-type/index.vue": 1779307917.5932443,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/index.vue": 1779423904.9098966,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/index.vue": 1779505696.3277574,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue": 1779435878.9581294,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/sop/index.vue": 1779309609.6630545,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-task/index.vue": 1779414898.4036016,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/reagent/index.vue": 1779307562.93293,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue": 1779435645.231045,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1779438049.6475797,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1779438051.7892237,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue": 1779438266.265473,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue": 1779438056.25628,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue": 1779504046.6918495,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1779506613.3205173,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1779507602.4356499,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue": 1779507354.8086,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue": 1779506615.8328075,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/EditTaskInfoDialog.vue": 1779434933.0239685,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputList.vue": 1779432481.883849,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputList.vue": 1779507354.785396,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue": 1779432481.6736288,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue": 1779433865.7417676,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue": 1779505558.2585275,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/consumable-pack/index.vue": 1779307488.018346,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/consumable/index.vue": 1779307921.9798808,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process/index.vue": 1779436610.457994,
|
||||
@@ -293,6 +294,9 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/index.vue": 1779284232.1223023,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue": 1779423489.553699,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue": 1779371036.8066387,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue": 1779502744.2123482,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue": 1779492551.3344166,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue": 1779492939.2935543,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material-usage/index.vue": 1779285902.4727366,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/third-party-company/index.vue": 1779423582.5553048,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/500/index.vue": 1778582068.0705533,
|
||||
@@ -329,7 +333,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/RoutePermissionValidator.ts": 1778582068.0127926,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/index.ts": 1778582068.0138035,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/IframeRouteManager.ts": 1778582068.0121944,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts": 1779286045.7832122,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts": 1779492323.722161,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/index.ts": 1778598510.6043763,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/exception.ts": 1778600310.4085584,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routes/staticRoutes.ts": 1778582786.832072,
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
import request from '@/utils/http'
|
||||
|
||||
type PageList<T> = {
|
||||
list: T[]
|
||||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
export type StorageReleaseReview = {
|
||||
id: string
|
||||
sampleCode?: string
|
||||
sampleName?: string
|
||||
protocolNo?: string
|
||||
receptionSource?: string
|
||||
receptionSourceLabel?: string
|
||||
reviewConclusion?: string
|
||||
reviewConclusionLabel?: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
reviewBy?: string
|
||||
reviewByName?: string
|
||||
reviewTime?: string
|
||||
storageStatus?: string
|
||||
storageStatusLabel?: string
|
||||
storageBy?: string
|
||||
storageByName?: string
|
||||
storageTime?: string
|
||||
remark?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
export type ProtocolInfo = {
|
||||
protocolNo?: string
|
||||
protocolStatus?: string
|
||||
motherName?: string
|
||||
motherPhone?: string
|
||||
fatherName?: string
|
||||
fatherPhone?: string
|
||||
newbornName?: string
|
||||
sampleCode?: string
|
||||
sampleTypeName?: string
|
||||
collectAmount?: number | null
|
||||
storageYears?: number | null
|
||||
}
|
||||
|
||||
export type TransportInfo = {
|
||||
transportType?: string
|
||||
waybillNo?: string
|
||||
expressFee?: number | null
|
||||
etaTime?: string
|
||||
carrierName?: string
|
||||
carrierPhone?: string
|
||||
}
|
||||
|
||||
export type ReceptionInfo = {
|
||||
receptionTime?: string
|
||||
handlerName?: string
|
||||
receiveTemperature?: string
|
||||
volumeMl?: number | null
|
||||
netWeightG?: number | null
|
||||
acceptConclusion?: string
|
||||
}
|
||||
|
||||
export type QcInfoTask = {
|
||||
id?: string
|
||||
taskType?: string
|
||||
taskTypeLabel?: string
|
||||
taskStatus?: string
|
||||
taskStatusLabel?: string
|
||||
conclusion?: string
|
||||
conclusionLabel?: string
|
||||
}
|
||||
|
||||
export type QcInfo = {
|
||||
workOrderNo?: string
|
||||
workOrderStatus?: string
|
||||
tasks?: QcInfoTask[]
|
||||
}
|
||||
|
||||
export type ProductionInfoTask = {
|
||||
id?: string
|
||||
taskNo?: string
|
||||
taskName?: string
|
||||
taskStatus?: string
|
||||
taskStatusLabel?: string
|
||||
startedTime?: string
|
||||
completedTime?: string
|
||||
}
|
||||
|
||||
export type ProductionInfo = {
|
||||
workOrderNo?: string
|
||||
workOrderStatus?: string
|
||||
tasks?: ProductionInfoTask[]
|
||||
}
|
||||
|
||||
export type StorageReleaseReviewDetail = {
|
||||
review: StorageReleaseReview
|
||||
protocolInfo?: ProtocolInfo
|
||||
transportInfo?: TransportInfo
|
||||
receptionInfo?: ReceptionInfo
|
||||
qcInfo?: QcInfo
|
||||
productionInfo?: ProductionInfo
|
||||
}
|
||||
|
||||
export function fetchStorageReleaseReviewList(params?: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
receptionSource?: string
|
||||
reviewConclusion?: string
|
||||
storageStatus?: string
|
||||
keyword?: string
|
||||
}) {
|
||||
return request.get<PageList<StorageReleaseReview>>({
|
||||
url: '/web/admin/storage-release-reviews',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchStorageReleaseReviewDetail(id: string) {
|
||||
return request.get<StorageReleaseReviewDetail>({
|
||||
url: `/web/admin/storage-release-reviews/${id}`
|
||||
})
|
||||
}
|
||||
|
||||
export function submitStorageReleaseReview(
|
||||
id: string,
|
||||
data: {
|
||||
reviewConclusion: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
}
|
||||
) {
|
||||
return request.put<StorageReleaseReview>({
|
||||
url: `/web/admin/storage-release-reviews/${id}/review`,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function batchReviewStorageRelease(data: {
|
||||
ids: string[]
|
||||
reviewConclusion: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
}) {
|
||||
return request.put<void>({
|
||||
url: '/web/admin/storage-release-reviews/batch-review',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function storeSample(id: string) {
|
||||
return request.put<StorageReleaseReview>({
|
||||
url: `/web/admin/storage-release-reviews/${id}/store`
|
||||
})
|
||||
}
|
||||
|
||||
export function batchStoreSample(data: { ids: string[] }) {
|
||||
return request.put<void>({
|
||||
url: '/web/admin/storage-release-reviews/batch-store',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -361,9 +361,14 @@
|
||||
useCommon().refresh()
|
||||
return
|
||||
}
|
||||
router.push({
|
||||
path: item.path,
|
||||
query: item.query as LocationQueryRaw
|
||||
// 强制重新加载:先将目标组件从 KeepAlive 缓存中排除
|
||||
store.addKeepAliveExclude(item)
|
||||
// 等待 Vue 处理 exclude 变更、清除缓存后再导航
|
||||
nextTick(() => {
|
||||
router.push({
|
||||
path: item.path,
|
||||
query: item.query as LocationQueryRaw
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_cell_product_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L1"}, {"id": "production_work_order_index_handleview", "label": "handleView()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L454"}, {"id": "production_work_order_index_handlecomplete", "label": "handleComplete()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L458"}, {"id": "production_work_order_index_handleopendispatch", "label": "handleOpenDispatch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L468"}, {"id": "production_work_order_index_handleedittask", "label": "handleEditTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L485"}, {"id": "production_work_order_index_opentaskeditor", "label": "openTaskEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L500"}, {"id": "production_work_order_index_openedittaskinfo", "label": "openEditTaskInfo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L512"}, {"id": "production_work_order_index_handleedittaskinfosuccess", "label": "handleEditTaskInfoSuccess()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L517"}, {"id": "production_work_order_index_handledeletetask", "label": "handleDeleteTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L523"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L454", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handlecomplete", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L458", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleopendispatch", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L468", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleedittask", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L485", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_opentaskeditor", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L500", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_openedittaskinfo", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L512", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleedittaskinfosuccess", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L517", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskinputenhancedtable_vue", "label": "TaskInputEnhancedTable.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputproductdialog_vue", "label": "TaskOutputProductDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L1"}, {"id": "production_work_order_taskoutputproductdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L342"}, {"id": "production_work_order_taskoutputproductdialog_initaddform", "label": "initAddForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L408"}, {"id": "production_work_order_taskoutputproductdialog_applyproductcodetolines", "label": "applyProductCodeToLines()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L426"}, {"id": "production_work_order_taskoutputproductdialog_ondialogopened", "label": "onDialogOpened()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L431"}, {"id": "production_work_order_taskoutputproductdialog_createquantityvalue", "label": "createQuantityValue()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L437"}, {"id": "production_work_order_taskoutputproductdialog_stripproductcodesuffix", "label": "stripProductCodeSuffix()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L443"}, {"id": "production_work_order_taskoutputproductdialog_buildlineproductcode", "label": "buildLineProductCode()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L449"}, {"id": "production_work_order_taskoutputproductdialog_synclinecodesfrombase", "label": "syncLineCodesFromBase()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L455"}, {"id": "production_work_order_taskoutputproductdialog_syncproductlines", "label": "syncProductLines()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L462"}, {"id": "production_work_order_taskoutputproductdialog_oncreatecountchange", "label": "onCreateCountChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L475"}, {"id": "production_work_order_taskoutputproductdialog_onbaseproductcodeinput", "label": "onBaseProductCodeInput()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L481"}, {"id": "production_work_order_taskoutputproductdialog_onlineproductcodeinput", "label": "onLineProductCodeInput()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L496"}, {"id": "production_work_order_taskoutputproductdialog_resetform", "label": "resetForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L541"}, {"id": "production_work_order_taskoutputproductdialog_filenamefromurl", "label": "fileNameFromUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L562"}, {"id": "production_work_order_taskoutputproductdialog_syncimagestoform", "label": "syncImagesToForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L568"}, {"id": "production_work_order_taskoutputproductdialog_syncformtoimages", "label": "syncFormToImages()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L575"}, {"id": "production_work_order_taskoutputproducLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "label": "StepDetailPanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L1"}, {"id": "production_work_order_stepdetailpanel_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L699"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "target": "production_work_order_stepdetailpanel_for", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L699", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "push", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L701"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "decodeURIComponent", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L704"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "pop", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L704"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "split", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L704"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L704"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L1"}, {"id": "art_work_tab_index_settransition", "label": "setTransition()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L204"}, {"id": "art_work_tab_index_handlewheelscroll", "label": "handleWheelScroll()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L283"}, {"id": "art_work_tab_index_handletouchstart", "label": "handleTouchStart()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L300"}, {"id": "art_work_tab_index_handletouchmove", "label": "handleTouchMove()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L304"}, {"id": "art_work_tab_index_handletouchend", "label": "handleTouchEnd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L318"}, {"id": "art_work_tab_index_setupeventlisteners", "label": "setupEventListeners()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L322"}, {"id": "art_work_tab_index_cleanupeventlisteners", "label": "cleanupEventListeners()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L331"}, {"id": "art_work_tab_index_clicktab", "label": "clickTab()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L359"}, {"id": "art_work_tab_index_closeworktab", "label": "closeWorktab()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L375"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_settransition", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L204", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_handlewheelscroll", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L283", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_handletouchstart", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L300", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_handletouchmove", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L304", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_handletouchend", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L318", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_setupeventlisteners", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L322", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_cleanupeventlisteners", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L331", "weight": 1.0}, {"source": "users_wuzilong_work_Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "label": "StorageReleaseReviewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L1"}, {"id": "storage_release_review_storagereleasereviewdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L173"}, {"id": "storage_release_review_storagereleasereviewdialog_isimagefile", "label": "isImageFile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L179"}, {"id": "storage_release_review_storagereleasereviewdialog_removeattachment", "label": "removeAttachment()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L185"}, {"id": "storage_release_review_storagereleasereviewdialog_getlist", "label": "_getList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L191"}, {"id": "storage_release_review_storagereleasereviewdialog_resetform", "label": "resetForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L262"}, {"id": "storage_release_review_storagereleasereviewdialog_handlesubmit", "label": "handleSubmit()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L271"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L120", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L121", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_business", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L122", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L123", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_fullscreen_loading", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L129", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "storage_release_review_storagereleasereviewdialog_fullfileurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L173", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "storage_release_review_storagereleasereviewdialog_isimagefile",Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputproductdialog_vue", "label": "TaskOutputProductDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L42"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L57"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L64"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_tree_path", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_toapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_fromapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39", "weight": 1.0}, {"source": "users_wuLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_cell_product_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L42"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L57"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L64"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_tree_path", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_toapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_fromapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39", "weight": 1.0}, {"source": "users_wuLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_router_modules_sqxp_ts", "label": "sqxp.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_router_modules_sqxp_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_types_router", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleaseviewdialog_vue", "label": "StorageReleaseViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue", "source_location": "L1"}, {"id": "storage_release_review_storagereleaseviewdialog_formatval", "label": "formatVal()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue", "source_location": "L175"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleaseviewdialog_vue", "target": "storage_release_review_storagereleaseviewdialog_formatval", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue", "source_location": "L175", "weight": 1.0}], "raw_calls": [{"caller_nid": "storage_release_review_storagereleaseviewdialog_formatval", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue", "source_location": "L177"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputlist_vue", "label": "TaskOutputList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L1"}, {"id": "production_work_order_index_handleview", "label": "handleView()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L454"}, {"id": "production_work_order_index_handlecomplete", "label": "handleComplete()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L458"}, {"id": "production_work_order_index_handleopendispatch", "label": "handleOpenDispatch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L468"}, {"id": "production_work_order_index_handleedittask", "label": "handleEditTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L485"}, {"id": "production_work_order_index_opentaskeditor", "label": "openTaskEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L500"}, {"id": "production_work_order_index_openedittaskinfo", "label": "openEditTaskInfo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L512"}, {"id": "production_work_order_index_handleedittaskinfosuccess", "label": "handleEditTaskInfoSuccess()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L517"}, {"id": "production_work_order_index_handledeletetask", "label": "handleDeleteTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L523"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L454", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handlecomplete", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L458", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleopendispatch", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L468", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleedittask", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L485", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_opentaskeditor", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L500", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_openedittaskinfo", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L512", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleedittaskinfosuccess", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L517", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputlist_vue", "label": "TaskOutputList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_index_ts", "label": "index.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L1"}, {"id": "http_index_subscribetokenrefresh", "label": "subscribeTokenRefresh()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L45"}, {"id": "http_index_onrefreshed", "label": "onRefreshed()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L49"}, {"id": "http_index_onrefreshfailed", "label": "onRefreshFailed()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L54"}, {"id": "http_index_createhttperror", "label": "createHttpError()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L188"}, {"id": "http_index_handleunauthorizedresponse", "label": "handleUnauthorizedResponse()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L195"}, {"id": "http_index_triggerlogoutwithdebounce", "label": "triggerLogoutWithDebounce()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L256"}, {"id": "http_index_resetunauthorizederror", "label": "resetUnauthorizedError()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L266"}, {"id": "http_index_logout", "label": "logOut()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L273"}, {"id": "http_index_resolveuserid", "label": "resolveUserId()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L279"}, {"id": "http_index_parseuseridfromtoken", "label": "parseUserIdFromToken()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L286"}, {"id": "http_index_ispathexcluded", "label": "isPathExcluded()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L300"}, {"id": "http_index_getencryptskipreason", "label": "getEncryptSkipReason()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L305"}, {"id": "http_index_normalizerequestdata", "label": "normalizeRequestData()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L324"}, {"id": "http_index_decryptresponseifneeded", "label": "decryptResponseIfNeeded()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L337"}, {"id": "http_index_shouldretry", "label": "shouldRetry()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L361"}, {"id": "http_index_getmaxretriesforconfig", "label": "getMaxRetriesForConfig()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L372"}, {"id": "http_index_retryrequest", "label": "retryRequest()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L378"}, {"id": "http_index_delay", "label": "delay()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L395"}, {"id": "http_index_request", "label": "request()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L400"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_index_ts", "target": "axios", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L17", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_index_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L18", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_index_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_status", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "label": "storage-release-review.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L1"}, {"id": "api_storage_release_review_fetchstoragereleasereviewlist", "label": "fetchStorageReleaseReviewList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L108"}, {"id": "api_storage_release_review_fetchstoragereleasereviewdetail", "label": "fetchStorageReleaseReviewDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L122"}, {"id": "api_storage_release_review_submitstoragereleasereview", "label": "submitStorageReleaseReview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L128"}, {"id": "api_storage_release_review_batchreviewstoragerelease", "label": "batchReviewStorageRelease()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L142"}, {"id": "api_storage_release_review_storesample", "label": "storeSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L154"}, {"id": "api_storage_release_review_batchstoresample", "label": "batchStoreSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L160"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_fetchstoragereleasereviewlist", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L108", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_fetchstoragereleasereviewdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L122", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_submitstoragereleasereview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L128", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_batchreviewstoragerelease", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L142", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_storesample", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L154", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_batchstoresample", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L160", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_storage_release_review_fetchstoragereleasereviewlist", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L116"}, {"caller_nid": "api_storage_release_review_fetchstoragereleasereviewdetail", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L123"}, {"caller_nid": "api_storage_release_review_submitstoragereleasereview", "callee": "put", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L136"}, {"caller_nid": "api_storage_release_review_batchreviewstoragerelease", "callee": "put", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "sLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskinputenhancedtable_vue", "label": "TaskInputEnhancedTable.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskexecutiondialog_vue", "label": "TaskExecutionDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry_ts", "label": "status-tag-registry.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L1"}, {"id": "utils_status_tag_registry_createlabelgetter", "label": "createLabelGetter()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L29"}, {"id": "utils_status_tag_registry_createtagtypegetter", "label": "createTagTypeGetter()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L33"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry_ts", "target": "utils_status_tag_registry_createlabelgetter", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L29", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry_ts", "target": "utils_status_tag_registry_createtagtypegetter", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L33", "weight": 1.0}], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputlist_vue", "label": "TaskOutputList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L42"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L57"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L64"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_tree_path", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_toapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_fromapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39", "weight": 1.0}, {"source": "users_wuLine truncated
|
||||
@@ -396,6 +396,17 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
|
||||
path: 'qc-material-usage',
|
||||
component: '/quality-inspection/qc-material-usage/index',
|
||||
meta: { title: '质检物料变动记录', keepAlive: true, menuCode: 'biz:qcMaterialUsage:list' }
|
||||
},
|
||||
{
|
||||
id: '70000000000000000000000000000011',
|
||||
name: 'QualityInspectionStorageReleaseReview',
|
||||
path: 'storage-release-review',
|
||||
component: '/quality-inspection/storage-release-review/index',
|
||||
meta: {
|
||||
title: '入库放行审核',
|
||||
keepAlive: true,
|
||||
menuCode: 'biz:storageReleaseReview:list'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -204,6 +204,12 @@ async function handleUnauthorizedResponse(
|
||||
throw createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
|
||||
}
|
||||
|
||||
// refresh-token 接口本身返回 401,说明 refreshToken 已失效,直接登出,避免死锁
|
||||
if (config.url && /\/auth\/refresh-token/.test(config.url)) {
|
||||
triggerLogoutWithDebounce(message)
|
||||
throw createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
|
||||
}
|
||||
|
||||
if ((config as ExtendedAxiosRequestConfig)._retry) {
|
||||
triggerLogoutWithDebounce(message)
|
||||
throw createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
|
||||
|
||||
@@ -325,4 +325,22 @@ export const packType: StatusRegistry = {
|
||||
export const packTypeLabel = createLabelGetter(packType)
|
||||
export const packTypeTagType = createTagTypeGetter(packType)
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// 入库放行审核域
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
export const storageReleaseConclusion: StatusRegistry = {
|
||||
tg: { label: '通过', tagType: 'success' },
|
||||
btg: { label: '不通过', tagType: 'danger' }
|
||||
}
|
||||
export const storageReleaseConclusionLabel = createLabelGetter(storageReleaseConclusion)
|
||||
export const storageReleaseConclusionTagType = createTagTypeGetter(storageReleaseConclusion)
|
||||
|
||||
export const storageStatus: StatusRegistry = {
|
||||
drk: { label: '待入库', tagType: 'warning' },
|
||||
yrk: { label: '已入库', tagType: 'success' }
|
||||
}
|
||||
export const storageStatusLabel = createLabelGetter(storageStatus)
|
||||
export const storageStatusTagType = createTagTypeGetter(storageStatus)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="已冻存" value="frozen" />
|
||||
<el-option label="生产中" value="in_production" />
|
||||
<el-option label="已售卖" value="sold" />
|
||||
<el-option label="已使用" value="used" />
|
||||
<el-option label="已作废" value="voided" />
|
||||
@@ -680,9 +681,11 @@
|
||||
function productStatusTag(status: string) {
|
||||
const map: Record<string, { label: string; type: string }> = {
|
||||
frozen: { label: '已冻存', type: 'success' },
|
||||
in_production: { label: '生产中', type: 'warning' },
|
||||
sold: { label: '已售卖', type: 'warning' },
|
||||
used: { label: '已使用', type: 'info' },
|
||||
voided: { label: '已作废', type: 'danger' },
|
||||
thawed: { label: '已复苏', type: 'info' },
|
||||
}
|
||||
return map[status] || { label: status, type: 'info' }
|
||||
}
|
||||
@@ -724,6 +727,8 @@
|
||||
/* ---- 编辑时状态选项 ---- */
|
||||
const editStatusOptions = [
|
||||
{ label: '已冻存', value: 'frozen' },
|
||||
{ label: '生产中', value: 'in_production' },
|
||||
{ label: '已复苏', value: 'thawed' },
|
||||
{ label: '已售卖', value: 'sold' },
|
||||
{ label: '已使用', value: 'used' },
|
||||
{ label: '已作废', value: 'voided' },
|
||||
@@ -1190,7 +1195,7 @@
|
||||
submitLoading.value = true
|
||||
try {
|
||||
syncImagesToForm()
|
||||
const shared = {
|
||||
const shared: Record<string, any> = {
|
||||
productName: formData.productName.trim(),
|
||||
productTypeId: formData.productTypeId || undefined,
|
||||
specId: formData.specId || undefined,
|
||||
@@ -1201,10 +1206,14 @@
|
||||
freezeDate: formData.freezeDate || undefined,
|
||||
unit: formData.unit?.trim() || undefined,
|
||||
expiryDate: formData.expiryDate || undefined,
|
||||
productStatus: formData.productStatus || undefined,
|
||||
productImages: formData.productImages?.trim() || undefined,
|
||||
remark: formData.remark?.trim() || undefined,
|
||||
}
|
||||
// 仅「已冻存」可变更状态;生产中/已复苏等系统态勿提交,避免后端校验失败
|
||||
const editableStatuses = ['frozen', 'sold', 'used', 'voided']
|
||||
if (!isEdit.value || editableStatuses.includes(formData.productStatus)) {
|
||||
shared.productStatus = formData.productStatus || undefined
|
||||
}
|
||||
if (isEdit.value && editingId.value) {
|
||||
await fetchUpdateCellProduct(editingId.value, {
|
||||
...shared,
|
||||
|
||||
@@ -50,13 +50,14 @@
|
||||
<el-form label-position="top" class="route-config-step-form">
|
||||
<div class="form-grid-4">
|
||||
<el-form-item label="工序名称">
|
||||
<el-input v-model="editor.selectedStep.value.processName" />
|
||||
<el-input v-model="editor.selectedStep.value.processName" :disabled="readonly" />
|
||||
</el-form-item>
|
||||
<el-form-item label="工序分类">
|
||||
<el-select
|
||||
v-model="editor.selectedStep.value.categoryId"
|
||||
placeholder="选择分类"
|
||||
clearable
|
||||
:disabled="readonly"
|
||||
>
|
||||
<el-option
|
||||
v-for="cat in editor.categoryOptions.value"
|
||||
@@ -67,10 +68,14 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备名称">
|
||||
<el-input v-model="editor.selectedStep.value.equipmentName" />
|
||||
<el-input v-model="editor.selectedStep.value.equipmentName" :disabled="readonly" />
|
||||
</el-form-item>
|
||||
<el-form-item label="预计时长(min)">
|
||||
<el-input v-model="editor.selectedStep.value.estimatedDuration" placeholder="如 2h" />
|
||||
<el-input
|
||||
v-model="editor.selectedStep.value.estimatedDuration"
|
||||
placeholder="如 2h"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" class="step-form-col-half">
|
||||
<el-input
|
||||
@@ -78,6 +83,7 @@
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="工序描述"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="关联SOP" class="step-form-col-half">
|
||||
@@ -86,6 +92,7 @@
|
||||
multiple
|
||||
placeholder="选择SOP文档"
|
||||
filterable
|
||||
:disabled="readonly"
|
||||
@change="editor.onSopIdsChange"
|
||||
>
|
||||
<el-option
|
||||
@@ -111,7 +118,7 @@
|
||||
|
||||
<el-tabs v-model="editor.activeSubTab.value" class="route-config-tabs">
|
||||
<el-tab-pane label="试剂" name="reagent">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<div v-if="!readonly" class="route-config-tab-toolbar">
|
||||
<el-button class="btn-main" type="primary" size="small" @click="editor.addReagentRow">
|
||||
<el-icon><Plus /></el-icon>添加试剂
|
||||
</el-button>
|
||||
@@ -119,8 +126,8 @@
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.reagents"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
class="list-table inner-table"
|
||||
max-height="400"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
@@ -129,14 +136,21 @@
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="试剂编码" min-width="110" align="center">
|
||||
<el-table-column label="试剂编码" min-width="110" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.reagentCode" placeholder="编码" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.reagentCode"
|
||||
placeholder="编码"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.reagentCode || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="试剂名称" min-width="160" align="center">
|
||||
<el-table-column label="试剂名称" min-width="160" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.reagentId"
|
||||
placeholder="请选择试剂"
|
||||
clearable
|
||||
@@ -156,20 +170,29 @@
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.reagentName || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格" min-width="100" align="center">
|
||||
<el-table-column label="规格" min-width="100" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.spec" placeholder="规格" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.spec"
|
||||
placeholder="规格"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.spec || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标准用量" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.standardUsage"
|
||||
placeholder="用量"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.standardUsage ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -180,16 +203,19 @@
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.actualUsage"
|
||||
placeholder="实际用量"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveReagents"
|
||||
/>
|
||||
<span v-else>{{ row.actualUsage ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="120" align="center">
|
||||
<el-table-column label="单位" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
@@ -208,19 +234,29 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.unit || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关键物料" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch v-model="row.isKeyMaterial" />
|
||||
<el-switch v-if="!readonly" v-model="row.isKeyMaterial" />
|
||||
<el-tag v-else :type="row.isKeyMaterial ? 'success' : 'info'" size="small">
|
||||
{{ row.isKeyMaterial ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<el-table-column label="备注" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.remark" placeholder="备注" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.remark || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button
|
||||
link
|
||||
@@ -240,7 +276,7 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="耗材" name="consumable">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<div v-if="!readonly" class="route-config-tab-toolbar">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
@@ -253,8 +289,8 @@
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.consumables"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
class="list-table inner-table"
|
||||
max-height="400"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
@@ -263,18 +299,21 @@
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="耗材编码" min-width="110" align="center">
|
||||
<el-table-column label="耗材编码" min-width="110" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.consumableCode"
|
||||
placeholder="编码"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.consumableCode || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="耗材名称" min-width="160" align="center">
|
||||
<el-table-column label="耗材名称" min-width="160" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.consumableId"
|
||||
placeholder="请选择耗材"
|
||||
clearable
|
||||
@@ -294,24 +333,29 @@
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.consumableName || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格型号" min-width="110" align="center">
|
||||
<el-table-column label="规格型号" min-width="110" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.specModel"
|
||||
placeholder="规格型号"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.specModel || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标准用量" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.standardUsage"
|
||||
placeholder="用量"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.standardUsage ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -322,16 +366,19 @@
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.actualUsage"
|
||||
placeholder="实际用量"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveConsumables"
|
||||
/>
|
||||
<span v-else>{{ row.actualUsage ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="120" align="center">
|
||||
<el-table-column label="单位" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
@@ -350,19 +397,29 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.unit || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="必需" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch v-model="row.isRequired" />
|
||||
<el-switch v-if="!readonly" v-model="row.isRequired" />
|
||||
<el-tag v-else :type="row.isRequired ? 'success' : 'info'" size="small">
|
||||
{{ row.isRequired ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<el-table-column label="备注" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.remark" placeholder="备注" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.remark || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button
|
||||
link
|
||||
@@ -382,7 +439,7 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="产出物" name="output">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<div v-if="!readonly" class="route-config-tab-toolbar">
|
||||
<el-button class="btn-main" type="primary" size="small" @click="editor.addOutputRow">
|
||||
<el-icon><Plus /></el-icon>添加产出物
|
||||
</el-button>
|
||||
@@ -390,8 +447,8 @@
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.outputs"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
class="list-table inner-table"
|
||||
max-height="400"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
@@ -400,9 +457,10 @@
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="产出类型" min-width="150" align="center">
|
||||
<el-table-column label="产出类型" min-width="150" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.outputTypeId"
|
||||
placeholder="选择类型"
|
||||
filterable
|
||||
@@ -416,39 +474,46 @@
|
||||
:value="ot.id"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ outputTypeLabel(row.outputTypeId) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" min-width="150" align="center">
|
||||
<el-table-column label="名称" min-width="150" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.outputName"
|
||||
placeholder="名称"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveOutputs"
|
||||
/>
|
||||
<span v-else>{{ row.outputName || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="条码" min-width="150" align="center">
|
||||
<el-table-column label="条码" min-width="150" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.outputBarcode"
|
||||
placeholder="条码"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveOutputs"
|
||||
/>
|
||||
<span v-else>{{ row.outputBarcode || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<el-table-column label="备注" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveOutputs"
|
||||
/>
|
||||
<span v-else>{{ row.remark || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" size="small" @click="editor.removeOutputRow($index)"
|
||||
>删除</el-button
|
||||
@@ -464,7 +529,7 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="数据采集" name="dataCollection">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<div v-if="!readonly" class="route-config-tab-toolbar">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
@@ -477,8 +542,8 @@
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.dataCollections"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
class="list-table inner-table"
|
||||
max-height="400"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
@@ -487,19 +552,22 @@
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="采集名称" min-width="180" align="center">
|
||||
<el-table-column label="采集名称" min-width="180" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.collectionName"
|
||||
placeholder="采集项名称"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveDataCollections"
|
||||
/>
|
||||
<span v-else>{{ row.collectionName || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="120" align="center">
|
||||
<el-table-column label="单位" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
@@ -519,6 +587,7 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.unit || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -529,24 +598,28 @@
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.collectionValue"
|
||||
placeholder="输入采集值"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveDataCollections"
|
||||
/>
|
||||
<span v-else>{{ row.collectionValue ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<el-table-column label="备注" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveDataCollections"
|
||||
/>
|
||||
<span v-else>{{ row.remark || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button
|
||||
link
|
||||
@@ -588,6 +661,11 @@
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
function outputTypeLabel(outputTypeId: string) {
|
||||
const item = props.editor.outputTypeOptions.value.find((ot) => ot.id === outputTypeId)
|
||||
return item?.typeName || item?.name || outputTypeId || '—'
|
||||
}
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
function sopAttachmentFullUrl(url: string) {
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
<el-tag v-if="editor.virtualOutputStatus.value === 'filled'" type="success" size="small"
|
||||
>已填写 {{ editor.taskOutputs.value.length }} 项</el-tag
|
||||
>
|
||||
<el-tag v-else type="danger" size="small">未填写</el-tag>
|
||||
<el-tag v-else type="info" size="small">暂无</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 分割线 -->
|
||||
@@ -266,7 +266,7 @@
|
||||
<!-- 任务输出详情 -->
|
||||
<template v-else-if="editor.selectedVirtualStep.value === 'output'">
|
||||
<div class="execution-detail-header">
|
||||
<h3>📤 产出(细胞产品)<span class="virtual-step-required">必填</span></h3>
|
||||
<h3>📤 产出(细胞产品)</h3>
|
||||
<div v-if="!readonly" class="execution-detail-actions">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
|
||||
@@ -16,7 +16,15 @@
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table :data="items" size="small" stripe border v-loading="loading" empty-text="暂无原材料">
|
||||
<el-table
|
||||
:data="items"
|
||||
class="list-table"
|
||||
stripe
|
||||
border
|
||||
height="420"
|
||||
v-loading="loading"
|
||||
empty-text="暂无原材料"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="56" align="center" />
|
||||
<el-table-column label="类型" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
@@ -88,7 +96,7 @@
|
||||
<el-dialog
|
||||
v-model="selectDialogVisible"
|
||||
title="选择细胞产品"
|
||||
width="1160px"
|
||||
width="1400px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
destroy-on-close
|
||||
@@ -157,6 +165,28 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="批次号"
|
||||
prop="batchNo"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="规格" prop="specName" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column
|
||||
label="预计细胞数量(亿)"
|
||||
prop="expectedCellCount"
|
||||
min-width="140"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ row.expectedCellCount ?? '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活性" prop="viability" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.viability != null ? row.viability : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -192,6 +222,10 @@
|
||||
passage?: string
|
||||
storageSlotName?: string
|
||||
productStatus?: string
|
||||
batchNo?: string
|
||||
specName?: string
|
||||
expectedCellCount?: number
|
||||
viability?: number
|
||||
}[]
|
||||
cellProductOptionsLoading: boolean
|
||||
}>(),
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
<!-- 列表 -->
|
||||
<el-table
|
||||
:data="items"
|
||||
size="small"
|
||||
class="list-table"
|
||||
stripe
|
||||
border
|
||||
height="420"
|
||||
v-loading="loading"
|
||||
empty-text="暂无产出产品"
|
||||
>
|
||||
@@ -38,6 +39,26 @@
|
||||
{{ row.storageSlotName || '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计细胞数量(亿)" min-width="130" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.expectedCellCount ?? '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活性" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.viability != null ? row.viability : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="冻存日期" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatDateOnly(row.freezeDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="有效期" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatDateOnly(row.expiryDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.quantity ?? '—' }}
|
||||
@@ -149,17 +170,42 @@
|
||||
emit('update:items', newItems)
|
||||
}
|
||||
|
||||
function handleDialogConfirm(data: Record<string, any>) {
|
||||
function formatDateOnly(val: unknown) {
|
||||
if (!val) return '—'
|
||||
const s = String(val)
|
||||
return s.length >= 10 ? s.slice(0, 10) : s
|
||||
}
|
||||
|
||||
function enrichOutputRow(row: Record<string, any>) {
|
||||
const productType = props.productTypeOptions.find((o) => o.id === row.productTypeId)
|
||||
const spec = props.specOptions.find((o) => o.id === row.specId)
|
||||
const slot = props.storageSlotOptions.find((o) => o.id === row.storageSlotId)
|
||||
const productCode = row.productCode || row.outputBarcode || ''
|
||||
const productName = row.productName || row.outputName || ''
|
||||
return {
|
||||
...row,
|
||||
productCode,
|
||||
productName,
|
||||
outputBarcode: productCode,
|
||||
outputName: productName,
|
||||
productTypeName: row.productTypeName || productType?.productTypeName || '',
|
||||
specName: row.specName || spec?.specName || '',
|
||||
storageSlotName: row.storageSlotName || slot?.name || ''
|
||||
}
|
||||
}
|
||||
|
||||
function handleDialogConfirm(data: Record<string, any> | Record<string, any>[]) {
|
||||
const rows = (Array.isArray(data) ? data : [data]).map(enrichOutputRow)
|
||||
const newItems = [...props.items]
|
||||
if (editingIndex.value >= 0) {
|
||||
// 编辑:替换原有项,保留 cellProductId
|
||||
const row = rows[0]
|
||||
if (!row) return
|
||||
newItems[editingIndex.value] = {
|
||||
...newItems[editingIndex.value],
|
||||
...data
|
||||
...row
|
||||
}
|
||||
} else {
|
||||
// 新增:添加到列表
|
||||
newItems.push(data)
|
||||
newItems.push(...rows)
|
||||
}
|
||||
emit('update:items', newItems)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
:title="isEdit ? '编辑产出产品' : '新增产出产品'"
|
||||
width="1000px"
|
||||
width="1080px"
|
||||
class="dialog-form-layout task-output-product-dialog"
|
||||
align-center
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
@opened="onDialogOpened"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
@@ -21,7 +22,12 @@
|
||||
<div class="form-grid-3">
|
||||
<!-- 产品编码 -->
|
||||
<el-form-item label="产品编码" prop="productCode">
|
||||
<el-input v-model="formData.productCode" placeholder="请输入产品编码" />
|
||||
<el-input
|
||||
v-model="formData.productCode"
|
||||
placeholder="请输入产品编码"
|
||||
:readonly="isEdit"
|
||||
@input="onBaseProductCodeInput"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 产品名称 -->
|
||||
<el-form-item label="产品名称" prop="productName">
|
||||
@@ -74,8 +80,8 @@
|
||||
<el-form-item label="批次号" prop="batchNo">
|
||||
<el-input v-model="formData.batchNo" placeholder="请输入批次号" />
|
||||
</el-form-item>
|
||||
<!-- 储物格位置 -->
|
||||
<el-form-item label="储物格位置">
|
||||
<!-- 储物格位置(编辑单条) -->
|
||||
<el-form-item v-if="isEdit" label="储物格位置">
|
||||
<el-select
|
||||
v-model="formData.storageSlotId"
|
||||
placeholder="请选择储物格"
|
||||
@@ -122,14 +128,16 @@
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 数量 -->
|
||||
<el-form-item label="数量">
|
||||
<!-- 入库份数 / 数量 -->
|
||||
<el-form-item :label="isEdit ? '数量' : '入库份数'" prop="quantity">
|
||||
<el-input-number
|
||||
v-model="formData.quantity"
|
||||
:min="0"
|
||||
:min="isEdit ? 0 : 1"
|
||||
:max="isEdit ? undefined : 50"
|
||||
:precision="0"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
@change="onCreateCountChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 单位 -->
|
||||
@@ -164,6 +172,21 @@
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 状态(编辑单条,对齐细胞产品库) -->
|
||||
<el-form-item v-if="isEdit" label="状态">
|
||||
<el-select
|
||||
v-model="formData.productStatus"
|
||||
style="width: 100%"
|
||||
:disabled="formData.productStatus !== 'frozen'"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in editStatusOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 产品图片 (full width) -->
|
||||
<el-form-item label="产品图片" class="form-col-span-2">
|
||||
<div class="attachment-upload">
|
||||
@@ -228,6 +251,45 @@
|
||||
<el-form-item label="备注" class="form-col-span-2">
|
||||
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="可选" />
|
||||
</el-form-item>
|
||||
<!-- 入库明细(新增多份) -->
|
||||
<div v-if="!isEdit" class="product-lines-section">
|
||||
<div class="product-lines-head">
|
||||
<span class="product-lines-title">入库明细</span>
|
||||
<span class="form-label-tip"
|
||||
>每份填写编码与储物格;多份时编码为「产品编码-01、-02…」</span
|
||||
>
|
||||
</div>
|
||||
<el-table :data="productLines" size="small" class="product-lines-table">
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column label="产品编码" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.productCode"
|
||||
placeholder="产品编码"
|
||||
@input="onLineProductCodeInput"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="储物格位置" min-width="280">
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-model="row.storageSlotId"
|
||||
placeholder="请选择储物格"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in storageSlotOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -258,10 +320,13 @@
|
||||
import { Plus, Close, Check, Delete, Download, Loading } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import { fetchNextCellProductCode } from '@/api/cell-product'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
|
||||
defineOptions({ name: 'TaskOutputProductDialog' })
|
||||
|
||||
type ProductLine = { productCode: string; storageSlotId: string }
|
||||
|
||||
type ProductImageFile = {
|
||||
id?: string
|
||||
fileName: string
|
||||
@@ -282,12 +347,21 @@
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
confirm: [data: Record<string, any>]
|
||||
confirm: [data: Record<string, any> | Record<string, any>[]]
|
||||
searchUnit: [query: string]
|
||||
}>()
|
||||
|
||||
const passageOptions = ['P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7', 'P8']
|
||||
|
||||
const editStatusOptions = [
|
||||
{ label: '已冻存', value: 'frozen' },
|
||||
{ label: '生产中', value: 'in_production' },
|
||||
{ label: '已售卖', value: 'sold' },
|
||||
{ label: '已使用', value: 'used' },
|
||||
{ label: '已作废', value: 'voided' },
|
||||
{ label: '已复苏', value: 'thawed' }
|
||||
]
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
function fullFileUrl(suffix: string) {
|
||||
if (!suffix) return ''
|
||||
@@ -296,13 +370,14 @@
|
||||
return `${minioBaseUrl}/${suffix}`
|
||||
}
|
||||
|
||||
const isEdit = computed(() => !!props.editItem?.cellProductId)
|
||||
const isEdit = computed(() => !!props.editItem)
|
||||
const formRef = ref()
|
||||
const submitLoading = ref(false)
|
||||
const productImageUploadRef = ref()
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const productImageFiles = ref<ProductImageFile[]>([])
|
||||
const productLines = ref<ProductLine[]>([{ productCode: '', storageSlotId: '' }])
|
||||
|
||||
const formData = reactive({
|
||||
cellProductId: '',
|
||||
@@ -319,44 +394,181 @@
|
||||
quantity: 1 as number,
|
||||
unit: '',
|
||||
expiryDate: '',
|
||||
productStatus: 'frozen',
|
||||
productImages: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
productCode: [{ required: true, message: '请输入产品编码', trigger: 'blur' }],
|
||||
productName: [{ required: true, message: '请输入产品名称', trigger: 'blur' }]
|
||||
productName: [{ required: true, message: '请输入产品名称', trigger: 'blur' }],
|
||||
batchNo: [{ required: true, message: '请输入批次号', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// Watch editItem to populate form
|
||||
watch(
|
||||
() => props.editItem,
|
||||
(item) => {
|
||||
if (item) {
|
||||
formData.cellProductId = item.cellProductId || ''
|
||||
formData.productCode = item.productCode || ''
|
||||
formData.productName = item.productName || ''
|
||||
formData.productTypeId = item.productTypeId || ''
|
||||
formData.specId = item.specId || ''
|
||||
formData.passage = item.passage || ''
|
||||
formData.batchNo = item.batchNo || ''
|
||||
formData.storageSlotId = item.storageSlotId || ''
|
||||
formData.expectedCellCount = item.expectedCellCount ?? undefined
|
||||
formData.viability = item.viability ?? undefined
|
||||
formData.freezeDate = item.freezeDate || ''
|
||||
formData.quantity = item.quantity ?? 1
|
||||
formData.unit = item.unit || ''
|
||||
formData.expiryDate = item.expiryDate || ''
|
||||
formData.productImages = item.productImages || ''
|
||||
formData.remark = item.remark || ''
|
||||
syncFormToImages(item.productImages || '')
|
||||
} else {
|
||||
resetForm()
|
||||
function normalizeDateValue(val: unknown): string {
|
||||
if (!val) return ''
|
||||
const s = String(val)
|
||||
return s.length >= 10 ? s.slice(0, 10) : s
|
||||
}
|
||||
|
||||
function populateFormFromEditItem(item: Record<string, any>) {
|
||||
formData.cellProductId = item.cellProductId || ''
|
||||
formData.productCode = item.productCode || item.outputBarcode || ''
|
||||
formData.productName = item.productName || item.outputName || ''
|
||||
formData.productTypeId = item.productTypeId || ''
|
||||
formData.specId = item.specId || ''
|
||||
formData.passage = item.passage || ''
|
||||
formData.batchNo = item.batchNo || ''
|
||||
formData.storageSlotId = item.storageSlotId || ''
|
||||
formData.expectedCellCount = item.expectedCellCount ?? undefined
|
||||
formData.viability = item.viability ?? undefined
|
||||
formData.freezeDate = normalizeDateValue(item.freezeDate)
|
||||
formData.quantity = item.quantity ?? 1
|
||||
formData.unit = item.unit || ''
|
||||
formData.expiryDate = normalizeDateValue(item.expiryDate)
|
||||
formData.productStatus = item.productStatus || 'frozen'
|
||||
formData.productImages = item.productImages || ''
|
||||
formData.remark = item.remark || ''
|
||||
syncFormToImages(item.productImages || '')
|
||||
}
|
||||
|
||||
async function initAddForm() {
|
||||
resetForm()
|
||||
formData.quantity = 1
|
||||
emit('searchUnit', '')
|
||||
await nextTick()
|
||||
formRef.value?.clearValidate?.()
|
||||
try {
|
||||
const res = await fetchNextCellProductCode()
|
||||
if (res?.code) {
|
||||
formData.productCode = res.code
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
} catch {
|
||||
/* 自动生成失败时用户可手动输入 */
|
||||
}
|
||||
applyProductCodeToLines()
|
||||
}
|
||||
|
||||
/** 按入库份数将主表单产品编码同步到入库明细(每份均带 -01、-02…,单份时为 -01) */
|
||||
function applyProductCodeToLines() {
|
||||
syncProductLines()
|
||||
syncLineCodesFromBase()
|
||||
}
|
||||
|
||||
function onDialogOpened() {
|
||||
if (props.editItem) {
|
||||
populateFormFromEditItem(props.editItem)
|
||||
emit('searchUnit', props.editItem.unit || '')
|
||||
nextTick(() => formRef.value?.clearValidate?.())
|
||||
} else {
|
||||
void initAddForm()
|
||||
}
|
||||
}
|
||||
|
||||
function createQuantityValue() {
|
||||
const n = Math.floor(Number(formData.quantity) || 0)
|
||||
if (n < 1) return 1
|
||||
return Math.min(n, 50)
|
||||
}
|
||||
|
||||
function stripProductCodeSuffix(code: string) {
|
||||
const c = (code || '').trim()
|
||||
const m = c.match(/^(.+)-(\d{2})$/)
|
||||
return m ? m[1].trim() : c
|
||||
}
|
||||
|
||||
function buildLineProductCode(base: string, index: number) {
|
||||
const b = stripProductCodeSuffix(base)
|
||||
if (!b) return ''
|
||||
return `${b}-${String(index + 1).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
function syncLineCodesFromBase() {
|
||||
const base = formData.productCode
|
||||
productLines.value.forEach((line, index) => {
|
||||
line.productCode = buildLineProductCode(base, index)
|
||||
})
|
||||
}
|
||||
|
||||
function syncProductLines() {
|
||||
const count = createQuantityValue()
|
||||
const prev = productLines.value.map((l) => ({ ...l }))
|
||||
const lines: ProductLine[] = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
lines.push({
|
||||
productCode: buildLineProductCode(formData.productCode, i),
|
||||
storageSlotId: prev[i]?.storageSlotId || ''
|
||||
})
|
||||
}
|
||||
productLines.value = lines.length ? lines : [{ productCode: '', storageSlotId: '' }]
|
||||
}
|
||||
|
||||
function onCreateCountChange() {
|
||||
if (!isEdit.value) {
|
||||
applyProductCodeToLines()
|
||||
}
|
||||
}
|
||||
|
||||
function onBaseProductCodeInput() {
|
||||
if (!isEdit.value) {
|
||||
applyProductCodeToLines()
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => formData.quantity,
|
||||
() => {
|
||||
if (!isEdit.value && props.modelValue) {
|
||||
applyProductCodeToLines()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function onLineProductCodeInput() {
|
||||
if (!isEdit.value && createQuantityValue() <= 1) {
|
||||
formData.productCode = stripProductCodeSuffix(productLines.value[0]?.productCode || '')
|
||||
}
|
||||
}
|
||||
|
||||
async function validateProductLines(): Promise<boolean> {
|
||||
if (isEdit.value) return true
|
||||
const count = createQuantityValue()
|
||||
for (let i = 0; i < productLines.value.length; i++) {
|
||||
const line = productLines.value[i]
|
||||
if (!line.productCode?.trim()) {
|
||||
ElMessage.warning(`请填写第 ${i + 1} 份产品编码`)
|
||||
return false
|
||||
}
|
||||
if (!line.storageSlotId) {
|
||||
ElMessage.warning(`请选择第 ${i + 1} 份产品的储物格位置`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (productLines.value.length !== count) {
|
||||
ElMessage.warning('入库明细行数与入库份数不一致,请调整入库份数')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function buildSharedResult(): Record<string, any> {
|
||||
return {
|
||||
productName: formData.productName.trim(),
|
||||
productTypeId: formData.productTypeId || undefined,
|
||||
specId: formData.specId || undefined,
|
||||
passage: formData.passage || undefined,
|
||||
batchNo: formData.batchNo.trim(),
|
||||
expectedCellCount: formData.expectedCellCount ?? undefined,
|
||||
viability: formData.viability ?? undefined,
|
||||
freezeDate: formData.freezeDate || undefined,
|
||||
unit: formData.unit?.trim() || undefined,
|
||||
expiryDate: formData.expiryDate || undefined,
|
||||
productImages: formData.productImages?.trim() || undefined,
|
||||
productStatus: formData.productStatus || 'frozen',
|
||||
remark: formData.remark?.trim() || undefined
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formData.cellProductId = ''
|
||||
formData.productCode = ''
|
||||
@@ -372,9 +584,11 @@
|
||||
formData.quantity = 1
|
||||
formData.unit = ''
|
||||
formData.expiryDate = ''
|
||||
formData.productStatus = 'frozen'
|
||||
formData.productImages = ''
|
||||
formData.remark = ''
|
||||
productImageFiles.value = []
|
||||
productLines.value = [{ productCode: '', storageSlotId: '' }]
|
||||
}
|
||||
|
||||
function fileNameFromUrl(url: string, index: number) {
|
||||
@@ -464,30 +678,29 @@
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!(await validateProductLines())) return
|
||||
await formRef.value?.validate?.()
|
||||
syncImagesToForm()
|
||||
submitLoading.value = true
|
||||
try {
|
||||
const result: Record<string, any> = {
|
||||
cellProductId: formData.cellProductId || undefined,
|
||||
productCode: formData.productCode.trim(),
|
||||
productName: formData.productName.trim(),
|
||||
productTypeId: formData.productTypeId || undefined,
|
||||
specId: formData.specId || undefined,
|
||||
passage: formData.passage || undefined,
|
||||
batchNo: formData.batchNo.trim() || undefined,
|
||||
storageSlotId: formData.storageSlotId || undefined,
|
||||
expectedCellCount: formData.expectedCellCount ?? undefined,
|
||||
viability: formData.viability ?? undefined,
|
||||
freezeDate: formData.freezeDate || undefined,
|
||||
quantity: formData.quantity,
|
||||
unit: formData.unit?.trim() || undefined,
|
||||
expiryDate: formData.expiryDate || undefined,
|
||||
productImages: formData.productImages?.trim() || undefined,
|
||||
productStatus: 'frozen',
|
||||
remark: formData.remark?.trim() || undefined
|
||||
const shared = buildSharedResult()
|
||||
if (isEdit.value) {
|
||||
emit('confirm', {
|
||||
...shared,
|
||||
cellProductId: formData.cellProductId || undefined,
|
||||
productCode: formData.productCode.trim(),
|
||||
storageSlotId: formData.storageSlotId || undefined,
|
||||
quantity: formData.quantity
|
||||
})
|
||||
} else {
|
||||
const results = productLines.value.map((line) => ({
|
||||
...shared,
|
||||
productCode: line.productCode.trim(),
|
||||
storageSlotId: line.storageSlotId,
|
||||
quantity: 1
|
||||
}))
|
||||
emit('confirm', results)
|
||||
}
|
||||
emit('confirm', result)
|
||||
emit('update:modelValue', false)
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
@@ -651,4 +864,27 @@
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.product-lines-section {
|
||||
grid-column: 1 / -1;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.product-lines-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.product-lines-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--mint-text, #334155);
|
||||
}
|
||||
|
||||
.product-lines-table {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="page-container">
|
||||
<header class="page-head">
|
||||
<h1>入库生产工单</h1>
|
||||
<p class="page-desc">入库生产工单管理,查看工单详情、派发生产任务并完成待配置工单。</p>
|
||||
<p class="page-desc">入库生产工单管理,查看工单详情、派发生产任务并完成工单。</p>
|
||||
</header>
|
||||
|
||||
<section class="panel">
|
||||
@@ -102,7 +102,7 @@
|
||||
><el-icon><EditPen /></el-icon>编辑任务</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="row.workOrderStatus === 'dpz'"
|
||||
v-if="row.workOrderStatus === 'dpz' || row.workOrderStatus === 'jxz'"
|
||||
class="btn-op-item primary"
|
||||
@click="handleOpenDispatch(row)"
|
||||
>
|
||||
|
||||
@@ -107,6 +107,10 @@ export function useTaskStepEditor() {
|
||||
passage?: string
|
||||
storageSlotName?: string
|
||||
productStatus?: string
|
||||
batchNo?: string
|
||||
specName?: string
|
||||
expectedCellCount?: number
|
||||
viability?: number
|
||||
}>
|
||||
>([])
|
||||
const cellProductOptionsLoading = ref(false)
|
||||
@@ -391,7 +395,11 @@ export function useTaskStepEditor() {
|
||||
productTypeName: p.productTypeName || '',
|
||||
passage: p.passage || '',
|
||||
storageSlotName: p.storageSlotName || '',
|
||||
productStatus: p.productStatus || ''
|
||||
productStatus: p.productStatus || '',
|
||||
batchNo: p.batchNo || '',
|
||||
specName: p.specName || '',
|
||||
expectedCellCount: p.expectedCellCount ?? undefined,
|
||||
viability: p.viability ?? undefined
|
||||
}))
|
||||
for (const row of taskInputs.value) {
|
||||
if (
|
||||
@@ -570,6 +578,25 @@ export function useTaskStepEditor() {
|
||||
loadStorageSlotOptions()
|
||||
])
|
||||
}
|
||||
|
||||
function enrichTaskOutputRow(row: Record<string, any>) {
|
||||
const productType = productTypeOptions.value.find((o) => o.id === row.productTypeId)
|
||||
const spec = specOptions.value.find((o) => o.id === row.specId)
|
||||
const slot = storageSlotOptions.value.find((o) => o.id === row.storageSlotId)
|
||||
const productCode = row.productCode || row.outputBarcode || ''
|
||||
const productName = row.productName || row.outputName || ''
|
||||
return {
|
||||
...row,
|
||||
productCode,
|
||||
productName,
|
||||
outputBarcode: productCode,
|
||||
outputName: productName,
|
||||
productTypeName: row.productTypeName || productType?.productTypeName || '',
|
||||
specName: row.specName || spec?.specName || '',
|
||||
storageSlotName: row.storageSlotName || slot?.name || ''
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTaskDetail(overrideId?: string) {
|
||||
const id = overrideId || taskId.value
|
||||
if (!id) return
|
||||
@@ -586,7 +613,9 @@ export function useTaskStepEditor() {
|
||||
if (mode.value === 'execution') {
|
||||
taskDetail.value = detail
|
||||
taskInputs.value = (detail.inputs || []).map((i: Record<string, any>) => ({ ...i }))
|
||||
taskOutputs.value = (detail.outputs || []).map((o: Record<string, any>) => ({ ...o }))
|
||||
taskOutputs.value = (detail.outputs || []).map((o: Record<string, any>) =>
|
||||
enrichTaskOutputRow(o)
|
||||
)
|
||||
for (const row of taskInputs.value) {
|
||||
if (
|
||||
row.inputType === 'cell_product' &&
|
||||
@@ -1007,10 +1036,6 @@ export function useTaskStepEditor() {
|
||||
}
|
||||
async function saveTaskOutputs() {
|
||||
if (!taskDetail.value?.id) return
|
||||
if (!taskOutputs.value.length) {
|
||||
ElMessage.warning('请至少添加一项产出(细胞产品)')
|
||||
return
|
||||
}
|
||||
outputSaving.value = true
|
||||
try {
|
||||
await fetchSetTaskOutputs(taskDetail.value.id, taskOutputs.value)
|
||||
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
<!-- 入库放行审核 — 审核弹窗(支持单条和批量) -->
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
:title="dialogTitle"
|
||||
width="640px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
style="backdrop-filter: none"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
@closed="resetForm"
|
||||
>
|
||||
<div v-if="isSingleMode && singleDetail" class="review-info-summary">
|
||||
<div class="review-summary-row">
|
||||
<span class="review-summary-label">协议编号</span>
|
||||
<span class="review-summary-value">{{ singleDetail.review?.protocolNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="review-summary-row">
|
||||
<span class="review-summary-label">样本编码</span>
|
||||
<span class="review-summary-value">{{ singleDetail.review?.sampleCode || '—' }}</span>
|
||||
</div>
|
||||
<div class="review-summary-row">
|
||||
<span class="review-summary-label">样本名称</span>
|
||||
<span class="review-summary-value">{{ singleDetail.review?.sampleName || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else-if="reviewIds.length > 1" class="audit-target-hint">
|
||||
将对已选
|
||||
<strong>{{ reviewIds.length }}</strong> 条记录提交相同审核结果。
|
||||
</p>
|
||||
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="biz-dialog-form"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item label="审核结论" prop="reviewConclusion">
|
||||
<el-radio-group v-model="form.reviewConclusion">
|
||||
<el-radio value="tg">通过</el-radio>
|
||||
<el-radio value="btg">不通过</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
v-model="form.reviewRemark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="可选"
|
||||
maxlength="1000"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="附件">
|
||||
<div class="attachment-upload-area">
|
||||
<div class="attachment-list">
|
||||
<el-upload
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:multiple="true"
|
||||
accept="image/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.txt,.zip"
|
||||
@change="handleAttachmentChange"
|
||||
>
|
||||
<div class="upload-card">
|
||||
<el-icon :size="20" color="#10b981"><Plus /></el-icon>
|
||||
<span class="upload-card-text">上传附件</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div
|
||||
v-for="(file, fIdx) in attachmentFiles"
|
||||
:key="file.id || fIdx"
|
||||
class="attachment-item"
|
||||
:class="{ 'is-uploading': file._uploading }"
|
||||
>
|
||||
<div class="attachment-preview">
|
||||
<template v-if="isImageFile(file) && file.fileUrl">
|
||||
<img :src="fullFileUrl(file.fileUrl)" :alt="file.fileName" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="attachment-preview__icon attachment-preview__icon--doc">
|
||||
<el-icon :size="22"><Document /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="attachment-footer">
|
||||
<span class="attachment-name" :title="file.fileName">{{ file.fileName }}</span>
|
||||
<div class="attachment-actions">
|
||||
<el-icon class="action-btn" title="删除" @click.stop="removeAttachment(fIdx)">
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="upload-hint">支持图片、文档等,单文件不超过 20MB。</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="emit('update:modelValue', false)">
|
||||
<el-icon><Close /></el-icon>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="submitting" @click="handleSubmit">
|
||||
<el-icon><Check /></el-icon>
|
||||
确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Check, Close, Plus, Document, Delete } from '@element-plus/icons-vue'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import {
|
||||
fetchStorageReleaseReviewDetail,
|
||||
submitStorageReleaseReview,
|
||||
batchReviewStorageRelease,
|
||||
type StorageReleaseReviewDetail
|
||||
} from '@/api/storage-release-review'
|
||||
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
|
||||
|
||||
type AttachmentItem = {
|
||||
id: string
|
||||
fileName: string
|
||||
fileUrl: string
|
||||
fileType: string
|
||||
_uploading?: boolean
|
||||
}
|
||||
|
||||
defineOptions({ name: 'StorageReleaseReviewDialog' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
reviewIds: string[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', val: boolean): void
|
||||
(e: 'submitted'): void
|
||||
}>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const submitting = ref(false)
|
||||
const singleDetail = ref<StorageReleaseReviewDetail | null>(null)
|
||||
const attachmentFiles = ref<AttachmentItem[]>([])
|
||||
|
||||
const isSingleMode = computed(() => props.reviewIds.length === 1)
|
||||
|
||||
const form = reactive({
|
||||
reviewConclusion: 'tg',
|
||||
reviewRemark: ''
|
||||
})
|
||||
|
||||
const rules: FormRules = {
|
||||
reviewConclusion: [{ required: true, message: '请选择审核结论', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const dialogTitle = computed(() =>
|
||||
props.reviewIds.length > 1 ? `批量审核(${props.reviewIds.length} 条)` : '入库放行审核'
|
||||
)
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
function fullFileUrl(url: string) {
|
||||
if (!url) return ''
|
||||
if (/^https?:\/\//i.test(url)) return url
|
||||
return minioBaseUrl ? `${minioBaseUrl}/${url.replace(/^\//, '')}` : url
|
||||
}
|
||||
|
||||
function isImageFile(file: AttachmentItem) {
|
||||
const t = file.fileType || ''
|
||||
const n = file.fileName || ''
|
||||
return t.startsWith('image/') || /\.(png|jpe?g|gif|webp|bmp|svg)$/i.test(n)
|
||||
}
|
||||
|
||||
function removeAttachment(index: number) {
|
||||
attachmentFiles.value.splice(index, 1)
|
||||
}
|
||||
|
||||
let _pendingList: AttachmentItem[] | null = null
|
||||
|
||||
function _getList(): AttachmentItem[] {
|
||||
if (_pendingList) return _pendingList
|
||||
return attachmentFiles.value
|
||||
}
|
||||
|
||||
async function handleAttachmentChange(uploadFile: { raw?: File }) {
|
||||
const file = uploadFile.raw
|
||||
if (!file) return
|
||||
const maxSizeMb = 20
|
||||
if (file.size / 1024 / 1024 > maxSizeMb) {
|
||||
ElMessage.error(`文件大小不能超过 ${maxSizeMb}MB`)
|
||||
return
|
||||
}
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
||||
const uploading: AttachmentItem = {
|
||||
id: tempId,
|
||||
fileName: file.name,
|
||||
fileUrl: '',
|
||||
fileType: file.type || '',
|
||||
_uploading: true
|
||||
}
|
||||
const current = _getList()
|
||||
const listWithTemp = [...current, uploading]
|
||||
_pendingList = listWithTemp
|
||||
attachmentFiles.value = listWithTemp
|
||||
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('folder', 'storage-release-review')
|
||||
fd.append('file', file)
|
||||
const res = await fetchUploadAttachment(fd)
|
||||
const latest = _pendingList || attachmentFiles.value
|
||||
const updated = latest.map((a) =>
|
||||
a.id === tempId
|
||||
? {
|
||||
id: res.id || tempId,
|
||||
fileName: file.name || res.fileName,
|
||||
fileUrl: res.fileUrl,
|
||||
fileType: res.fileType || file.type || '',
|
||||
_uploading: false
|
||||
}
|
||||
: a
|
||||
)
|
||||
_pendingList = updated
|
||||
attachmentFiles.value = updated
|
||||
} catch {
|
||||
const latest = _pendingList || attachmentFiles.value
|
||||
const cleaned = latest.filter((a) => a.id !== tempId)
|
||||
_pendingList = cleaned
|
||||
attachmentFiles.value = cleaned
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.modelValue, props.reviewIds] as const,
|
||||
async ([vis, ids]) => {
|
||||
if (!vis || !ids.length) {
|
||||
singleDetail.value = null
|
||||
return
|
||||
}
|
||||
if (ids.length === 1) {
|
||||
try {
|
||||
singleDetail.value = await fetchStorageReleaseReviewDetail(ids[0])
|
||||
} catch {
|
||||
singleDetail.value = null
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function resetForm() {
|
||||
form.reviewConclusion = 'tg'
|
||||
form.reviewRemark = ''
|
||||
attachmentFiles.value = []
|
||||
_pendingList = null
|
||||
singleDetail.value = null
|
||||
formRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
const valid = await formRef.value?.validate().catch(() => false)
|
||||
if (!valid || !props.reviewIds.length) return
|
||||
|
||||
const attachmentUrls = attachmentFiles.value
|
||||
.filter((f) => f.fileUrl)
|
||||
.map((f) => f.fileUrl)
|
||||
.join(',')
|
||||
|
||||
const payload = {
|
||||
reviewConclusion: form.reviewConclusion,
|
||||
reviewRemark: form.reviewRemark.trim() || undefined,
|
||||
reviewAttachments: attachmentUrls || undefined
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
if (props.reviewIds.length === 1) {
|
||||
await submitStorageReleaseReview(props.reviewIds[0], payload)
|
||||
ElMessage.success('审核成功')
|
||||
} else {
|
||||
await withFullscreenLoading(async () => {
|
||||
await batchReviewStorageRelease({
|
||||
ids: props.reviewIds,
|
||||
...payload
|
||||
})
|
||||
ElMessage.success(`批量审核成功,共 ${props.reviewIds.length} 条`)
|
||||
}, fullscreenLoadingText.save)
|
||||
}
|
||||
emit('update:modelValue', false)
|
||||
emit('submitted')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.audit-target-hint {
|
||||
margin: 0 0 16px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
|
||||
strong {
|
||||
color: #1e293b;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.review-info-summary {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
background: #f8fafc;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px 20px;
|
||||
}
|
||||
|
||||
.review-summary-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.review-summary-label {
|
||||
color: #64748b;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.review-summary-value {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.attachment-upload-area {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.attachment-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.upload-card {
|
||||
width: 120px;
|
||||
height: 88px;
|
||||
border: 1px dashed #a7f3d0;
|
||||
border-radius: 8px;
|
||||
background: #f0fdf4;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s;
|
||||
|
||||
&:hover {
|
||||
border-color: #10b981;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-card-text {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.attachment-item {
|
||||
width: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
|
||||
&.is-uploading {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8fafc;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-preview__icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&--doc {
|
||||
background: #e0f2fe;
|
||||
color: #0284c7;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.attachment-name {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 11px;
|
||||
color: #334155;
|
||||
line-height: 1.4;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.attachment-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
cursor: pointer;
|
||||
background: #f1f5f9;
|
||||
transition:
|
||||
background 0.15s,
|
||||
color 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
margin: 8px 0 0;
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
</style>
|
||||
+403
@@ -0,0 +1,403 @@
|
||||
<!-- 入库放行审核 — 只读查看 -->
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<SystemViewDialog
|
||||
v-model:visible="innerVisible"
|
||||
title="入库放行审核详情"
|
||||
width="960px"
|
||||
:grid-columns="2"
|
||||
:icon="CircleCheck"
|
||||
:meta="detail?.review?.protocolNo || ''"
|
||||
:sections="viewSections"
|
||||
:loading="loading"
|
||||
append-to-body
|
||||
>
|
||||
<template #qc-tasks>
|
||||
<div v-if="detail?.qcInfo?.tasks?.length" class="view-section-slot">
|
||||
<div class="qc-task-cards">
|
||||
<div v-for="task in detail.qcInfo.tasks" :key="task.id" class="qc-task-card">
|
||||
<div class="qc-task-card__header">
|
||||
<span class="qc-task-card__type">
|
||||
<el-tag type="info" size="small">{{
|
||||
task.taskTypeLabel || task.taskType || '—'
|
||||
}}</el-tag>
|
||||
</span>
|
||||
<span class="qc-task-card__conclusion">
|
||||
<el-tag
|
||||
v-if="task.conclusion"
|
||||
:type="task.conclusion === 'tg' ? 'success' : 'danger'"
|
||||
size="small"
|
||||
>{{ task.conclusionLabel || task.conclusion }}</el-tag
|
||||
>
|
||||
<span v-else class="qc-kv__muted">—</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="qc-task-card__body">
|
||||
<div class="qc-kv">
|
||||
<span class="qc-kv__label">任务状态</span>
|
||||
<span class="qc-kv__value">
|
||||
<el-tag
|
||||
:type="
|
||||
task.taskStatus === 'ywc'
|
||||
? 'success'
|
||||
: task.taskStatus === 'jxz'
|
||||
? 'warning'
|
||||
: 'info'
|
||||
"
|
||||
size="small"
|
||||
>{{ task.taskStatusLabel || task.taskStatus || '—' }}</el-tag
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="view-section-slot"><p class="view-empty-hint">暂无质检任务</p></div>
|
||||
</template>
|
||||
|
||||
<template #production-tasks>
|
||||
<div v-if="detail?.productionInfo?.tasks?.length" class="view-section-slot">
|
||||
<div class="qc-task-cards">
|
||||
<div v-for="task in detail.productionInfo.tasks" :key="task.id" class="qc-task-card">
|
||||
<div class="qc-task-card__header">
|
||||
<span class="qc-task-card__name">{{ task.taskName || task.taskNo || '—' }}</span>
|
||||
<span class="qc-task-card__conclusion">
|
||||
<el-tag
|
||||
:type="
|
||||
task.taskStatus === 'ywc'
|
||||
? 'success'
|
||||
: task.taskStatus === 'jxz'
|
||||
? 'warning'
|
||||
: 'info'
|
||||
"
|
||||
size="small"
|
||||
>{{ task.taskStatusLabel || task.taskStatus || '—' }}</el-tag
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<div class="qc-task-card__body">
|
||||
<div class="qc-kv">
|
||||
<span class="qc-kv__label">任务号</span>
|
||||
<span class="qc-kv__value">{{ task.taskNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="qc-kv">
|
||||
<span class="qc-kv__label">开始时间</span>
|
||||
<span class="qc-kv__value">{{ task.startedTime || '—' }}</span>
|
||||
</div>
|
||||
<div class="qc-kv">
|
||||
<span class="qc-kv__label">完成时间</span>
|
||||
<span class="qc-kv__value">{{ task.completedTime || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="view-section-slot"><p class="view-empty-hint">暂无生产任务</p></div>
|
||||
</template>
|
||||
|
||||
<template #review-attachments>
|
||||
<div class="view-section-slot">
|
||||
<ProtocolAttachmentReadonly
|
||||
:files="reviewAttachmentFiles"
|
||||
@preview="handleAttachmentPreview"
|
||||
@download="handleAttachmentDownload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</SystemViewDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CircleCheck } from '@element-plus/icons-vue'
|
||||
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
|
||||
import type { ViewSection } from '@/components/business/system-view-dialog/index.vue'
|
||||
import ProtocolAttachmentReadonly from '@/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue'
|
||||
import {
|
||||
fetchStorageReleaseReviewDetail,
|
||||
type StorageReleaseReview,
|
||||
type StorageReleaseReviewDetail,
|
||||
type ProtocolInfo,
|
||||
type TransportInfo,
|
||||
type ReceptionInfo,
|
||||
type QcInfo,
|
||||
type ProductionInfo
|
||||
} from '@/api/storage-release-review'
|
||||
import {
|
||||
receptionSourceLabel,
|
||||
receptionSourceTagType,
|
||||
storageReleaseConclusionLabel,
|
||||
storageReleaseConclusionTagType,
|
||||
acceptConclusionLabel,
|
||||
acceptConclusionTagType,
|
||||
transportTypeLabel
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
reviewId: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void
|
||||
}>()
|
||||
|
||||
const innerVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: (val) => emit('update:visible', val)
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const detail = ref<StorageReleaseReviewDetail | null>(null)
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
watch(
|
||||
() => [props.visible, props.reviewId] as const,
|
||||
async ([vis, id]) => {
|
||||
if (!vis || !id) {
|
||||
detail.value = null
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
detail.value = await fetchStorageReleaseReviewDetail(id)
|
||||
} catch {
|
||||
detail.value = null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function formatVal(v: unknown) {
|
||||
if (v === null || v === undefined || v === '') return '—'
|
||||
return String(v)
|
||||
}
|
||||
|
||||
const reviewAttachmentFiles = computed(() => {
|
||||
const urls = detail.value?.review?.reviewAttachments || ''
|
||||
if (!urls.trim()) return []
|
||||
return urls
|
||||
.split(',')
|
||||
.map((u) => u.trim())
|
||||
.filter(Boolean)
|
||||
.map((fileUrl, i) => ({
|
||||
id: `att_${i}`,
|
||||
fileUrl,
|
||||
fileName: fileUrl.split('/').pop() || ''
|
||||
}))
|
||||
})
|
||||
|
||||
const viewSections = computed<ViewSection[]>(() => {
|
||||
const d = detail.value
|
||||
if (!d) return []
|
||||
const rev: Partial<StorageReleaseReview> = d.review || {}
|
||||
const proto: Partial<ProtocolInfo> = d.protocolInfo || {}
|
||||
const transport: Partial<TransportInfo> = d.transportInfo || {}
|
||||
const reception: Partial<ReceptionInfo> = d.receptionInfo || {}
|
||||
const qc: Partial<QcInfo> = d.qcInfo || {}
|
||||
const production: Partial<ProductionInfo> = d.productionInfo || {}
|
||||
|
||||
const reviewConclusionValue = rev.reviewConclusion || ''
|
||||
const reviewConclusionDisplay = reviewConclusionValue
|
||||
? storageReleaseConclusionLabel(reviewConclusionValue)
|
||||
: '待审核'
|
||||
const reviewConclusionTagType = reviewConclusionValue
|
||||
? storageReleaseConclusionTagType(reviewConclusionValue)
|
||||
: 'info'
|
||||
|
||||
return [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{ label: '协议编号', value: proto.protocolNo || rev.protocolNo || '—' },
|
||||
{
|
||||
label: '协议类别',
|
||||
value: receptionSourceLabel(rev.receptionSource || ''),
|
||||
tagType: receptionSourceTagType(rev.receptionSource || '')
|
||||
},
|
||||
{ label: '样本编码', value: proto.sampleCode || rev.sampleCode || '—' },
|
||||
{ label: '样本名称', value: rev.sampleName || '—' },
|
||||
{ label: '样本类型', value: proto.sampleTypeName || '—' },
|
||||
{ label: '母亲姓名', value: proto.motherName || '—' },
|
||||
{ label: '母亲电话', value: proto.motherPhone || '—' },
|
||||
{ label: '新生儿姓名', value: proto.newbornName || '—' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '运输信息',
|
||||
fields: transport.transportType
|
||||
? [
|
||||
{ label: '运输方式', value: transportTypeLabel(transport.transportType) },
|
||||
...(transport.transportType === 'kdys'
|
||||
? [{ label: '快递单号', value: transport.waybillNo || '—' }]
|
||||
: [
|
||||
{ label: '携带人', value: transport.carrierName || '—' },
|
||||
{ label: '携带人电话', value: transport.carrierPhone || '—' }
|
||||
]),
|
||||
{ label: '预计到达', value: transport.etaTime || '—' }
|
||||
]
|
||||
: []
|
||||
},
|
||||
{
|
||||
title: '接收信息',
|
||||
fields: reception.receptionTime
|
||||
? [
|
||||
{ label: '接收时间', value: reception.receptionTime || '—' },
|
||||
{ label: '接收经手人', value: reception.handlerName || '—' },
|
||||
{
|
||||
label: '接收结论',
|
||||
value: acceptConclusionLabel(reception.acceptConclusion || ''),
|
||||
tagType: acceptConclusionTagType(reception.acceptConclusion || '')
|
||||
},
|
||||
{ label: '接收温度(℃)', value: formatVal(reception.receiveTemperature) },
|
||||
{ label: '体积(ml)', value: formatVal(reception.volumeMl) }
|
||||
]
|
||||
: []
|
||||
},
|
||||
{
|
||||
title: '质检信息',
|
||||
slot: 'qc-tasks',
|
||||
fields: [
|
||||
{ label: '工单号', value: qc.workOrderNo || '—' },
|
||||
{
|
||||
label: '工单状态',
|
||||
value:
|
||||
qc.workOrderStatus === 'ywc'
|
||||
? '已完成'
|
||||
: qc.workOrderStatus === 'ypz'
|
||||
? '已配置'
|
||||
: qc.workOrderStatus || '—',
|
||||
tagType:
|
||||
qc.workOrderStatus === 'ywc'
|
||||
? 'success'
|
||||
: qc.workOrderStatus === 'ypz'
|
||||
? 'warning'
|
||||
: 'info'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '生产信息',
|
||||
slot: 'production-tasks',
|
||||
fields: [
|
||||
{ label: '工单号', value: production.workOrderNo || '—' },
|
||||
{
|
||||
label: '工单状态',
|
||||
value:
|
||||
production.workOrderStatus === 'ywc'
|
||||
? '已完成'
|
||||
: production.workOrderStatus === 'jxz'
|
||||
? '执行中'
|
||||
: production.workOrderStatus || '—',
|
||||
tagType:
|
||||
production.workOrderStatus === 'ywc'
|
||||
? 'success'
|
||||
: production.workOrderStatus === 'jxz'
|
||||
? 'warning'
|
||||
: 'info'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '放行审核',
|
||||
slot: 'review-attachments',
|
||||
fields: [
|
||||
{
|
||||
label: '审核结论',
|
||||
value: reviewConclusionDisplay,
|
||||
tagType: reviewConclusionTagType
|
||||
},
|
||||
{ label: '审核人', value: rev.reviewByName || '—' },
|
||||
{ label: '审核时间', value: rev.reviewTime || '—' },
|
||||
{ label: '审核备注', value: rev.reviewRemark || '—', span: true }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
function attachmentFullUrl(url: string) {
|
||||
const u = String(url || '').trim()
|
||||
if (!u) return ''
|
||||
if (/^https?:\/\//i.test(u)) return u
|
||||
return minioBaseUrl ? `${minioBaseUrl}/${u.replace(/^\//, '')}` : u
|
||||
}
|
||||
|
||||
function handleAttachmentPreview(file: Record<string, unknown>) {
|
||||
const url = attachmentFullUrl(String(file.fileUrl || ''))
|
||||
if (url) window.open(url, '_blank')
|
||||
}
|
||||
|
||||
function handleAttachmentDownload(file: Record<string, unknown>) {
|
||||
downloadAttachmentFile(file, { fullFileUrl: attachmentFullUrl })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.view-empty-hint {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.qc-task-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.qc-task-card {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.qc-task-card__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.qc-task-card__name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.qc-task-card__body {
|
||||
padding: 10px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.qc-kv {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.qc-kv__label {
|
||||
color: #94a3b8;
|
||||
flex-shrink: 0;
|
||||
min-width: 56px;
|
||||
}
|
||||
|
||||
.qc-kv__value {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.qc-kv__muted {
|
||||
color: #94a3b8;
|
||||
font-weight: 400;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,398 @@
|
||||
<!-- 入库放行审核 -->
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<header class="page-head">
|
||||
<h1>入库放行审核</h1>
|
||||
<p class="page-desc">审核入库放行记录,管理样本入库流程。</p>
|
||||
</header>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-top">
|
||||
<h2>审核列表</h2>
|
||||
</div>
|
||||
|
||||
<div class="filter-block">
|
||||
<el-form :model="searchForm" class="filter-form" @submit.prevent>
|
||||
<div class="filter-grid">
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="协议号/样本编码/样本名称"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="协议类别">
|
||||
<el-select
|
||||
v-model="searchForm.receptionSource"
|
||||
clearable
|
||||
placeholder="全部"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in receptionSourceOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核结论">
|
||||
<el-select
|
||||
v-model="searchForm.reviewConclusion"
|
||||
clearable
|
||||
placeholder="全部"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="待审核" value="" />
|
||||
<el-option
|
||||
v-for="item in reviewConclusionOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="入库状态">
|
||||
<el-select
|
||||
v-model="searchForm.storageStatus"
|
||||
clearable
|
||||
placeholder="全部"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in storageStatusOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="filter-actions-row">
|
||||
<span class="filter-hint">支持按协议号、样本编码、样本名称搜索。</span>
|
||||
<div class="filter-actions-end">
|
||||
<el-button class="btn-ghost" @click="handleReset">
|
||||
<el-icon><RefreshLeft /></el-icon>重置
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>搜索
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="list-toolbar">
|
||||
<div class="list-toolbar-row">
|
||||
<div class="list-toolbar-selected">
|
||||
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
|
||||
</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="btn-main"
|
||||
:disabled="!canBatchReview"
|
||||
@click="openReviewDialog()"
|
||||
>
|
||||
<el-icon><CircleCheck /></el-icon>
|
||||
批量审核
|
||||
</el-button>
|
||||
<el-button class="btn-ghost" :disabled="!canBatchStore" @click="handleBatchStore">
|
||||
<el-icon><Box /></el-icon>
|
||||
批量入库
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-body list-wrap">
|
||||
<el-table
|
||||
class="list-table"
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
@row-dblclick="handleView"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" header-align="center" />
|
||||
<el-table-column
|
||||
prop="protocolNo"
|
||||
label="协议编号"
|
||||
width="160"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="sampleCode"
|
||||
label="样本编码"
|
||||
width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="sampleName"
|
||||
label="样本名称"
|
||||
width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="receptionSource"
|
||||
label="协议类别"
|
||||
width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="receptionSourceTagType(row.receptionSource || '')" size="small">
|
||||
{{ receptionSourceLabel(row.receptionSource || '') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="reviewConclusion"
|
||||
label="审核结论"
|
||||
width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.reviewConclusion"
|
||||
:type="storageReleaseConclusionTagType(row.reviewConclusion)"
|
||||
size="small"
|
||||
>
|
||||
{{ storageReleaseConclusionLabel(row.reviewConclusion) }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="info" size="small">待审核</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="storageStatus"
|
||||
label="入库状态"
|
||||
width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="storageStatusTagType(row.storageStatus || 'drk')" size="small">
|
||||
{{ storageStatusLabel(row.storageStatus || 'drk') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="reviewByName"
|
||||
label="审核人"
|
||||
width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="reviewTime"
|
||||
label="审核时间"
|
||||
width="170"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
min-width="260"
|
||||
fixed="right"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-button class="btn-op-item" @click="handleView(row)">
|
||||
<el-icon><View /></el-icon>查看
|
||||
</el-button>
|
||||
<el-button class="btn-op-item" @click="openReviewDialog(row)">
|
||||
<el-icon><CircleCheck /></el-icon>审核
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.reviewConclusion === 'tg' && row.storageStatus === 'drk'"
|
||||
class="btn-op-item"
|
||||
@click="handleStore(row)"
|
||||
>
|
||||
<el-icon><Box /></el-icon>样本入库
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination list-pager-bar">
|
||||
<el-pagination
|
||||
:current-page="pagination.page"
|
||||
:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
|
||||
@current-change="(val) => ((pagination.page = val), loadData())"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StorageReleaseViewDialog v-model:visible="viewVisible" :review-id="viewReviewId" />
|
||||
|
||||
<StorageReleaseReviewDialog
|
||||
v-model="reviewDialogVisible"
|
||||
:review-ids="reviewDialogIds"
|
||||
@submitted="onReviewSubmitted"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { View, Search, RefreshLeft, CircleCheck, Box } from '@element-plus/icons-vue'
|
||||
import StorageReleaseViewDialog from './StorageReleaseViewDialog.vue'
|
||||
import StorageReleaseReviewDialog from './StorageReleaseReviewDialog.vue'
|
||||
import {
|
||||
fetchStorageReleaseReviewList,
|
||||
storeSample,
|
||||
batchStoreSample,
|
||||
type StorageReleaseReview
|
||||
} from '@/api/storage-release-review'
|
||||
import {
|
||||
receptionSourceLabel,
|
||||
receptionSourceTagType,
|
||||
storageReleaseConclusionLabel,
|
||||
storageReleaseConclusionTagType,
|
||||
storageStatusLabel,
|
||||
storageStatusTagType
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
|
||||
|
||||
defineOptions({ name: 'QualityInspectionStorageReleaseReview' })
|
||||
|
||||
const receptionSourceOptions = [
|
||||
{ value: 'cell_storage', label: '细胞存储' },
|
||||
{ value: 'research_donation', label: '科研捐赠' }
|
||||
]
|
||||
|
||||
const reviewConclusionOptions = [
|
||||
{ value: 'tg', label: '通过' },
|
||||
{ value: 'btg', label: '不通过' }
|
||||
]
|
||||
|
||||
const storageStatusOptions = [
|
||||
{ value: 'drk', label: '待入库' },
|
||||
{ value: 'yrk', label: '已入库' }
|
||||
]
|
||||
|
||||
const searchForm = reactive({
|
||||
receptionSource: '',
|
||||
reviewConclusion: '',
|
||||
storageStatus: '',
|
||||
keyword: ''
|
||||
})
|
||||
const tableData = ref<StorageReleaseReview[]>([])
|
||||
const selectedRows = ref<StorageReleaseReview[]>([])
|
||||
const loading = ref(false)
|
||||
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
|
||||
|
||||
const handleSelectionChange = (rows: StorageReleaseReview[]) => {
|
||||
selectedRows.value = rows
|
||||
}
|
||||
|
||||
const canBatchReview = computed(() => selectedRows.value.length > 0)
|
||||
|
||||
const canBatchStore = computed(
|
||||
() =>
|
||||
selectedRows.value.length > 0 &&
|
||||
selectedRows.value.every((r) => r.reviewConclusion === 'tg' && r.storageStatus === 'drk')
|
||||
)
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await fetchStorageReleaseReviewList({
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize,
|
||||
receptionSource: searchForm.receptionSource || undefined,
|
||||
reviewConclusion: searchForm.reviewConclusion || undefined,
|
||||
storageStatus: searchForm.storageStatus || undefined,
|
||||
keyword: searchForm.keyword || undefined
|
||||
})
|
||||
tableData.value = res.list || []
|
||||
pagination.total = res.total ?? 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.receptionSource = ''
|
||||
searchForm.reviewConclusion = ''
|
||||
searchForm.storageStatus = ''
|
||||
searchForm.keyword = ''
|
||||
pagination.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
const viewVisible = ref(false)
|
||||
const viewReviewId = ref('')
|
||||
|
||||
function handleView(row: StorageReleaseReview) {
|
||||
viewReviewId.value = String(row.id)
|
||||
viewVisible.value = true
|
||||
}
|
||||
|
||||
const reviewDialogVisible = ref(false)
|
||||
const reviewDialogIds = ref<string[]>([])
|
||||
|
||||
function openReviewDialog(row?: StorageReleaseReview) {
|
||||
const rows = row ? [row] : selectedRows.value
|
||||
if (!rows.length) {
|
||||
ElMessage.warning('请先选择要审核的记录')
|
||||
return
|
||||
}
|
||||
reviewDialogIds.value = rows.map((r) => String(r.id))
|
||||
reviewDialogVisible.value = true
|
||||
}
|
||||
|
||||
function onReviewSubmitted() {
|
||||
selectedRows.value = []
|
||||
loadData()
|
||||
}
|
||||
|
||||
async function handleStore(row: StorageReleaseReview) {
|
||||
await ElMessageBox.confirm(`确认将样本「${row.sampleCode || ''}」执行入库操作?`, '入库确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
await storeSample(String(row.id))
|
||||
ElMessage.success('入库成功')
|
||||
loadData()
|
||||
}
|
||||
|
||||
async function handleBatchStore() {
|
||||
if (!selectedRows.value.length) return
|
||||
await ElMessageBox.confirm(
|
||||
`确认将选中的 ${selectedRows.value.length} 条记录执行入库操作?`,
|
||||
'批量入库',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
await withFullscreenLoading(async () => {
|
||||
const ids = selectedRows.value.map((r) => String(r.id))
|
||||
await batchStoreSample({ ids })
|
||||
ElMessage.success(`批量入库成功,共 ${ids.length} 条`)
|
||||
selectedRows.value = []
|
||||
loadData()
|
||||
}, fullscreenLoadingText.save)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
Reference in new issue
Block a user