diff --git a/backend/app.pid b/backend/app.pid index e2bb11dae..b230bd83a 100644 --- a/backend/app.pid +++ b/backend/app.pid @@ -1 +1 @@ -1098 +69671 diff --git a/backend/app/api/outbound_release_review.py b/backend/app/api/outbound_release_review.py index b3a5150a4..dc7413de4 100644 --- a/backend/app/api/outbound_release_review.py +++ b/backend/app/api/outbound_release_review.py @@ -45,6 +45,8 @@ async def list_pilot_release_reviews( scheduleStatus: Optional[str] = None, qcStatus: Optional[str] = None, bizType: Optional[str] = None, + infusionTimeStart: Optional[str] = None, + infusionTimeEnd: Optional[str] = None, db: Session = Depends(get_db), user: dict = Depends(require_menu_code("biz:outboundReleaseReview:list")), ): @@ -58,6 +60,8 @@ async def list_pilot_release_reviews( schedule_status=scheduleStatus, qc_status=qcStatus, biz_type=bizType, + infusion_time_start=infusionTimeStart, + infusion_time_end=infusionTimeEnd, page=page, page_size=pageSize, user_info=user, diff --git a/backend/app/crud/clinical_payment.py b/backend/app/crud/clinical_payment.py index a4f870427..a7feb1151 100644 --- a/backend/app/crud/clinical_payment.py +++ b/backend/app/crud/clinical_payment.py @@ -128,6 +128,28 @@ def map_payments_by_schedule_id( return {row.infusion_schedule_id: _enrich_payment(db, row) for row in rows} +def map_payments_by_schedule_ids( + db: Session, + *, + schedule_ids: List[str], + tenant_no: str, +) -> Dict[str, Dict[str, Any]]: + """按输注计划 ID 批量索引缴费记录(列表页展示用,不做关联 enrich)""" + unique_ids = [sid for sid in {sid for sid in schedule_ids if sid}] + if not unique_ids: + return {} + rows = ( + db.query(BizPilotPayment) + .filter( + BizPilotPayment.infusion_schedule_id.in_(unique_ids), + BizPilotPayment.tenant_no == tenant_no, + BizPilotPayment.del_flag == 0, + ) + .all() + ) + return {row.infusion_schedule_id: _row_to_dict(row) for row in rows} + + # ─── create (auto) ──────────────────────────────────────── diff --git a/backend/app/crud/outbound_release_review.py b/backend/app/crud/outbound_release_review.py index 87fada13a..6ce33abb0 100644 --- a/backend/app/crud/outbound_release_review.py +++ b/backend/app/crud/outbound_release_review.py @@ -5,7 +5,7 @@ import math from datetime import datetime from typing import Any, Dict, List, Optional -from sqlalchemy import func, or_ +from sqlalchemy import and_, func, or_ from sqlalchemy.orm import Session from app.core.utils import generate_uuid @@ -118,6 +118,8 @@ def list_reviews_paginated( schedule_status: Optional[str] = None, qc_status: Optional[str] = None, biz_type: Optional[str] = None, + infusion_time_start: Optional[str] = None, + infusion_time_end: Optional[str] = None, page: int = 1, page_size: int = 10, user_info=None, @@ -212,6 +214,24 @@ def list_reviews_paginated( if qs: q0 = q0.filter(BizPilotInfusionSchedule.qc_status == qs) + # ── 输注时间范围 ── + time_bounds = [] + if (infusion_time_start or "").strip(): + try: + start_dt = datetime.strptime(infusion_time_start.strip()[:10], "%Y-%m-%d") + time_bounds.append(BizPilotInfusionSchedule.infusion_time >= start_dt) + except ValueError: + pass + if (infusion_time_end or "").strip(): + try: + end_dt = datetime.strptime(infusion_time_end.strip()[:10], "%Y-%m-%d") + end_dt = end_dt.replace(hour=23, minute=59, second=59) + time_bounds.append(BizPilotInfusionSchedule.infusion_time <= end_dt) + except ValueError: + pass + if time_bounds: + q0 = q0.filter(or_(and_(*time_bounds), BizPilotInfusionSchedule.infusion_time.is_(None))) + # ── review_conclusion 过滤 ── rc = (review_conclusion or "").strip() if rc == "pending": diff --git a/backend/app/crud/pilot_infusion_schedule_mgmt.py b/backend/app/crud/pilot_infusion_schedule_mgmt.py index 3fd17a5a0..92a014f9b 100644 --- a/backend/app/crud/pilot_infusion_schedule_mgmt.py +++ b/backend/app/crud/pilot_infusion_schedule_mgmt.py @@ -385,6 +385,21 @@ def list_infusion_schedules_mgmt( ) ) + schedule_ids = [item["id"] for item in items if item.get("id")] + if schedule_ids: + from app.crud import clinical_payment + + payment_map = clinical_payment.map_payments_by_schedule_ids( + db, schedule_ids=schedule_ids, tenant_no=tenant_no + ) + for item in items: + payment = payment_map.get(item["id"]) + if not payment: + continue + item["payment"] = payment + item["amount"] = payment.get("amount") + item["payStatus"] = payment.get("payStatus") + return { "list": items, "pagination": {"page": page, "pageSize": page_size, "total": total}, diff --git a/backend/app/schemas/outbound_release_review.py b/backend/app/schemas/outbound_release_review.py index 6b1c7a470..28c0d6863 100644 --- a/backend/app/schemas/outbound_release_review.py +++ b/backend/app/schemas/outbound_release_review.py @@ -12,6 +12,8 @@ class OutboundReviewListParams(BaseModel): reviewConclusion: Optional[str] = None scheduleStatus: Optional[str] = None qcStatus: Optional[str] = None + infusionTimeStart: Optional[str] = None + infusionTimeEnd: Optional[str] = None class OutboundReviewSubmitBody(BaseModel): diff --git a/web-admin/graphify-out/GRAPH_REPORT.md b/web-admin/graphify-out/GRAPH_REPORT.md index 5a31582ac..61319fb18 100644 --- a/web-admin/graphify-out/GRAPH_REPORT.md +++ b/web-admin/graphify-out/GRAPH_REPORT.md @@ -1,7 +1,7 @@ # Graph Report - src (2026-05-28) ## Corpus Check -- Large corpus: 672 files · ~417,392 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only. +- Large corpus: 672 files · ~417,381 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only. ## Summary - 1934 nodes · 2494 edges · 50 communities detected diff --git a/web-admin/graphify-out/manifest.json b/web-admin/graphify-out/manifest.json index b2462f4b9..274115fd2 100644 --- a/web-admin/graphify-out/manifest.json +++ b/web-admin/graphify-out/manifest.json @@ -329,7 +329,7 @@ "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-category/index.vue": 1779864070.4019275, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-category/OutputTypeTab.vue": 1779864078.964701, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/index/index.vue": 1779525781.6949234, - "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1779965464.3573413, + "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1779965568.1579607, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskSubList.vue": 1779948870.746249, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/OutboundQcViewDialog.vue": 1779889633.846836, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception-check-item/index.vue": 1779857055.2939715, @@ -339,7 +339,7 @@ "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/QcMaterialUsageTab.vue": 1779928630.307365, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue": 1779884110.7197206, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue": 1779926469.2154074, - "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue": 1779857044.9472554, + "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue": 1779966532.6566818, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue": 1779701230.688804, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue": 1779790362.6831455, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material-usage/index.vue": 1779864358.234948, diff --git a/web-admin/src/api/outbound-release-review.ts b/web-admin/src/api/outbound-release-review.ts index abf64752f..91d547586 100644 --- a/web-admin/src/api/outbound-release-review.ts +++ b/web-admin/src/api/outbound-release-review.ts @@ -160,6 +160,8 @@ export function fetchOutboundReleaseReviewList(params?: { keyword?: string /** pilot_service | clinical_application */ bizType?: string + infusionTimeStart?: string + infusionTimeEnd?: string }) { return request.get>({ url: '/web/admin/pilot-release-reviews', diff --git a/web-admin/src/components/business/outbound-material-form-sections/index.vue b/web-admin/src/components/business/outbound-material-form-sections/index.vue index 11024581b..91d048105 100644 --- a/web-admin/src/components/business/outbound-material-form-sections/index.vue +++ b/web-admin/src/components/business/outbound-material-form-sections/index.vue @@ -185,31 +185,58 @@ v-for="(att, attIdx) in row.attachments" :key="attIdx" class="paper-att-card" + :class="{ + 'paper-att-card--image': isImageFile(att), + 'paper-att-card--video': isVideoFile(att) + }" + :title="att.fileName" > - - {{ - att.fileName || '附件' - }} +
+ + + +
@@ -357,7 +384,8 @@ Search, Document, Download, - View + View, + VideoPlay } from '@element-plus/icons-vue' import { fetchUploadAttachment } from '@/api/business' import { fetchCellProductList } from '@/api/cell-product' @@ -874,6 +902,11 @@ display: flex; flex-wrap: wrap; gap: 8px; + + :deep(.el-upload) { + display: block; + line-height: normal; + } } .paper-att-card { @@ -884,17 +917,62 @@ background: #f8fafc; display: flex; flex-direction: column; - align-items: center; + align-items: stretch; justify-content: center; - padding: 6px 4px; + padding: 0; position: relative; overflow: hidden; } - .paper-att-card__icon { - color: #0284c7; - font-size: 18px; + .paper-att-card__preview { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 6px 4px; + overflow: hidden; + + &.is-clickable { + cursor: pointer; + } + + img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; + } + } + + .paper-att-card--image, + .paper-att-card--video { + .paper-att-card__preview { + padding: 0; + } + } + + .paper-att-card__type-icon { + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; margin-bottom: 2px; + + &--doc { + color: #0284c7; + } + + &--video { + width: 36px; + height: 36px; + border-radius: 8px; + background: #ede9fe; + color: #7c3aed; + font-size: 20px; + margin-bottom: 4px; + } } .paper-att-card__name { @@ -944,6 +1022,10 @@ } .paper-att-card--trigger { + align-items: center; + justify-content: center; + gap: 4px; + padding: 6px 4px; border: 1px dashed #a7f3d0; background: transparent; cursor: pointer; @@ -961,14 +1043,20 @@ } .paper-att-card__trigger-icon { + display: flex; + align-items: center; + justify-content: center; font-size: 16px; color: #10b981; - margin-bottom: 2px; + margin: 0; + line-height: 1; } .paper-att-card--trigger span { font-size: 11px; color: #64748b; + line-height: 1.2; + text-align: center; } .preview-container { diff --git a/web-admin/src/graphify-out/cache/ast/aaede6cd859915f0bd26e7cbfb62b7f6b2fd4635e8b761d45a5fc1963c3aaa42.json b/web-admin/src/graphify-out/cache/ast/aaede6cd859915f0bd26e7cbfb62b7f6b2fd4635e8b761d45a5fc1963c3aaa42.json new file mode 100644 index 000000000..4c52933ee --- /dev/null +++ b/web-admin/src/graphify-out/cache/ast/aaede6cd859915f0bd26e7cbfb62b7f6b2fd4635e8b761d45a5fc1963c3aaa42.json @@ -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": []} \ No newline at end of file diff --git a/web-admin/src/graphify-out/cache/ast/b29f358a9b8eed3851abe87fef3209b1d0d9a9a0499156988b074bb6386f8a6a.json b/web-admin/src/graphify-out/cache/ast/b29f358a9b8eed3851abe87fef3209b1d0d9a9a0499156988b074bb6386f8a6a.json new file mode 100644 index 000000000..e516998c8 --- /dev/null +++ b/web-admin/src/graphify-out/cache/ast/b29f358a9b8eed3851abe87fef3209b1d0d9a9a0499156988b074bb6386f8a6a.json @@ -0,0 +1 @@ +{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1"}, {"id": "qc_work_order_index_formattempcellproductfield", "label": "formatTempCellProductField()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L840"}, {"id": "qc_work_order_index_handlereceptionattachmentpreview", "label": "handleReceptionAttachmentPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1217"}, {"id": "qc_work_order_index_handlereceptionattachmentdownload", "label": "handleReceptionAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1222"}, {"id": "qc_work_order_index_taskattachmentfullurl", "label": "taskAttachmentFullUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1226"}, {"id": "qc_work_order_index_handletaskattachmentpreview", "label": "handleTaskAttachmentPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1233"}, {"id": "qc_work_order_index_handletaskattachmentdownload", "label": "handleTaskAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1238"}, {"id": "qc_work_order_index_formatconfiguredlabitems", "label": "formatConfiguredLabItems()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1254"}, {"id": "qc_work_order_index_formatconfiguredthirditems", "label": "formatConfiguredThirdItems()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1260"}, {"id": "qc_work_order_index_buildtasktypesections", "label": "buildTaskTypeSections()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1271"}, {"id": "qc_work_order_index_buildconfiguredonlysection", "label": "buildConfiguredOnlySection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1360"}, {"id": "qc_work_order_index_formatdetectmethods", "label": "formatDetectMethods()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1378"}, {"id": "qc_work_order_index_formatmetric", "label": "formatMetric()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1385"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formattempcellproductfield", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L840", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handlereceptionattachmentpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1217", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handlereceptionattachmentdownload", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1222", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_taskattachmentfullurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1226", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handletaskattachmentpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1233", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handletaskattachmentdownload", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1238", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formatconfiguredlabitems", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1254", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formatconfiguredthirditems", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1260", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_buildtasktypesections", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1271", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_buildconfiguredonlysection", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1360", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formatdetectmethods", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1378", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formatmetric", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1385", "weight": 1.0}, {"source": "qc_work_order_index_handletaskattachmentpreview", "target": "qc_work_order_index_taskattachmentfullurl", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1234", "weight": 1.0}], "raw_calls": [{"caller_nid": "qc_work_order_index_formattempcellproductfield", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L842"}, {"caller_nid": "qc_work_order_index_formattempcellproductfield", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L842"}, {"caller_nid": "qc_work_order_index_handlereceptionattachmentpreview", "callee": "receptionAttachmentFullUrl", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1218"}, {"caller_nid": "qc_work_order_index_handlereceptionattachmentpreview", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1218"}, {"caller_nid": "qc_work_order_index_handlereceptionattachmentpreview", "callee": "open", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1219"}, {"caller_nid": "qc_work_order_index_handlereceptionattachmentdownload", "callee": "downloadAttachmentFile", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1223"}, {"caller_nid": "qc_work_order_index_taskattachmentfullurl", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1227"}, {"caller_nid": "qc_work_order_index_taskattachmentfullurl", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1227"}, {"caller_nid": "qc_work_order_index_taskattachmentfullurl", "callee": "test", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1229"}, {"caller_nid": "qc_work_order_index_taskattachmentfullurl", "callee": "replace", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1230"}, {"caller_nid": "qc_work_order_index_handletaskattachmentpreview", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1234"}, {"caller_nid": "qc_work_order_index_handletaskattachmentpreview", "callee": "open", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1235"}, {"caller_nid": "qc_work_order_index_handletaskattachmentdownload", "callee": "downloadAttachmentFile", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1239"}, {"caller_nid": "qc_work_order_index_formatconfiguredlabitems", "callee": "join", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1256"}, {"caller_nid": "qc_work_order_index_formatconfiguredlabitems", "callee": "map", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1256"}, {"caller_nid": "qc_work_order_index_formatconfiguredthirditems", "callee": "join", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1262"}, {"caller_nid": "qc_work_order_index_formatconfiguredthirditems", "callee": "map", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1262"}, {"caller_nid": "qc_work_order_index_buildtasktypesections", "callee": "filter", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1276"}, {"caller_nid": "qc_work_order_index_buildtasktypesections", "callee": "flatMap", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1279"}, {"caller_nid": "qc_work_order_index_formatdetectmethods", "callee": "push", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1380"}, {"caller_nid": "qc_work_order_index_formatdetectmethods", "callee": "push", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1381"}, {"caller_nid": "qc_work_order_index_formatdetectmethods", "callee": "join", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1382"}, {"caller_nid": "qc_work_order_index_formatmetric", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1387"}]} \ No newline at end of file diff --git a/web-admin/src/utils/outbound-qc-pilot-view-sections.ts b/web-admin/src/utils/outbound-qc-pilot-view-sections.ts index 81f22d4ef..de30af0ed 100644 --- a/web-admin/src/utils/outbound-qc-pilot-view-sections.ts +++ b/web-admin/src/utils/outbound-qc-pilot-view-sections.ts @@ -10,21 +10,42 @@ import { qcStatusLabel, qcStatusTagType, scheduleStatusLabel, - scheduleStatusTagType + scheduleStatusTagType, + sexLabel } from '@/utils/status-tag-registry' -/** 出库质检 + 中试服务:申请单 / 患者 / 输注方案 / 输注计划 / 产品明细 */ -export function buildOutboundPilotContextSections(detail: OutboundQcDetail): ViewSection[] { +export type OutboundViewContextVariant = 'pilot_service' | 'clinical_application' + +const SECTION_TITLES: Record< + OutboundViewContextVariant, + { application: string; schedule: string } +> = { + pilot_service: { + application: '中试服务申请单信息', + schedule: '本次输注计划信息' + }, + clinical_application: { + application: '临床应用申请信息', + schedule: '临床应用输注计划信息' + } +} + +/** 出库工单/质检:申请单 / 患者 / 输注方案 / 输注计划 / 产品明细(码值转中文标签) */ +export function buildOutboundPilotContextSections( + detail: OutboundQcDetail, + variant: OutboundViewContextVariant = 'pilot_service' +): ViewSection[] { const app = detail.application const patient = detail.patient const plan = detail.infusionPlan const schedule = detail.infusionSchedule const products = detail.products || [] + const titles = SECTION_TITLES[variant] const result: ViewSection[] = [] if (app) { result.push({ - title: '中试服务申请单信息', + title: titles.application, fields: [ { label: '申请单号', value: app.applicationNo || '—' }, { label: '申请单名称', value: app.applicationName || '—' }, @@ -43,7 +64,7 @@ export function buildOutboundPilotContextSections(detail: OutboundQcDetail): Vie title: '患者信息', fields: [ { label: '患者姓名', value: patient.patientName || '—' }, - { label: '性别', value: patient.sex || '—' }, + { label: '性别', value: sexLabel(String(patient.sex ?? '')) || '—' }, { label: '年龄', value: patient.age || '—' }, { label: '联系电话', value: patient.phoneNo || '—' }, { @@ -98,7 +119,7 @@ export function buildOutboundPilotContextSections(detail: OutboundQcDetail): Vie if (schedule.cancelReason) { scheduleFields.push({ label: '取消原因', value: schedule.cancelReason, span: true }) } - result.push({ title: '本次输注计划信息', fields: scheduleFields }) + result.push({ title: titles.schedule, fields: scheduleFields }) } if (products.length) { diff --git a/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseMaterialDialog.vue b/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseMaterialDialog.vue index 331e68c59..e19b300f2 100644 --- a/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseMaterialDialog.vue +++ b/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseMaterialDialog.vue @@ -212,31 +212,58 @@ v-for="(att, attIdx) in row.attachments" :key="attIdx" class="paper-att-card" + :class="{ + 'paper-att-card--image': isImageFile(att), + 'paper-att-card--video': isVideoFile(att) + }" + :title="att.fileName" > - - {{ - att.fileName || '附件' - }} +
+ + + +
@@ -398,7 +425,8 @@ Search, Document, Download, - View + View, + VideoPlay } from '@element-plus/icons-vue' import { fetchUploadAttachment } from '@/api/business' import { fetchCellProductList } from '@/api/cell-product' @@ -908,6 +936,11 @@ display: flex; flex-wrap: wrap; gap: 8px; + + :deep(.el-upload) { + display: block; + line-height: normal; + } } .paper-att-card { @@ -918,17 +951,62 @@ background: #f8fafc; display: flex; flex-direction: column; - align-items: center; + align-items: stretch; justify-content: center; - padding: 6px 4px; + padding: 0; position: relative; overflow: hidden; } - .paper-att-card__icon { - color: #0284c7; - font-size: 18px; + .paper-att-card__preview { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 6px 4px; + overflow: hidden; + + &.is-clickable { + cursor: pointer; + } + + img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; + } + } + + .paper-att-card--image, + .paper-att-card--video { + .paper-att-card__preview { + padding: 0; + } + } + + .paper-att-card__type-icon { + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; margin-bottom: 2px; + + &--doc { + color: #0284c7; + } + + &--video { + width: 36px; + height: 36px; + border-radius: 8px; + background: #ede9fe; + color: #7c3aed; + font-size: 20px; + margin-bottom: 4px; + } } .paper-att-card__name { @@ -978,6 +1056,10 @@ } .paper-att-card--trigger { + align-items: center; + justify-content: center; + gap: 4px; + padding: 6px 4px; border: 1px dashed #a7f3d0; background: transparent; cursor: pointer; @@ -995,14 +1077,20 @@ } .paper-att-card__trigger-icon { + display: flex; + align-items: center; + justify-content: center; font-size: 16px; color: #10b981; - margin-bottom: 2px; + margin: 0; + line-height: 1; } .paper-att-card--trigger span { font-size: 11px; color: #64748b; + line-height: 1.2; + text-align: center; } .preview-container { diff --git a/web-admin/src/views/pilot-service/outbound-release-review/index.vue b/web-admin/src/views/pilot-service/outbound-release-review/index.vue index c8bfd5198..bb46a6a19 100644 --- a/web-admin/src/views/pilot-service/outbound-release-review/index.vue +++ b/web-admin/src/views/pilot-service/outbound-release-review/index.vue @@ -33,9 +33,22 @@ /> + + +
- 支持按申请单号、患者姓名搜索。 + 支持按申请单号、患者姓名搜索;输注时间默认今天前 15 天至后 15 天。
重置 @@ -267,9 +280,27 @@ { value: 'btg', label: '不通过' } ] + function formatLocalDate(d: Date) { + const y = d.getFullYear() + const m = String(d.getMonth() + 1).padStart(2, '0') + const day = String(d.getDate()).padStart(2, '0') + return `${y}-${m}-${day}` + } + + /** 输注时间默认:今天前 15 天 ~ 今天后 15 天 */ + function getDefaultInfusionTimeRange(): [string, string] { + const today = new Date() + const start = new Date(today) + start.setDate(start.getDate() - 15) + const end = new Date(today) + end.setDate(end.getDate() + 15) + return [formatLocalDate(start), formatLocalDate(end)] + } + const searchForm = reactive({ reviewConclusion: '', - keyword: '' + keyword: '', + infusionTimeRange: getDefaultInfusionTimeRange() as [string, string] }) const tableData = ref([]) const selectedRows = ref([]) @@ -290,7 +321,9 @@ pageSize: pagination.pageSize, reviewConclusion: searchForm.reviewConclusion || undefined, keyword: searchForm.keyword || undefined, - bizType: effectiveBizType.value + bizType: effectiveBizType.value, + infusionTimeStart: searchForm.infusionTimeRange?.[0] || undefined, + infusionTimeEnd: searchForm.infusionTimeRange?.[1] || undefined }) tableData.value = res.list || [] pagination.total = res.total ?? 0 @@ -307,6 +340,7 @@ function handleReset() { searchForm.reviewConclusion = '' searchForm.keyword = '' + searchForm.infusionTimeRange = getDefaultInfusionTimeRange() pagination.page = 1 loadData() } diff --git a/web-admin/src/views/pilot-service/pilot-application/InfusionScheduleTab.vue b/web-admin/src/views/pilot-service/pilot-application/InfusionScheduleTab.vue index 4b5a393f6..bfc55e2b9 100644 --- a/web-admin/src/views/pilot-service/pilot-application/InfusionScheduleTab.vue +++ b/web-admin/src/views/pilot-service/pilot-application/InfusionScheduleTab.vue @@ -199,10 +199,10 @@ > @@ -215,13 +215,13 @@ > { - const orderTypeLabel = channelServiceOrderTypeLabel(row.orderType) const patient = row.patientName ? `患者「${row.patientName}」` : '该患者' + const pendingConfirmHint = + getInfusionStatus(row) === 'dtz' ? '当前为待输注,系统将先记录用户确认再标记完成。' : '' + + if (isClinical.value) { + const paymentHint = + '临床应用不在此扣减渠道服务订单费用,缴费请在「缴费信息」中单独处理。' + return `${patient}的输注计划将标记为已完成。${pendingConfirmHint}${paymentHint}是否继续?` + } + + const orderTypeLabel = channelServiceOrderTypeLabel(row.orderType) const statusHint = - getInfusionStatus(row) === 'dtz' ? '当前为待输注,系统将先记录用户确认再完成结算。' : '' + getInfusionStatus(row) === 'dtz' + ? '当前为待输注,系统将先记录用户确认再完成结算。' + : '' let settlementHint = '并触发关联服务订单的结算扣减。' if (row.orderType === 'yfk') { settlementHint = `关联服务订单类型为「${orderTypeLabel}」,系统将按渠道协议单价扣减服务订单账户余额。` @@ -603,7 +614,9 @@ scheduleId, infusionStatus: 'ywc' }) - ElMessage.success('输注计划已完成,结算扣减已处理') + ElMessage.success( + isClinical.value ? '输注计划已完成' : '输注计划已完成,结算扣减已处理' + ) loadData() } catch (e: any) { if (e !== 'cancel') { diff --git a/web-admin/src/views/production/production-work-order/StepDetailPanel.vue b/web-admin/src/views/production/production-work-order/StepDetailPanel.vue index 81cc3bb99..463d4a14b 100644 --- a/web-admin/src/views/production/production-work-order/StepDetailPanel.vue +++ b/web-admin/src/views/production/production-work-order/StepDetailPanel.vue @@ -14,8 +14,8 @@ @@ -53,9 +52,8 @@ editor.taskDetail.value.taskStatus === 'jxz' || editor.taskDetail.value.taskStatus === 'ywc' " - class="btn-main" + class="btn-main task-exec-toolbar-btn" type="success" - size="small" :loading="editor.taskActionLoading.value" @click="editor.handleCompleteTask" > @@ -263,13 +261,13 @@

📥 原材料(样本/细胞产品)必填

保存 + 保存 +
@@ -293,13 +291,13 @@

📤 产出(细胞产品)

保存 + 保存 +
@@ -712,3 +710,26 @@ margin-right: 8px; } + + + diff --git a/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue b/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue index da1863a57..7b4c2457d 100644 --- a/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue +++ b/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue @@ -4,11 +4,11 @@

原材料(样本/细胞产品)

- + 添加样本 - + 添加细胞产品 diff --git a/web-admin/src/views/production/production-work-order/TaskOutputList.vue b/web-admin/src/views/production/production-work-order/TaskOutputList.vue index dc27d0619..a5d2841fd 100644 --- a/web-admin/src/views/production/production-work-order/TaskOutputList.vue +++ b/web-admin/src/views/production/production-work-order/TaskOutputList.vue @@ -3,7 +3,12 @@

产出(细胞产品)

- + 新增产出 diff --git a/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue b/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue index d412b8b5a..472fcc491 100644 --- a/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue +++ b/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue @@ -1,72 +1,46 @@ +
+

暂无产品明细