zlwu 4 месяцев назад
Родитель
Сommit
58a47b96a5
38 измененных файлов с 1864 добавлено и 1946 удалено
  1. BIN
      backend.zip
  2. 1 1
      backend/app.pid
  3. 37 0
      backend/app/crud/production_work_order.py
  4. 29 0
      backend/app/crud/qc_work_order.py
  5. 0 0
      backend/app/graphify-out/cache/ast/aa792b69770f91c72fde0f6c156a8ef36d479c214e6aa59b2464a01ff71158ce.json
  6. 0 0
      backend/app/graphify-out/cache/ast/fa490691861edaf4e9d7d91dedc9ee7ed9d3058c6acc5b86d2cde4aaaa970df3.json
  7. 196 420
      backend/graphify-out/.graphify_analysis.json
  8. 171 164
      backend/graphify-out/.graphify_extract.json
  9. 81 76
      backend/graphify-out/GRAPH_REPORT.md
  10. 1 1
      backend/graphify-out/graph.html
  11. 112 112
      backend/graphify-out/graph.json
  12. 2 2
      backend/graphify-out/manifest.json
  13. 454 540
      web-admin/graphify-out/.graphify_analysis.json
  14. 90 397
      web-admin/graphify-out/.graphify_extract.json
  15. 106 101
      web-admin/graphify-out/GRAPH_REPORT.md
  16. 1 1
      web-admin/graphify-out/graph.html
  17. 2 2
      web-admin/graphify-out/graph.json
  18. 10 10
      web-admin/graphify-out/manifest.json
  19. 6 0
      web-admin/src/api/production-work-order.ts
  20. 6 0
      web-admin/src/api/qc-work-order.ts
  21. 1 0
      web-admin/src/graphify-out/cache/ast/07468df71a20f2c75ba9ecba1b5bca2a83bf63b875c6e68b99471d2613b64293.json
  22. 1 0
      web-admin/src/graphify-out/cache/ast/4506079098039c10a6e677f125472c7d3a685a0e496e98cd3c05dfa5bb835571.json
  23. 1 0
      web-admin/src/graphify-out/cache/ast/5fb0f30d87bbb940863761ce01607887a6afe05edbf636d2737a59eb342f0e0f.json
  24. 1 0
      web-admin/src/graphify-out/cache/ast/686495c44689c783c66328617a5cb322c6aa79a8e840ca290342c5f1bf85359d.json
  25. 0 0
      web-admin/src/graphify-out/cache/ast/70726652cbfe3dbe97bded90eebec9252542e32db5aa36528c947c84c4130e80.json
  26. 0 0
      web-admin/src/graphify-out/cache/ast/85094e0d7ef8fc73ef477c4d44b0a8e52d14f8ac244d19055575a0806416f936.json
  27. 1 0
      web-admin/src/graphify-out/cache/ast/8569ccf4780741f68bc4f973b51e8228576837ee8c990396b4c28749c2a1049e.json
  28. 0 0
      web-admin/src/graphify-out/cache/ast/8c12c9f17b5c67ae2fca703244f83a62ee956584c022393c68c2ac94170bb69c.json
  29. 1 0
      web-admin/src/graphify-out/cache/ast/a9bcfc991213e67bd27ede64d5fb16d000c64125160d3bd28fee64ab987a9853.json
  30. 0 0
      web-admin/src/graphify-out/cache/ast/b5a7ddf14e286e97ad6202f461aabb2c4eda32e426efdcee6e2be243505431e4.json
  31. 0 0
      web-admin/src/graphify-out/cache/ast/df70c7fe1152f96f10eccfa304bbd477f0dabdb25d41a377f622eddb772aadb2.json
  32. 1 0
      web-admin/src/graphify-out/cache/ast/e37e8fbc54360e71a9e407b4886f3b80267bbcd78929c4f949d7b4962af8acc4.json
  33. 0 0
      web-admin/src/graphify-out/cache/ast/f7f50431271417e71aa52d0168f65dfc871f5100323ae337a66ab13435c3867b.json
  34. 8 0
      web-admin/src/utils/production-work-order-card-meta.ts
  35. 8 0
      web-admin/src/utils/qc-work-order-card-meta.ts
  36. 129 0
      web-admin/src/views/pilot-service/pilot-application/ApplicationFormDialog.vue
  37. 243 118
      web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue
  38. 164 1
      web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue

+ 1 - 1
backend/app.pid

@@ -1 +1 @@
-66684
+57731

+ 37 - 0
backend/app/crud/production_work_order.py

@@ -1,6 +1,7 @@
 """入库生产工单 CRUD(含临时生产工单)"""
 """入库生产工单 CRUD(含临时生产工单)"""
 from __future__ import annotations
 from __future__ import annotations
 
 
+from collections import defaultdict
 from datetime import datetime
 from datetime import datetime
 from typing import Any, Dict, List, Optional, Tuple
 from typing import Any, Dict, List, Optional, Tuple
 
 
@@ -24,6 +25,8 @@ from app.models.models import (
     BizProductionTaskStepOutput,
     BizProductionTaskStepOutput,
     BizProductionTaskStepReagent,
     BizProductionTaskStepReagent,
     BizProductionWorkOrder,
     BizProductionWorkOrder,
+    BizPilotInfusionSchedule,
+    BizPilotInfusionScheduleProduct,
     BizProtocolSample,
     BizProtocolSample,
     BizSampleReception,
     BizSampleReception,
 )
 )
@@ -133,6 +136,15 @@ _BIZ_TYPE_LABELS = {
 }
 }
 
 
 
 
+def _dec_price(v: Any) -> Optional[float]:
+    if v is None:
+        return None
+    try:
+        return float(v)
+    except (TypeError, ValueError):
+        return None
+
+
 def _enrich_outbound_items(
 def _enrich_outbound_items(
     db: Session, tenant_no: str, items: List[Dict[str, Any]]
     db: Session, tenant_no: str, items: List[Dict[str, Any]]
 ) -> List[Dict[str, Any]]:
 ) -> List[Dict[str, Any]]:
@@ -156,6 +168,30 @@ def _enrich_outbound_items(
     if not outbound_ids:
     if not outbound_ids:
         return items
         return items
 
 
+    products_map: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
+    for product in (
+        db.query(BizPilotInfusionScheduleProduct)
+        .filter(
+            BizPilotInfusionScheduleProduct.infusion_schedule_id.in_(outbound_ids),
+            BizPilotInfusionScheduleProduct.tenant_no == tenant_no,
+            BizPilotInfusionScheduleProduct.del_flag == 0,
+        )
+        .order_by(BizPilotInfusionScheduleProduct.order_num.asc())
+        .all()
+    ):
+        products_map[product.infusion_schedule_id].append(
+            {
+                "projectCode": product.project_code or "",
+                "projectName": product.project_name or "",
+                "cellTypeName": product.cell_type_name or "",
+                "specText": product.spec_text or "",
+                "agreementPrice": _dec_price(product.agreement_price),
+                "actualPrice": _dec_price(product.actual_price),
+                "quantity": product.quantity,
+                "productStatus": product.product_status or "",
+            }
+        )
+
     schedules = (
     schedules = (
         db.query(BizPilotInfusionSchedule)
         db.query(BizPilotInfusionSchedule)
         .filter(
         .filter(
@@ -239,6 +275,7 @@ def _enrich_outbound_items(
         it["specText"] = sched.spec_text or ""
         it["specText"] = sched.spec_text or ""
         it["infusionTime"] = _fmt_dt(sched.infusion_time) if sched.infusion_time else ""
         it["infusionTime"] = _fmt_dt(sched.infusion_time) if sched.infusion_time else ""
         it["infusionLocation"] = sched.infusion_location or ""
         it["infusionLocation"] = sched.infusion_location or ""
+        it["products"] = products_map.get(it["infusionScheduleId"], [])
 
 
     return items
     return items
 
 

+ 29 - 0
backend/app/crud/qc_work_order.py

@@ -1,6 +1,7 @@
 """质检工单 CRUD"""
 """质检工单 CRUD"""
 from __future__ import annotations
 from __future__ import annotations
 
 
+from collections import defaultdict
 from datetime import datetime
 from datetime import datetime
 from decimal import Decimal
 from decimal import Decimal
 from typing import Any, Dict, List, Optional, Tuple
 from typing import Any, Dict, List, Optional, Tuple
@@ -213,6 +214,31 @@ def _enrich_outbound_work_orders(
     schedule_ids = list({x.get("infusionScheduleId") for x in outbound if x.get("infusionScheduleId")})
     schedule_ids = list({x.get("infusionScheduleId") for x in outbound if x.get("infusionScheduleId")})
     if not schedule_ids:
     if not schedule_ids:
         return items
         return items
+
+    products_map: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
+    for product in (
+        db.query(BizPilotInfusionScheduleProduct)
+        .filter(
+            BizPilotInfusionScheduleProduct.infusion_schedule_id.in_(schedule_ids),
+            BizPilotInfusionScheduleProduct.tenant_no == tenant_no,
+            BizPilotInfusionScheduleProduct.del_flag == 0,
+        )
+        .order_by(BizPilotInfusionScheduleProduct.order_num.asc())
+        .all()
+    ):
+        products_map[product.infusion_schedule_id].append(
+            {
+                "projectCode": product.project_code or "",
+                "projectName": product.project_name or "",
+                "cellTypeName": product.cell_type_name or "",
+                "specText": product.spec_text or "",
+                "agreementPrice": _dec(product.agreement_price),
+                "actualPrice": _dec(product.actual_price),
+                "quantity": product.quantity,
+                "productStatus": product.product_status or "",
+            }
+        )
+
     # 查输注计划 → 输注方案 → 申请单 → 患者
     # 查输注计划 → 输注方案 → 申请单 → 患者
     schedules = {
     schedules = {
         s.id: s
         s.id: s
@@ -287,7 +313,10 @@ def _enrich_outbound_work_orders(
         item["patientName"] = patient.patient_name if patient else ""
         item["patientName"] = patient.patient_name if patient else ""
         item["sortNo"] = course.get("sortNo")
         item["sortNo"] = course.get("sortNo")
         item["totalCourseCount"] = course.get("totalCourseCount")
         item["totalCourseCount"] = course.get("totalCourseCount")
+        item["cellType"] = sched.cell_type or ""
+        item["specText"] = sched.spec_text or ""
         item["infusionTime"] = _fmt_dt(sched.infusion_time)
         item["infusionTime"] = _fmt_dt(sched.infusion_time)
+        item["products"] = products_map.get(sid, [])
     return items
     return items
 
 
 
 

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
backend/app/graphify-out/cache/ast/aa792b69770f91c72fde0f6c156a8ef36d479c214e6aa59b2464a01ff71158ce.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
backend/app/graphify-out/cache/ast/fa490691861edaf4e9d7d91dedc9ee7ed9d3058c6acc5b86d2cde4aaaa970df3.json


Разница между файлами не показана из-за своего большого размера
+ 196 - 420
backend/graphify-out/.graphify_analysis.json


Разница между файлами не показана из-за своего большого размера
+ 171 - 164
backend/graphify-out/.graphify_extract.json


+ 81 - 76
backend/graphify-out/GRAPH_REPORT.md

@@ -1,11 +1,11 @@
 # Graph Report - app  (2026-06-17)
 # Graph Report - app  (2026-06-17)
 
 
 ## Corpus Check
 ## Corpus Check
-- Large corpus: 208 files · ~244,444 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
+- Large corpus: 208 files · ~244,597 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
 
 
 ## Summary
 ## Summary
-- 4189 nodes · 22047 edges · 52 communities detected
-- Extraction: 33% EXTRACTED · 67% INFERRED · 0% AMBIGUOUS · INFERRED: 14869 edges (avg confidence: 0.59)
+- 4190 nodes · 22061 edges · 53 communities detected
+- Extraction: 33% EXTRACTED · 67% INFERRED · 0% AMBIGUOUS · INFERRED: 14880 edges (avg confidence: 0.59)
 - Token cost: 0 input · 0 output
 - Token cost: 0 input · 0 output
 
 
 ## Community Hubs (Navigation)
 ## Community Hubs (Navigation)
@@ -55,12 +55,13 @@
 - [[_COMMUNITY_Community 43|Community 43]]
 - [[_COMMUNITY_Community 43|Community 43]]
 - [[_COMMUNITY_Community 44|Community 44]]
 - [[_COMMUNITY_Community 44|Community 44]]
 - [[_COMMUNITY_Community 45|Community 45]]
 - [[_COMMUNITY_Community 45|Community 45]]
-- [[_COMMUNITY_Community 48|Community 48]]
+- [[_COMMUNITY_Community 46|Community 46]]
 - [[_COMMUNITY_Community 49|Community 49]]
 - [[_COMMUNITY_Community 49|Community 49]]
 - [[_COMMUNITY_Community 50|Community 50]]
 - [[_COMMUNITY_Community 50|Community 50]]
 - [[_COMMUNITY_Community 51|Community 51]]
 - [[_COMMUNITY_Community 51|Community 51]]
 - [[_COMMUNITY_Community 52|Community 52]]
 - [[_COMMUNITY_Community 52|Community 52]]
 - [[_COMMUNITY_Community 53|Community 53]]
 - [[_COMMUNITY_Community 53|Community 53]]
+- [[_COMMUNITY_Community 54|Community 54]]
 
 
 ## God Nodes (most connected - your core abstractions)
 ## God Nodes (most connected - your core abstractions)
 1. `success_response()` - 643 edges
 1. `success_response()` - 643 edges
@@ -79,103 +80,103 @@
   core/api_request_log_middleware.py → models/models.py
   core/api_request_log_middleware.py → models/models.py
 - `get_menu_list()` --calls--> `success_response()`  [INFERRED]
 - `get_menu_list()` --calls--> `success_response()`  [INFERRED]
   api/menu.py → core/response.py
   api/menu.py → core/response.py
+- `mark_requisition_fully_used()` --calls--> `apply_data_scope()`  [INFERRED]
+  crud/production_material_usage.py → core/data_scope.py
 - `保留匹配节点及其全部上级,供关键词检索后仍能正确组树。` --uses--> `SysDept`  [INFERRED]
 - `保留匹配节点及其全部上级,供关键词检索后仍能正确组树。` --uses--> `SysDept`  [INFERRED]
   crud/dept.py → models/models.py
   crud/dept.py → models/models.py
 - `物品单位 CRUD(biz_item_unit)` --uses--> `BizItemUnit`  [INFERRED]
 - `物品单位 CRUD(biz_item_unit)` --uses--> `BizItemUnit`  [INFERRED]
   crud/item_unit.py → models/models.py
   crud/item_unit.py → models/models.py
-- `快递公司 CRUD(biz_express_company)` --uses--> `BizExpressCompany`  [INFERRED]
-  crud/express_company.py → models/models.py
 
 
 ## Communities
 ## Communities
 
 
 ### Community 0 - "Community 0"
 ### Community 0 - "Community 0"
 Cohesion: 0.01
 Cohesion: 0.01
-Nodes (807): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+799 more)
+Nodes (800): attachment_download_url(), create_dept(), create_dict_data(), create_dict_type(), create_express_company(), create_item_unit(), create_menu(), create_post() (+792 more)
 
 
 ### Community 1 - "Community 1"
 ### Community 1 - "Community 1"
 Cohesion: 0.01
 Cohesion: 0.01
-Nodes (587): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+579 more)
+Nodes (582): AttachmentDownloadUrlBody, DeptCreateBody, DeptUpdateBody, DictDataCreateBody, DictDataUpdateBody, DictTypeCreateBody, DictTypeUpdateBody, ExpressCompanyCreateBody (+574 more)
 
 
 ### Community 2 - "Community 2"
 ### Community 2 - "Community 2"
-Cohesion: 0.07
-Nodes (479): BatchRefundFinanceSubmitBody, 细胞存储 — 采集协议单 / 审核 / 财务确认 / 分期预警 API, If mother info exists but no customerId, create a customer record and link it., RefundFinanceSubmitBody, Base, 细胞产品 CRUD(biz_cell_product), 解析任务样本投入物,返回祖先链单段(样本终止节点)。, 管理端手动改状态:允许在合法枚举间任意变更,并同步库位占用。 (+471 more)
+Cohesion: 0.1
+Nodes (315): 环境检测 — 检测项维护 + 登记管理 API, 保险公司及险种价格管理 API(保险公司 + 开票信息 + 险种价格), 审批意见:前端传 commentText,兼容 comment。, TaskActionBody, Base, 设备 CRUD(biz_equipment), 将 BizMaterialUsage 行转换为字典(用于我的领用记录)。, 当前登录人的手动出库领用记录:领用人字段存昵称或用户 ID(与生产/质检一致)。 (+307 more)
 
 
 ### Community 3 - "Community 3"
 ### Community 3 - "Community 3"
 Cohesion: 0.01
 Cohesion: 0.01
-Nodes (278): _ensure_customer_for_mother(), _persist_protocol_sub_resources(), list_ethnics_for_select(), apply_data_scope(), _get_child_dept_ids(), get_user_dept_id(), _resolve_user_role_scopes(), format_date() (+270 more)
+Nodes (258): _ensure_customer_for_mother(), _persist_protocol_sub_resources(), list_ethnics_for_select(), apply_data_scope(), _get_child_dept_ids(), get_user_dept_id(), _resolve_user_role_scopes(), format_date() (+250 more)
 
 
 ### Community 4 - "Community 4"
 ### Community 4 - "Community 4"
 Cohesion: 0.01
 Cohesion: 0.01
-Nodes (283): generate_uuid(), batch_stock_in(), batch_stock_out(), consumable_code_exists(), create_consumable(), _dec(), get_consumable(), _is_enabled_status() (+275 more)
+Nodes (262): set_current_process_route(), generate_uuid(), batch_create_sterilize_logs(), batch_create_wash_logs(), batch_discard_garments(), batch_soft_delete_garments(), _count_map(), create_garment() (+254 more)
 
 
 ### Community 5 - "Community 5"
 ### Community 5 - "Community 5"
 Cohesion: 0.02
 Cohesion: 0.02
-Nodes (235): eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci(), _format_attachment_file_size(), urls_to_attachment_list() (+227 more)
+Nodes (225): eq_unicode(), like_unicode(), MySQL 字符串比较/模糊匹配时统一排序规则,避免 Illegal mix of collations。, 跨表字符串等值比较(JOIN / WHERE)时统一为 utf8mb4_unicode_ci。, 将列显式设为 utf8mb4_unicode_ci。, unicode_ci(), _format_attachment_file_size(), urls_to_attachment_list() (+217 more)
 
 
 ### Community 6 - "Community 6"
 ### Community 6 - "Community 6"
-Cohesion: 0.03
-Nodes (161): batch_discard(), batch_options(), _batch_row_to_dict(), batch_stock_in(), batch_stock_out(), _create_batch_and_usage(), delete_batch(), _discard_batch() (+153 more)
+Cohesion: 0.08
+Nodes (227): AuditCreateBody, BatchDetailIdsBody, BatchRefundFinanceSubmitBody, FinanceConfirmBody, FullmoonTodoInsuranceBody, InvoiceRequestCompleteBody, InvoiceRequestCreateBody, ProtocolCreateBody (+219 more)
 
 
 ### Community 7 - "Community 7"
 ### Community 7 - "Community 7"
 Cohesion: 0.02
 Cohesion: 0.02
-Nodes (103): get_designer_data(), _error_json(), 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin, sqlalchemy_exception_handler(), unhandled_exception_handler(), BaseHTTPMiddleware, ApiCryptoError (+95 more)
+Nodes (180): _avatar_url_field(), _build_current_user_payload(), _build_user_info_payload(), get_current_user_profile(), _jwt_user_payload(), logout(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线), Web 端登录用 RSA 公钥(PKCS#1 v1.5,与 JSEncrypt 一致) (+172 more)
 
 
 ### Community 8 - "Community 8"
 ### Community 8 - "Community 8"
 Cohesion: 0.02
 Cohesion: 0.02
-Nodes (151): api_save_application(), get_protocol_nos_used_in_protocols(), create_dict_type(), dict_type_code_exists(), get_dict_type(), list_dict_types(), 字典类型 CRUD(sys_dict_type), _row_to_dict() (+143 more)
+Nodes (196): batch_discard(), batch_options(), _batch_row_to_dict(), batch_stock_in(), batch_stock_out(), _create_batch_and_usage(), delete_batch(), _discard_batch() (+188 more)
 
 
 ### Community 9 - "Community 9"
 ### Community 9 - "Community 9"
-Cohesion: 0.03
-Nodes (112): _avatar_url_field(), _build_current_user_payload(), _build_user_info_payload(), get_current_user_profile(), get_web_auth_menu_codes(), _jwt_user_payload(), logout(), 公共接口(Web 登录、刷新 Token、登出;用户模型对齐 docs/sql/zkxw.sql 基线) (+104 more)
+Cohesion: 0.02
+Nodes (104): get_designer_data(), _error_json(), 数据库异常:返回 JSON 并保留 CORS 头,避免浏览器只报跨域错误, 未捕获异常:统一 JSON 500,确保跨域场景下仍有 Access-Control-Allow-Origin, sqlalchemy_exception_handler(), unhandled_exception_handler(), BaseHTTPMiddleware, ApiCryptoError (+96 more)
 
 
 ### Community 10 - "Community 10"
 ### Community 10 - "Community 10"
-Cohesion: 0.05
-Nodes (93): AccessLogCreateBody, AccessLogUpdateBody, CleanRoomCreateBody, CleanRoomUpdateBody, ExhibitionAppointmentCreateBody, ExhibitionAppointmentUpdateBody, OccupancyCreateBody, 运营支撑 API(洁净室、进出记录、仓储设施、储位、展厅预约) (+85 more)
+Cohesion: 0.03
+Nodes (116): get_next_cell_product_code(), _ancestor_link_from_cell_product_input(), _ancestor_sample_chain_link(), _apply_cell_product_status_change(), _build_ancestor_chain(), _build_descendant_chain(), _build_facility_path_label(), build_split_product_codes() (+108 more)
 
 
 ### Community 11 - "Community 11"
 ### Community 11 - "Community 11"
-Cohesion: 0.36
-Nodes (67): BatchStockInBody, BatchStockInItem, BatchStockOutBody, BatchStockOutItem, ConsumableCreateBody, ConsumablePackCreateBody, ConsumablePackUpdateBody, ConsumableUpdateBody (+59 more)
+Cohesion: 0.05
+Nodes (117): AccessLogCreateBody, AccessLogUpdateBody, batch_create_storage_slots(), CleanRoomCreateBody, CleanRoomUpdateBody, create_access_log(), create_storage_slot(), create_uv_disinfect_log() (+109 more)
 
 
 ### Community 12 - "Community 12"
 ### Community 12 - "Community 12"
-Cohesion: 0.07
-Nodes (66): 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() (+58 more)
+Cohesion: 0.36
+Nodes (67): BatchStockInBody, BatchStockInItem, BatchStockOutBody, BatchStockOutItem, ConsumableCreateBody, ConsumablePackCreateBody, ConsumablePackUpdateBody, ConsumableUpdateBody (+59 more)
 
 
 ### Community 13 - "Community 13"
 ### Community 13 - "Community 13"
-Cohesion: 0.06
-Nodes (67): get_next_cell_product_code(), _ancestor_link_from_cell_product_input(), _ancestor_sample_chain_link(), _apply_cell_product_status_change(), _build_ancestor_chain(), _build_descendant_chain(), _build_facility_path_label(), build_split_product_codes() (+59 more)
+Cohesion: 0.08
+Nodes (57): api_save_application(), auto_generate_schedules(), complete_application(), create_application(), create_basic_experiment_sale_schedules(), create_infusion_plan(), create_schedule(), _create_schedule_products() (+49 more)
 
 
 ### Community 14 - "Community 14"
 ### Community 14 - "Community 14"
-Cohesion: 0.11
-Nodes (39): _amount_for_row(), _batch_map(), deduct_for_task(), _deduct_task_usage_legacy_batch(), _display_remark(), _fmt_date_val(), _linked_qc_task_no_map(), list_linked_task_usages() (+31 more)
+Cohesion: 0.08
+Nodes (55): 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() (+47 more)
 
 
 ### Community 15 - "Community 15"
 ### Community 15 - "Community 15"
-Cohesion: 0.11
-Nodes (36): batch_create_sterilize_logs(), batch_create_wash_logs(), batch_discard_garments(), batch_soft_delete_garments(), _count_map(), create_garment(), create_sterilize_log(), create_wash_log() (+28 more)
-
-### Community 16 - "Community 16"
 Cohesion: 0.12
 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)
 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 16 - "Community 16"
+Cohesion: 0.2
+Nodes (19): _code_exists(), create_material(), _dec(), _fmt_dt(), get_material(), get_material_entity(), get_my_usages(), _is_enabled_status() (+11 more)
+
 ### Community 17 - "Community 17"
 ### Community 17 - "Community 17"
-Cohesion: 0.16
-Nodes (22): _code_exists(), create_material(), _dec(), _fmt_dt(), get_material(), get_material_entity(), get_my_usages(), _is_enabled_status() (+14 more)
+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 18 - "Community 18"
 ### Community 18 - "Community 18"
-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)
+Cohesion: 0.31
+Nodes (11): create_equipment(), _dec(), equipment_code_exists(), equipment_is_referenced(), get_equipment(), list_equipments(), _norm_status(), _parse_date() (+3 more)
 
 
 ### Community 19 - "Community 19"
 ### Community 19 - "Community 19"
-Cohesion: 0.21
-Nodes (13): create_collection_region(), create_hospital(), get_collection_region(), get_hospital(), _hospital_names_by_region_ids(), _hospital_to_dict(), list_collection_regions(), list_hospitals() (+5 more)
+Cohesion: 0.27
+Nodes (11): create_consumable_pack(), get_consumable_pack(), get_pack_detail(), _item_row_to_dict(), list_all_packs(), list_consumable_packs(), pack_code_exists(), 耗材包 CRUD(biz_consumable_pack + biz_consumable_pack_item) (+3 more)
 
 
 ### Community 20 - "Community 20"
 ### Community 20 - "Community 20"
-Cohesion: 0.31
-Nodes (11): create_equipment(), _dec(), equipment_code_exists(), equipment_is_referenced(), get_equipment(), list_equipments(), _norm_status(), _parse_date() (+3 more)
-
-### Community 21 - "Community 21"
 Cohesion: 0.24
 Cohesion: 0.24
 Nodes (11): deduct_for_step(), _display_remark(), list_stock_logs(), _parse_receiver_purpose_from_remark(), 试剂变动记录 CRUD(biz_reagent_stock_log), 撤销步骤此前 step_save 扣减,便于重新填报。, 扣减试剂库存并生成变动记录。调用方控制事务,本函数不 commit。, _resolve_receiver_purpose() (+3 more)
 Nodes (11): deduct_for_step(), _display_remark(), list_stock_logs(), _parse_receiver_purpose_from_remark(), 试剂变动记录 CRUD(biz_reagent_stock_log), 撤销步骤此前 step_save 扣减,便于重新填报。, 扣减试剂库存并生成变动记录。调用方控制事务,本函数不 commit。, _resolve_receiver_purpose() (+3 more)
 
 
+### Community 21 - "Community 21"
+Cohesion: 0.27
+Nodes (10): create_collection_region(), create_hospital(), get_collection_region(), get_hospital(), _hospital_to_dict(), list_hospitals(), soft_delete_collection_region(), soft_delete_hospital() (+2 more)
+
 ### Community 22 - "Community 22"
 ### Community 22 - "Community 22"
 Cohesion: 0.32
 Cohesion: 0.32
 Nodes (11): create_env_detect_record(), _generate_record_no(), get_env_detect_record(), _join_csv(), list_env_detect_records(), _resolve_inspector_snapshots(), _resolve_item_snapshot(), _row_to_dict() (+3 more)
 Nodes (11): create_env_detect_record(), _generate_record_no(), get_env_detect_record(), _join_csv(), list_env_detect_records(), _resolve_inspector_snapshots(), _resolve_item_snapshot(), _row_to_dict() (+3 more)
@@ -230,107 +231,111 @@ Nodes (7): create_post(), get_post(), list_posts(), post_code_exists(), _row_to_
 
 
 ### Community 35 - "Community 35"
 ### Community 35 - "Community 35"
 Cohesion: 0.36
 Cohesion: 0.36
-Nodes (7): create_tenant(), get_tenant(), list_tenants(), _row_to_dict(), soft_delete_tenant(), tenant_no_exists(), update_tenant()
+Nodes (8): company_code_exists(), create_company(), get_company(), list_companies(), list_company_options(), _row_to_dict(), soft_delete_company(), update_company()
 
 
 ### Community 36 - "Community 36"
 ### 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 37 - "Community 37"
 Cohesion: 0.33
 Cohesion: 0.33
 Nodes (7): _cascade_soft_delete(), create_menu(), get_menu(), list_menus(), _row_to_dict(), soft_delete_menu(), update_menu()
 Nodes (7): _cascade_soft_delete(), create_menu(), get_menu(), list_menus(), _row_to_dict(), soft_delete_menu(), update_menu()
 
 
-### Community 37 - "Community 37"
+### Community 38 - "Community 38"
 Cohesion: 0.36
 Cohesion: 0.36
 Nodes (7): create_site(), get_site(), list_sites(), _row_to_dict(), site_code_exists(), soft_delete_site(), update_site()
 Nodes (7): create_site(), get_site(), list_sites(), _row_to_dict(), site_code_exists(), soft_delete_site(), update_site()
 
 
-### Community 38 - "Community 38"
+### Community 39 - "Community 39"
 Cohesion: 0.25
 Cohesion: 0.25
 Nodes (7): 菜单 code 权限守卫(V0.3:通过 sys_user_role → sys_role_menu → sys_menu.perms), 运营端角色守卫:admin / super_admin 可访问。, 接口菜单权限守卫。super_admin 直接放行。, 接口菜单权限守卫:拥有任一 menuCode 即可访问。super_admin 直接放行。, require_admin_or_super_admin(), require_any_menu_code(), require_menu_code()
 Nodes (7): 菜单 code 权限守卫(V0.3:通过 sys_user_role → sys_role_menu → sys_menu.perms), 运营端角色守卫:admin / super_admin 可访问。, 接口菜单权限守卫。super_admin 直接放行。, 接口菜单权限守卫:拥有任一 menuCode 即可访问。super_admin 直接放行。, require_admin_or_super_admin(), require_any_menu_code(), require_menu_code()
 
 
-### Community 39 - "Community 39"
+### Community 40 - "Community 40"
 Cohesion: 0.29
 Cohesion: 0.29
 Nodes (2): BaseModel, SoftDeleteModel
 Nodes (2): BaseModel, SoftDeleteModel
 
 
-### Community 40 - "Community 40"
+### Community 41 - "Community 41"
 Cohesion: 0.38
 Cohesion: 0.38
 Nodes (6): derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。     约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断;     若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
 Nodes (6): derive_legacy_status_from_three(), derive_three_status_from_legacy(), 阶段A兼容:从旧 status 推导三字段。     约束:旧 status 本身混合了支付/配送/订单生命周期,因此只能尽力推断;     若业务侧已有更精确, 阶段A兼容:从三字段反推旧 status(用于旧前端/旧筛选参数兼容)。, ThreeStatus, _to_decimal()
 
 
-### Community 41 - "Community 41"
+### Community 42 - "Community 42"
 Cohesion: 0.5
 Cohesion: 0.5
 Nodes (3): BaseSettings, Config, Settings
 Nodes (3): BaseSettings, Config, Settings
 
 
-### Community 42 - "Community 42"
+### Community 43 - "Community 43"
 Cohesion: 0.5
 Cohesion: 0.5
 Nodes (2): _auto_fill_create_dept_id(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID
 Nodes (2): _auto_fill_create_dept_id(), ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID
 
 
-### Community 43 - "Community 43"
+### Community 44 - "Community 44"
 Cohesion: 0.67
 Cohesion: 0.67
 Nodes (2): get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)
 Nodes (2): get_menu_list(), 运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)
 
 
-### Community 44 - "Community 44"
+### Community 45 - "Community 45"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 请求级别的用户上下文变量  通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传
 Nodes (1): 请求级别的用户上下文变量  通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传
 
 
-### Community 45 - "Community 45"
+### Community 46 - "Community 46"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)
 Nodes (1): API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)
 
 
-### Community 48 - "Community 48"
+### Community 49 - "Community 49"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送订单状态通知                  Args:             openid: 用户openid             ord
 Nodes (1): 发送订单状态通知                  Args:             openid: 用户openid             ord
 
 
-### Community 49 - "Community 49"
+### Community 50 - "Community 50"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送支付成功通知                  Args:             openid: 用户openid             ord
 Nodes (1): 发送支付成功通知                  Args:             openid: 用户openid             ord
 
 
-### Community 50 - "Community 50"
+### Community 51 - "Community 51"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送配送通知                  Args:             openid: 配送员openid             task
 Nodes (1): 发送配送通知                  Args:             openid: 配送员openid             task
 
 
-### Community 51 - "Community 51"
+### Community 52 - "Community 52"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送配送时效预警                  Args:             openid: 配送员openid             ta
 Nodes (1): 发送配送时效预警                  Args:             openid: 配送员openid             ta
 
 
-### Community 52 - "Community 52"
+### Community 53 - "Community 53"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送退款结果通知                  Args:             openid: 用户openid             ord
 Nodes (1): 发送退款结果通知                  Args:             openid: 用户openid             ord
 
 
-### Community 53 - "Community 53"
+### Community 54 - "Community 54"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (1): 发送催单通知给配送员                  Args:             deliver_openid: 配送员openid
 Nodes (1): 发送催单通知给配送员                  Args:             deliver_openid: 配送员openid
 
 
 ## Knowledge Gaps
 ## Knowledge Gaps
 - **127 isolated node(s):** `刷新会话 TTL(refreshToken 续期;已登录用户每次 API 校验通过后滑动续期)`, `写入 refreshToken(用于 one-time 使用/吊销)`, `根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。` (+122 more)
 - **127 isolated node(s):** `刷新会话 TTL(refreshToken 续期;已登录用户每次 API 校验通过后滑动续期)`, `写入 refreshToken(用于 one-time 使用/吊销)`, `根据 token 获取当前用户(包含 Redis 会话校验;会话滑动续期)`, `登录凭据 RSA 解密(与前端 JSEncrypt PKCS#1 v1.5 兼容)。`, `Base64 密文 -> JSON 对象(须含 username、password、ts 毫秒时间戳)。` (+122 more)
   These have ≤1 connection - possible missing edges or undocumented components.
   These have ≤1 connection - possible missing edges or undocumented components.
-- **Thin community `Community 39`** (8 nodes): `BaseModel`, `.to_dict()`, `get_active()`, `base.py`, `SoftDeleteModel`, `.restore()`, `.soft_delete()`, `__tablename__()`
+- **Thin community `Community 40`** (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.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 42`** (4 nodes): `_auto_fill_create_dept_id()`, `get_db()`, `database.py`, `ORM before_insert 钩子:当 create_dept_id 为空时,自动从请求上下文填充当前用户部门 ID`
+- **Thin community `Community 43`** (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.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 43`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)`
+- **Thin community `Community 44`** (3 nodes): `get_menu_list()`, `menu.py`, `运营端菜单接口(与 web-admin 前端静态路由 sqxp.ts 对齐,5 个一级菜单分组)`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 44`** (2 nodes): `context.py`, `请求级别的用户上下文变量  通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
+- **Thin community `Community 45`** (2 nodes): `context.py`, `请求级别的用户上下文变量  通过 contextvars 在请求处理链路中传递当前用户信息, 供 SQLAlchemy 事件监听器等底层机制使用,避免逐层手动传`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 45`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
+- **Thin community `Community 46`** (2 nodes): `__init__.py`, `API 路由包(骨架:公共鉴权 + 静态菜单 + 运营端占位)`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 48`** (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.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 49`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 50`** (1 nodes): `发送支付成功通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 50`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
+- **Thin community `Community 51`** (1 nodes): `发送配送通知                  Args:             openid: 配送员openid             task`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 51`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
+- **Thin community `Community 52`** (1 nodes): `发送配送时效预警                  Args:             openid: 配送员openid             ta`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 52`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
+- **Thin community `Community 53`** (1 nodes): `发送退款结果通知                  Args:             openid: 用户openid             ord`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 53`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
+- **Thin community `Community 54`** (1 nodes): `发送催单通知给配送员                  Args:             deliver_openid: 配送员openid`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
 
 
 ## Suggested Questions
 ## Suggested Questions
 _Questions this graph is uniquely positioned to answer:_
 _Questions this graph is uniquely positioned to answer:_
 
 
-- **Why does `SysUser` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 14`, `Community 17`, `Community 21`, `Community 23`?**
-  _High betweenness centrality (0.106) - this node is a cross-community bridge._
-- **Why does `generate_uuid()` connect `Community 4` to `Community 0`, `Community 1`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 21`, `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`?**
-  _High betweenness centrality (0.079) - this node is a cross-community bridge._
-- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 3`, `Community 7`, `Community 8`, `Community 9`, `Community 43`, `Community 12`, `Community 13`?**
-  _High betweenness centrality (0.058) - this node is a cross-community bridge._
+- **Why does `SysUser` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 11`, `Community 12`, `Community 14`, `Community 20`, `Community 23`?**
+  _High betweenness centrality (0.105) - this node is a cross-community bridge._
+- **Why does `generate_uuid()` connect `Community 4` to `Community 0`, `Community 1`, `Community 3`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 13`, `Community 14`, `Community 16`, `Community 18`, `Community 19`, `Community 20`, `Community 21`, `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`?**
+  _High betweenness centrality (0.082) - this node is a cross-community bridge._
+- **Why does `success_response()` connect `Community 0` to `Community 1`, `Community 3`, `Community 4`, `Community 7`, `Community 9`, `Community 10`, `Community 11`, `Community 44`, `Community 13`, `Community 14`?**
+  _High betweenness centrality (0.065) - this node is a cross-community bridge._
 - **Are the 693 inferred relationships involving `str` (e.g. with `unhandled_exception_handler()` and `get_current_user_by_token()`) actually correct?**
 - **Are the 693 inferred relationships involving `str` (e.g. with `unhandled_exception_handler()` and `get_current_user_by_token()`) actually correct?**
   _`str` has 693 INFERRED edges - model-reasoned connections that need verification._
   _`str` has 693 INFERRED edges - model-reasoned connections that need verification._
 - **Are the 642 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**
 - **Are the 642 inferred relationships involving `success_response()` (e.g. with `list_storage_release_reviews()` and `get_storage_release_review_detail()`) actually correct?**

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
backend/graphify-out/graph.html


Разница между файлами не показана из-за своего большого размера
+ 112 - 112
backend/graphify-out/graph.json


+ 2 - 2
backend/graphify-out/manifest.json

@@ -85,7 +85,7 @@
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_check_result.py": 1779255909.8554008,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/sample_reception_check_result.py": 1779255909.8554008,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py": 1781513705.4688208,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_material_stock_batch.py": 1781513705.4688208,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py": 1780539127.1805215,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/user.py": 1780539127.1805215,
-  "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_work_order.py": 1781583479.8739135,
+  "/Users/wuzilong/work/00code/zkxw/backend/app/crud/production_work_order.py": 1781696877.7453213,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/material.py": 1781522283.6438622,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/material.py": 1781522283.6438622,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/dict_type.py": 1779265621.5212462,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/dict_type.py": 1779265621.5212462,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_occupancy.py": 1781335540.360182,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/storage_occupancy.py": 1781335540.360182,
@@ -136,7 +136,7 @@
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_item.py": 1781145689.9525824,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_item.py": 1781145689.9525824,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_category.py": 1779265700.0606413,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/process_category.py": 1779265700.0606413,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/item_unit.py": 1779265653.7504313,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/item_unit.py": 1779265653.7504313,
-  "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py": 1781607961.140553,
+  "/Users/wuzilong/work/00code/zkxw/backend/app/crud/qc_work_order.py": 1781698777.252274,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_handover_inventory.py": 1781598316.476379,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_handover_inventory.py": 1781598316.476379,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py": 1781145773.959546,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/env_detect_record.py": 1781145773.959546,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_release_material.py": 1781612877.0137436,
   "/Users/wuzilong/work/00code/zkxw/backend/app/crud/outbound_release_material.py": 1781612877.0137436,

Разница между файлами не показана из-за своего большого размера
+ 454 - 540
web-admin/graphify-out/.graphify_analysis.json


Разница между файлами не показана из-за своего большого размера
+ 90 - 397
web-admin/graphify-out/.graphify_extract.json


+ 106 - 101
web-admin/graphify-out/GRAPH_REPORT.md

@@ -1,11 +1,11 @@
 # Graph Report - src  (2026-06-17)
 # Graph Report - src  (2026-06-17)
 
 
 ## Corpus Check
 ## Corpus Check
-- Large corpus: 784 files · ~519,508 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
+- Large corpus: 784 files · ~520,683 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
 
 
 ## Summary
 ## Summary
-- 2617 nodes · 3644 edges · 53 communities detected
-- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 799 edges (avg confidence: 0.8)
+- 2595 nodes · 3611 edges · 54 communities detected
+- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 795 edges (avg confidence: 0.8)
 - Token cost: 0 input · 0 output
 - Token cost: 0 input · 0 output
 
 
 ## Community Hubs (Navigation)
 ## Community Hubs (Navigation)
@@ -44,29 +44,30 @@
 - [[_COMMUNITY_Community 32|Community 32]]
 - [[_COMMUNITY_Community 32|Community 32]]
 - [[_COMMUNITY_Community 33|Community 33]]
 - [[_COMMUNITY_Community 33|Community 33]]
 - [[_COMMUNITY_Community 34|Community 34]]
 - [[_COMMUNITY_Community 34|Community 34]]
-- [[_COMMUNITY_Community 36|Community 36]]
+- [[_COMMUNITY_Community 35|Community 35]]
 - [[_COMMUNITY_Community 37|Community 37]]
 - [[_COMMUNITY_Community 37|Community 37]]
 - [[_COMMUNITY_Community 38|Community 38]]
 - [[_COMMUNITY_Community 38|Community 38]]
 - [[_COMMUNITY_Community 39|Community 39]]
 - [[_COMMUNITY_Community 39|Community 39]]
 - [[_COMMUNITY_Community 40|Community 40]]
 - [[_COMMUNITY_Community 40|Community 40]]
 - [[_COMMUNITY_Community 41|Community 41]]
 - [[_COMMUNITY_Community 41|Community 41]]
-- [[_COMMUNITY_Community 43|Community 43]]
+- [[_COMMUNITY_Community 42|Community 42]]
 - [[_COMMUNITY_Community 45|Community 45]]
 - [[_COMMUNITY_Community 45|Community 45]]
 - [[_COMMUNITY_Community 46|Community 46]]
 - [[_COMMUNITY_Community 46|Community 46]]
-- [[_COMMUNITY_Community 49|Community 49]]
+- [[_COMMUNITY_Community 47|Community 47]]
 - [[_COMMUNITY_Community 50|Community 50]]
 - [[_COMMUNITY_Community 50|Community 50]]
-- [[_COMMUNITY_Community 52|Community 52]]
+- [[_COMMUNITY_Community 51|Community 51]]
 - [[_COMMUNITY_Community 53|Community 53]]
 - [[_COMMUNITY_Community 53|Community 53]]
 - [[_COMMUNITY_Community 54|Community 54]]
 - [[_COMMUNITY_Community 54|Community 54]]
 - [[_COMMUNITY_Community 56|Community 56]]
 - [[_COMMUNITY_Community 56|Community 56]]
 - [[_COMMUNITY_Community 58|Community 58]]
 - [[_COMMUNITY_Community 58|Community 58]]
-- [[_COMMUNITY_Community 62|Community 62]]
+- [[_COMMUNITY_Community 59|Community 59]]
 - [[_COMMUNITY_Community 64|Community 64]]
 - [[_COMMUNITY_Community 64|Community 64]]
+- [[_COMMUNITY_Community 66|Community 66]]
 
 
 ## God Nodes (most connected - your core abstractions)
 ## God Nodes (most connected - your core abstractions)
 1. `get()` - 265 edges
 1. `get()` - 265 edges
 2. `push()` - 64 edges
 2. `push()` - 64 edges
-3. `emit()` - 28 edges
+3. `emit()` - 27 edges
 4. `WebSocketClient` - 26 edges
 4. `WebSocketClient` - 26 edges
 5. `MenuProcessor` - 16 edges
 5. `MenuProcessor` - 16 edges
 6. `withFullscreenLoading()` - 15 edges
 6. `withFullscreenLoading()` - 15 edges
@@ -91,192 +92,192 @@
 
 
 ### Community 0 - "Community 0"
 ### Community 0 - "Community 0"
 Cohesion: 0.01
 Cohesion: 0.01
-Nodes (144): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchChannelAccount(), fetchChannelAgreementPrices(), fetchChannelAgreements() (+136 more)
+Nodes (142): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchChannelAccount(), fetchChannelAgreementPrices(), fetchChannelAgreements() (+134 more)
 
 
 ### Community 1 - "Community 1"
 ### Community 1 - "Community 1"
-Cohesion: 0.03
-Nodes (52): fetchGetMenuList(), fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchUpdateProfile(), fetchUploadProfileAvatar(), ComponentLoader, IframeRouteManager (+44 more)
-
-### Community 2 - "Community 2"
 Cohesion: 0.02
 Cohesion: 0.02
 Nodes (69): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchAttachmentDownloadUrl(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchCreateProtocolInvoiceRequest(), fetchInvoiceEligiblePayments() (+61 more)
 Nodes (69): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchAttachmentDownloadUrl(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchCreateProtocolInvoiceRequest(), fetchInvoiceEligiblePayments() (+61 more)
 
 
-### Community 3 - "Community 3"
+### Community 2 - "Community 2"
 Cohesion: 0.02
 Cohesion: 0.02
 Nodes (42): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumableStockLogs(), fetchAllEquipments(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents() (+34 more)
 Nodes (42): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumableStockLogs(), fetchAllEquipments(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents() (+34 more)
 
 
-### Community 4 - "Community 4"
+### Community 3 - "Community 3"
 Cohesion: 0.03
 Cohesion: 0.03
-Nodes (60): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), fetchProductionTaskList(), handleAdd(), handleBatchDelete() (+52 more)
+Nodes (35): fetchGetMenuList(), fetchGetMenuCodes(), fetchGetUserInfo(), ComponentLoader, IframeRouteManager, MenuProcessor, RouteRegistry, RouteTransformer (+27 more)
 
 
-### Community 5 - "Community 5"
+### Community 4 - "Community 4"
 Cohesion: 0.04
 Cohesion: 0.04
-Nodes (78): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupActualItems(), fetchPilotCheckupDetail(), fetchPilotCheckupReserveItems() (+70 more)
+Nodes (74): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupDetail(), markPilotCheckupChecked(), savePilotCheckup(), savePilotCheckupActualItems() (+66 more)
+
+### Community 5 - "Community 5"
+Cohesion: 0.03
+Nodes (51): fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionList(), fetchCheckupInstitutionProjects(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchUpdateCheckupInstitution(), fetchUpdateCheckupInstitutionInvoice() (+43 more)
 
 
 ### Community 6 - "Community 6"
 ### Community 6 - "Community 6"
-Cohesion: 0.04
-Nodes (66): fetchUploadAttachment(), reload(), handleReportUpload(), formatNowDatetime(), handleReportUpload(), walk(), formatTodayDate(), handleMenuJump() (+58 more)
+Cohesion: 0.05
+Nodes (60): fetchUploadAttachment(), reload(), handleReportUpload(), handleReportUpload(), walk(), handleMenuJump(), openExternalLink(), async() (+52 more)
 
 
 ### Community 7 - "Community 7"
 ### Community 7 - "Community 7"
-Cohesion: 0.03
-Nodes (50): fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionProjects(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchUpdateCheckupInstitution(), fetchUpdateCheckupInstitutionInvoice(), fetchUpdateCheckupInstitutionProject() (+42 more)
+Cohesion: 0.04
+Nodes (42): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), for(), set(), annotateForkEdges(), chainApiToElements() (+34 more)
 
 
 ### Community 8 - "Community 8"
 ### Community 8 - "Community 8"
 Cohesion: 0.04
 Cohesion: 0.04
-Nodes (43): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), for(), set(), annotateForkEdges(), chainApiToElements() (+35 more)
+Nodes (38): fetchProductionTaskList(), fetchApproveTask(), fetchBusinessAllTasks(), fetchCompletedTasks(), fetchDesignerData(), fetchInstanceDetail(), fetchMyInstances(), fetchPendingTasks() (+30 more)
 
 
 ### Community 9 - "Community 9"
 ### Community 9 - "Community 9"
-Cohesion: 0.04
-Nodes (41): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), buildInboundBasicSection(), buildInboundCellStorageViewSections(), buildInboundResearchDonationViewSections(), buildReceptionSection() (+33 more)
+Cohesion: 0.05
+Nodes (31): fetchDeleteQcMaterialBatch(), fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), fetchMyQcMaterialUsageList() (+23 more)
 
 
 ### Community 10 - "Community 10"
 ### Community 10 - "Community 10"
 Cohesion: 0.05
 Cohesion: 0.05
 Nodes (34): fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed(), fetchClinicalRefundPending(), fetchOrderAdjusts(), fetchOrderDetail() (+26 more)
 Nodes (34): fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed(), fetchClinicalRefundPending(), fetchOrderAdjusts(), fetchOrderDetail() (+26 more)
 
 
 ### Community 11 - "Community 11"
 ### Community 11 - "Community 11"
-Cohesion: 0.06
-Nodes (38): buildBaseFields(), buildFeeSection(), buildInboundBasicSection(), buildInboundCellStorageProductionViewSections(), buildInboundResearchDonationProductionViewSections(), buildOutboundBasicExperimentProductionViewSections(), buildOutboundClinicalProductionViewSections(), buildOutboundDetail() (+30 more)
+Cohesion: 0.05
+Nodes (34): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), buildInboundBasicSection(), buildInboundCellStorageViewSections(), buildInboundResearchDonationViewSections(), buildReceptionSection() (+26 more)
 
 
 ### Community 12 - "Community 12"
 ### Community 12 - "Community 12"
 Cohesion: 0.06
 Cohesion: 0.06
 Nodes (11): fetchGetLatestUpgradeLog(), getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager (+3 more)
 Nodes (11): fetchGetLatestUpgradeLog(), getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager (+3 more)
 
 
 ### Community 13 - "Community 13"
 ### Community 13 - "Community 13"
-Cohesion: 0.05
-Nodes (26): fetchDeleteQcMaterialBatch(), fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), financeAttachmentsToUrlString() (+18 more)
+Cohesion: 0.04
+Nodes (28): fetchEthnicList(), formatNowDatetime(), formatHandoverUserLabel(), formatNowDatetime(), fullFileUrl(), handleDownloadAttachment(), handlePreviewAttachment(), isVideoFile() (+20 more)
 
 
 ### Community 14 - "Community 14"
 ### Community 14 - "Community 14"
 Cohesion: 0.05
 Cohesion: 0.05
 Nodes (26): collectIds(), useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), walk() (+18 more)
 Nodes (26): collectIds(), useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), walk() (+18 more)
 
 
 ### Community 15 - "Community 15"
 ### Community 15 - "Community 15"
-Cohesion: 0.05
-Nodes (28): handleBlur(), handleFocus(), handleInput(), fetchEthnicList(), formatAmount(), formatAmount(), formatProtocolAmount(), applyDonationBundleToForm() (+20 more)
-
-### Community 16 - "Community 16"
 Cohesion: 0.06
 Cohesion: 0.06
 Nodes (13): fmtMoney(), auditorDisplay(), dash(), ensureUnitsInOptions(), expressName(), formatYm(), formatYmd(), sampleTypeName() (+5 more)
 Nodes (13): fmtMoney(), auditorDisplay(), dash(), ensureUnitsInOptions(), expressName(), formatYm(), formatYmd(), sampleTypeName() (+5 more)
 
 
+### Community 16 - "Community 16"
+Cohesion: 0.1
+Nodes (2): WebSocketClient, TableCache
+
 ### Community 17 - "Community 17"
 ### Community 17 - "Community 17"
-Cohesion: 0.06
-Nodes (34): fetchUserList(), fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchReassignProductionTask(), fetchReorderTaskSteps() (+26 more)
+Cohesion: 0.08
+Nodes (32): toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes() (+24 more)
 
 
 ### Community 18 - "Community 18"
 ### Community 18 - "Community 18"
-Cohesion: 0.1
-Nodes (2): WebSocketClient, TableCache
+Cohesion: 0.08
+Nodes (29): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), updateInfusionScheduleStatus(), handleAdd(), handleBatchDelete(), handleDelete() (+21 more)
 
 
 ### Community 19 - "Community 19"
 ### Community 19 - "Community 19"
 Cohesion: 0.08
 Cohesion: 0.08
-Nodes (33): fetchRefreshToken(), toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url() (+25 more)
+Nodes (27): fetchUserOptions(), fetchSampleReceptionCheckItemList(), fetchSampleReceptionCheckItemsActive(), fetchAllocateLabStorageCodes(), fetchSampleReceptionBySampleCode(), fetchSampleReceptionDetail(), fetchSampleReceptionPendingList(), fetchSampleReceptionReceivedList() (+19 more)
 
 
 ### Community 20 - "Community 20"
 ### Community 20 - "Community 20"
 Cohesion: 0.08
 Cohesion: 0.08
-Nodes (27): fetchUserOptions(), fetchSampleReceptionCheckItemList(), fetchSampleReceptionCheckItemsActive(), fetchAllocateLabStorageCodes(), fetchSampleReceptionBySampleCode(), fetchSampleReceptionDetail(), fetchSampleReceptionPendingList(), fetchSampleReceptionReceivedList() (+19 more)
+Nodes (20): maskIdCard(), buildBasicExperimentApplicationFields(), buildDefaultApplicationFields(), buildOutboundPilotContextSections(), applicationStatusLabel(), applicationStatusTagType(), cellProductStatusLabel(), cellProductStatusTagType() (+12 more)
 
 
 ### Community 21 - "Community 21"
 ### Community 21 - "Community 21"
 Cohesion: 0.09
 Cohesion: 0.09
-Nodes (28): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), clearTableSelection(), handleBatchComplete(), handleBatchStart() (+20 more)
+Nodes (31): handleBlur(), handleFocus(), handleInput(), formatAmount(), formatAmount(), formatProtocolAmount(), buildBaseFields(), buildFeeSection() (+23 more)
 
 
 ### Community 22 - "Community 22"
 ### Community 22 - "Community 22"
 Cohesion: 0.08
 Cohesion: 0.08
-Nodes (16): fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions(), fetchMaterialList(), fetchMaterialOptions(), fetchUpdateMaterialBatch(), fetchMaterialUsageList(), fetchMyMaterialUsageList() (+8 more)
+Nodes (27): fetchCellSampleTypeList(), fetchCreateCellSampleType(), fetchDeleteCellSampleType(), fetchUpdateCellSampleType(), handleAdd(), handleDelete(), handleDropdownCommand(), handleEdit() (+19 more)
 
 
 ### Community 23 - "Community 23"
 ### Community 23 - "Community 23"
-Cohesion: 0.12
-Nodes (24): fetchCellProductSpecList(), fetchCellProductTypeList(), applyProductCodeToLines(), calcExpiryFromFreeze(), createEmptyProductLine(), createQuantityValue(), getLineStorageSlotOptions(), getOtherLineStorageSlotIds() (+16 more)
+Cohesion: 0.09
+Nodes (28): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), clearTableSelection(), handleBatchComplete(), handleBatchStart() (+20 more)
 
 
 ### Community 24 - "Community 24"
 ### Community 24 - "Community 24"
-Cohesion: 0.13
-Nodes (20): fetchCreateUser(), fetchDeleteUser(), fetchResetPassword(), fetchUpdateUser(), fetchUserDetail(), formatUserDeptDisplay(), handleAdd(), handleBatchDelete() (+12 more)
+Cohesion: 0.1
+Nodes (27): fetchUserList(), fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchReassignProductionTask(), fetchReorderTaskSteps() (+19 more)
 
 
 ### Community 25 - "Community 25"
 ### Community 25 - "Community 25"
-Cohesion: 0.12
-Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
+Cohesion: 0.08
+Nodes (16): fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions(), fetchMaterialList(), fetchMaterialOptions(), fetchUpdateMaterialBatch(), fetchMaterialUsageList(), fetchMyMaterialUsageList() (+8 more)
 
 
 ### Community 26 - "Community 26"
 ### Community 26 - "Community 26"
-Cohesion: 0.11
-Nodes (11): fetchOutboundReleaseMaterial(), fetchOutboundReleaseReviewDetail(), fetchOutboundReleaseReviewList(), fetchReleaseReviewList(), handleReset(), handleSearch(), handleToggleSelect(), loadData() (+3 more)
+Cohesion: 0.12
+Nodes (25): fetchCellProductSpecList(), fetchCellProductTypeList(), fetchMaxPassageProductSeq(), applyProductCodeToLines(), calcExpiryFromFreeze(), createEmptyProductLine(), createQuantityValue(), getLineStorageSlotOptions() (+17 more)
 
 
 ### Community 27 - "Community 27"
 ### Community 27 - "Community 27"
 Cohesion: 0.13
 Cohesion: 0.13
-Nodes (18): fetchCellSampleTypeList(), fetchCreateCellSampleType(), fetchDeleteCellSampleType(), fetchUpdateCellSampleType(), handleAdd(), handleDelete(), handleDropdownCommand(), handleEdit() (+10 more)
+Nodes (18): fetchLogin(), fetchRefreshToken(), fetchUpdateProfile(), fetchUploadProfileAvatar(), buildWebEncryptedLoginBody(), defaultAvatarPathByRole(), hasCustomUserAvatar(), normalizeAvatarStorageKey() (+10 more)
 
 
 ### Community 28 - "Community 28"
 ### Community 28 - "Community 28"
-Cohesion: 0.16
-Nodes (14): fetchCreateEnvDetectItem(), fetchDeleteEnvDetectItem(), fetchEnvDetectItemDetail(), fetchEnvDetectItemList(), fetchEnvDetectItemsActive(), fetchUpdateEnvDetectItem(), handleAdd(), handleBatchDelete() (+6 more)
+Cohesion: 0.13
+Nodes (20): fetchCreateUser(), fetchDeleteUser(), fetchResetPassword(), fetchUpdateUser(), fetchUserDetail(), formatUserDeptDisplay(), handleAdd(), handleBatchDelete() (+12 more)
 
 
 ### Community 29 - "Community 29"
 ### Community 29 - "Community 29"
 Cohesion: 0.12
 Cohesion: 0.12
-Nodes (12): fetchApproveTask(), fetchBusinessAllTasks(), fetchCompletedTasks(), fetchDesignerData(), fetchInstanceDetail(), fetchMyInstances(), fetchPendingTasks(), fetchProcessDefList() (+4 more)
+Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
 
 
 ### Community 30 - "Community 30"
 ### Community 30 - "Community 30"
+Cohesion: 0.11
+Nodes (11): fetchOutboundReleaseMaterial(), fetchOutboundReleaseReviewDetail(), fetchOutboundReleaseReviewList(), fetchReleaseReviewList(), handleReset(), handleSearch(), handleToggleSelect(), loadData() (+3 more)
+
+### Community 31 - "Community 31"
+Cohesion: 0.16
+Nodes (14): fetchCreateEnvDetectItem(), fetchDeleteEnvDetectItem(), fetchEnvDetectItemDetail(), fetchEnvDetectItemList(), fetchEnvDetectItemsActive(), fetchUpdateEnvDetectItem(), handleAdd(), handleBatchDelete() (+6 more)
+
+### Community 32 - "Community 32"
 Cohesion: 0.16
 Cohesion: 0.16
 Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
 Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
 
 
-### Community 31 - "Community 31"
+### Community 33 - "Community 33"
 Cohesion: 0.27
 Cohesion: 0.27
 Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
 Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
 
 
-### Community 32 - "Community 32"
+### Community 34 - "Community 34"
 Cohesion: 0.33
 Cohesion: 0.33
 Nodes (13): buildProtocolPaymentFlowSteps(), dash(), financeReceiptTime(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit() (+5 more)
 Nodes (13): buildProtocolPaymentFlowSteps(), dash(), financeReceiptTime(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit() (+5 more)
 
 
-### Community 33 - "Community 33"
-Cohesion: 0.22
-Nodes (9): fullFileUrl(), handleDownloadAttachment(), handlePreviewAttachment(), isVideoFile(), normalizeDisplayValue(), resolveReviewCellType(), resolveReviewQuantity(), resolveReviewSpecText() (+1 more)
-
-### Community 34 - "Community 34"
+### Community 35 - "Community 35"
 Cohesion: 0.19
 Cohesion: 0.19
 Nodes (6): fetchServiceOrderTxnList(), handleReset(), handleSearch(), handleTabChange(), loadData(), switchTab()
 Nodes (6): fetchServiceOrderTxnList(), handleReset(), handleSearch(), handleTabChange(), loadData(), switchTab()
 
 
-### Community 36 - "Community 36"
+### Community 37 - "Community 37"
 Cohesion: 0.18
 Cohesion: 0.18
 Nodes (3): normalizeStepSubTables(), parseEquipmentIds(), syncEquipmentNamesFromOptions()
 Nodes (3): normalizeStepSubTables(), parseEquipmentIds(), syncEquipmentNamesFromOptions()
 
 
-### Community 37 - "Community 37"
+### Community 38 - "Community 38"
+Cohesion: 0.17
+Nodes (3): fetchResearchDonationAuditList(), fetchResearchDonationDetail(), fetchResearchDonationList()
+
+### Community 39 - "Community 39"
 Cohesion: 0.24
 Cohesion: 0.24
 Nodes (6): buildInboundMetaFields(), getCardMetaConfig(), getCardMetaConfigForLegacyTab(), parseReceptionTime(), receptionOver24HoursLabel(), receptionOver24HoursTagType()
 Nodes (6): buildInboundMetaFields(), getCardMetaConfig(), getCardMetaConfigForLegacyTab(), parseReceptionTime(), receptionOver24HoursLabel(), receptionOver24HoursTagType()
 
 
-### Community 38 - "Community 38"
+### Community 40 - "Community 40"
 Cohesion: 0.42
 Cohesion: 0.42
 Nodes (9): buildPaymentFlowSteps(), dash(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit(), receiptSubmitTime() (+1 more)
 Nodes (9): buildPaymentFlowSteps(), dash(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit(), receiptSubmitTime() (+1 more)
 
 
-### Community 39 - "Community 39"
-Cohesion: 0.2
-Nodes (3): fetchProductionWorkOrderCellProductOptions(), fetchProductionWorkOrderDetail(), fetchProductionWorkOrderList()
-
-### Community 40 - "Community 40"
+### Community 41 - "Community 41"
 Cohesion: 0.25
 Cohesion: 0.25
 Nodes (2): handleTouchEnd(), setTransition()
 Nodes (2): handleTouchEnd(), setTransition()
 
 
-### Community 41 - "Community 41"
+### Community 42 - "Community 42"
 Cohesion: 0.28
 Cohesion: 0.28
 Nodes (1): FireworkSystem
 Nodes (1): FireworkSystem
 
 
-### Community 43 - "Community 43"
+### Community 45 - "Community 45"
 Cohesion: 0.28
 Cohesion: 0.28
 Nodes (4): fetchMyProductionMaterialConsumptions(), handleReset(), handleSearch(), loadData()
 Nodes (4): fetchMyProductionMaterialConsumptions(), handleReset(), handleSearch(), loadData()
 
 
-### Community 45 - "Community 45"
+### Community 46 - "Community 46"
 Cohesion: 0.39
 Cohesion: 0.39
 Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
 Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
 
 
-### Community 46 - "Community 46"
+### Community 47 - "Community 47"
 Cohesion: 0.36
 Cohesion: 0.36
 Nodes (1): TableHeightCalculator
 Nodes (1): TableHeightCalculator
 
 
-### Community 49 - "Community 49"
+### Community 50 - "Community 50"
 Cohesion: 0.33
 Cohesion: 0.33
 Nodes (2): logApiCryptoBootInfo(), maskSecret()
 Nodes (2): logApiCryptoBootInfo(), maskSecret()
 
 
-### Community 50 - "Community 50"
+### Community 51 - "Community 51"
 Cohesion: 0.47
 Cohesion: 0.47
 Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
 Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
 
 
-### Community 52 - "Community 52"
-Cohesion: 0.33
-Nodes (2): fetchQcLabTestItemList(), fetchQcLabTestItemsActive()
-
 ### Community 53 - "Community 53"
 ### Community 53 - "Community 53"
 Cohesion: 0.5
 Cohesion: 0.5
 Nodes (2): formatProtocolDocDeleteConfirm(), formatProtocolDocSubject()
 Nodes (2): formatProtocolDocDeleteConfirm(), formatProtocolDocSubject()
@@ -291,28 +292,30 @@ Nodes (2): if(), logSidebarMenu()
 
 
 ### Community 58 - "Community 58"
 ### Community 58 - "Community 58"
 Cohesion: 0.5
 Cohesion: 0.5
-Nodes (1): fetchStorageEntrySmsList()
+Nodes (1): fetchSampleSmsNotificationList()
 
 
-### Community 62 - "Community 62"
+### Community 59 - "Community 59"
+Cohesion: 0.5
+Nodes (1): fetchQcSmsList()
+
+### Community 64 - "Community 64"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (2): getSettingDefaults(), resetToDefaults()
 Nodes (2): getSettingDefaults(), resetToDefaults()
 
 
-### Community 64 - "Community 64"
+### Community 66 - "Community 66"
 Cohesion: 1.0
 Cohesion: 1.0
 Nodes (2): copyTextToClipboard(), copyTextWithFallback()
 Nodes (2): copyTextToClipboard(), copyTextWithFallback()
 
 
 ## Knowledge Gaps
 ## Knowledge Gaps
-- **Thin community `Community 18`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
-  Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 40`** (9 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `if()`, `setTransition()`, `setupEventListeners()`, `index.vue`
+- **Thin community `Community 16`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 41`** (9 nodes): `FireworkSystem`, `.constructor()`, `.createFirework()`, `.createParticle()`, `.getAvailableParticle()`, `.initializePool()`, `.preloadAllImages()`, `.preloadImage()`, `index.vue`
+- **Thin community `Community 41`** (9 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `if()`, `setTransition()`, `setupEventListeners()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 46`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
+- **Thin community `Community 42`** (9 nodes): `FireworkSystem`, `.constructor()`, `.createFirework()`, `.createParticle()`, `.getAvailableParticle()`, `.initializePool()`, `.preloadAllImages()`, `.preloadImage()`, `index.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 49`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
+- **Thin community `Community 47`** (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.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 52`** (6 nodes): `fetchCreateQcLabTestItem()`, `fetchDeleteQcLabTestItem()`, `fetchQcLabTestItemList()`, `fetchQcLabTestItemsActive()`, `fetchUpdateQcLabTestItem()`, `qc-lab-test-item.ts`
+- **Thin community `Community 50`** (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.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
 - **Thin community `Community 53`** (5 nodes): `formatProtocolDocBatchDeleteConfirm()`, `formatProtocolDocDeleteConfirm()`, `formatProtocolDocSubject()`, `getProtocolDocTypeLabel()`, `protocol-doc-label.ts`
 - **Thin community `Community 53`** (5 nodes): `formatProtocolDocBatchDeleteConfirm()`, `formatProtocolDocDeleteConfirm()`, `formatProtocolDocSubject()`, `getProtocolDocTypeLabel()`, `protocol-doc-label.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
@@ -320,27 +323,29 @@ Nodes (2): copyTextToClipboard(), copyTextWithFallback()
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
 - **Thin community `Community 56`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
 - **Thin community `Community 56`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 58`** (4 nodes): `fetchGenerateStorageEntrySms()`, `fetchStorageEntrySmsList()`, `fetchToggleStorageEntrySmsNotified()`, `storage-entry-sms-notification.ts`
+- **Thin community `Community 58`** (4 nodes): `fetchGenerateSms()`, `fetchSampleSmsNotificationList()`, `fetchToggleSmsNotified()`, `sample-sms-notification.ts`
+  Too small to be a meaningful cluster - may be noise or needs more connections extracted.
+- **Thin community `Community 59`** (4 nodes): `fetchGenerateQcSms()`, `fetchQcSmsList()`, `fetchToggleQcSmsNotified()`, `qc-sms-notification.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 62`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
+- **Thin community `Community 64`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
-- **Thin community `Community 64`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
+- **Thin community `Community 66`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
   Too small to be a meaningful cluster - may be noise or needs more connections extracted.
 
 
 ## Suggested Questions
 ## Suggested Questions
 _Questions this graph is uniquely positioned to answer:_
 _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 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 17`, `Community 20`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 26`, `Community 27`, `Community 28`, `Community 29`, `Community 34`, `Community 39`, `Community 43`, `Community 52`, `Community 58`?**
-  _High betweenness centrality (0.388) - this node is a cross-community bridge._
-- **Why does `push()` connect `Community 6` to `Community 0`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 7`, `Community 8`, `Community 9`, `Community 11`, `Community 12`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 21`, `Community 23`, `Community 37`, `Community 41`?**
-  _High betweenness centrality (0.198) - this node is a cross-community bridge._
-- **Why does `fetchGetUserInfo()` connect `Community 1` to `Community 0`?**
-  _High betweenness centrality (0.037) - this node is a cross-community bridge._
+- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 18`, `Community 19`, `Community 22`, `Community 23`, `Community 24`, `Community 25`, `Community 26`, `Community 28`, `Community 30`, `Community 31`, `Community 35`, `Community 38`, `Community 45`, `Community 58`, `Community 59`?**
+  _High betweenness centrality (0.387) - this node is a cross-community bridge._
+- **Why does `push()` connect `Community 6` to `Community 0`, `Community 1`, `Community 2`, `Community 4`, `Community 5`, `Community 7`, `Community 8`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 20`, `Community 21`, `Community 23`, `Community 26`, `Community 39`, `Community 42`?**
+  _High betweenness centrality (0.182) - this node is a cross-community bridge._
 - **Are the 264 inferred relationships involving `get()` (e.g. with `buildNodeCodePathFromFlat()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
 - **Are the 264 inferred relationships involving `get()` (e.g. with `buildNodeCodePathFromFlat()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
   _`get()` has 264 INFERRED edges - model-reasoned connections that need verification._
   _`get()` has 264 INFERRED edges - model-reasoned connections that need verification._
 - **Are the 63 inferred relationships involving `push()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `formatDetectMethods()`) actually correct?**
 - **Are the 63 inferred relationships involving `push()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `formatDetectMethods()`) actually correct?**
   _`push()` has 63 INFERRED edges - model-reasoned connections that need verification._
   _`push()` has 63 INFERRED edges - model-reasoned connections that need verification._
-- **Are the 27 inferred relationships involving `emit()` (e.g. with `handleSubmit()` and `handleBlur()`) actually correct?**
-  _`emit()` has 27 INFERRED edges - model-reasoned connections that need verification._
+- **Are the 26 inferred relationships involving `emit()` (e.g. with `handleSubmit()` and `handleBlur()`) actually correct?**
+  _`emit()` has 26 INFERRED edges - model-reasoned connections that need verification._
 - **Should `Community 0` be split into smaller, more focused modules?**
 - **Should `Community 0` be split into smaller, more focused modules?**
-  _Cohesion score 0.01 - nodes in this community are weakly interconnected._
+  _Cohesion score 0.01 - nodes in this community are weakly interconnected._
+- **Should `Community 1` be split into smaller, more focused modules?**
+  _Cohesion score 0.02 - nodes in this community are weakly interconnected._

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
web-admin/graphify-out/graph.html


+ 2 - 2
web-admin/graphify-out/graph.json

@@ -21382,7 +21382,7 @@
       "label": "remoteSearchCustomers()",
       "label": "remoteSearchCustomers()",
       "file_type": "code",
       "file_type": "code",
       "source_file": "views/pilot-service/pilot-application/ApplicationFormDialog.vue",
       "source_file": "views/pilot-service/pilot-application/ApplicationFormDialog.vue",
-      "source_location": "L2498",
+      "source_location": "L2625",
       "id": "pilot_application_applicationformdialog_remotesearchcustomers",
       "id": "pilot_application_applicationformdialog_remotesearchcustomers",
       "community": 15,
       "community": 15,
       "norm_label": "remotesearchcustomers()"
       "norm_label": "remotesearchcustomers()"
@@ -63354,7 +63354,7 @@
       "relation": "contains",
       "relation": "contains",
       "confidence": "EXTRACTED",
       "confidence": "EXTRACTED",
       "source_file": "views/pilot-service/pilot-application/ApplicationFormDialog.vue",
       "source_file": "views/pilot-service/pilot-application/ApplicationFormDialog.vue",
-      "source_location": "L2498",
+      "source_location": "L2625",
       "weight": 1.0,
       "weight": 1.0,
       "_src": "views_pilot_service_pilot_application_applicationformdialog_vue",
       "_src": "views_pilot_service_pilot_application_applicationformdialog_vue",
       "_tgt": "pilot_application_applicationformdialog_remotesearchcustomers",
       "_tgt": "pilot_application_applicationformdialog_remotesearchcustomers",

+ 10 - 10
web-admin/graphify-out/manifest.json

@@ -17,8 +17,8 @@
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1781272556.3597305,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1781272556.3597305,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1781272556.3601494,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1781272556.3601494,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1781272556.3606477,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1781272556.3606477,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1781690399.279859,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1781690383.8771734,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1781698782.7971725,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1781698274.702177,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1781272556.364766,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1781272556.364766,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1781272556.3652694,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1781272556.3652694,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1781272556.3657222,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1781272556.3657222,
@@ -48,13 +48,13 @@
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fullscreen-loading.ts": 1781272556.3849921,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fullscreen-loading.ts": 1781272556.3849921,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-material-ui.ts": 1781525352.6976836,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-material-ui.ts": 1781525352.6976836,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-tabs.ts": 1781600462.9964879,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-tabs.ts": 1781600462.9964879,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-card-meta.ts": 1781608059.199473,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-work-order-card-meta.ts": 1781698792.9167962,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/router.ts": 1781272556.3875713,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/router.ts": 1781272556.3875713,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-mine-task-groups.ts": 1781583493.924234,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-mine-task-groups.ts": 1781583493.924234,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-mine-task-groups.ts": 1781608059.9990764,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/qc-mine-task-groups.ts": 1781608059.9990764,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-labels.ts": 1781272556.3878465,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-labels.ts": 1781272556.3878465,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1781604158.2285209,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1781604158.2285209,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts": 1781607345.7468734,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts": 1781696893.3885784,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fetch-paged-list.ts": 1781272556.3903377,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fetch-paged-list.ts": 1781272556.3903377,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sort-order.ts": 1781272556.3913012,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sort-order.ts": 1781272556.3913012,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/outbound-qc-pilot-view-sections.ts": 1781566346.7618802,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/outbound-qc-pilot-view-sections.ts": 1781566346.7618802,
@@ -237,7 +237,7 @@
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts": 1781536765.5084789,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts": 1781536765.5084789,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-material.ts": 1781510846.4290047,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-material.ts": 1781510846.4290047,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/workflow.ts": 1781272556.6579714,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/workflow.ts": 1781272556.6579714,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts": 1781583227.52806,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-work-order.ts": 1781696883.6649792,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts": 1781272556.6589992,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts": 1781272556.6589992,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/protocol-sample-collection.ts": 1781272556.659353,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/protocol-sample-collection.ts": 1781272556.659353,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material-usage.ts": 1781517319.838243,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material-usage.ts": 1781517319.838243,
@@ -266,7 +266,7 @@
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material.ts": 1781346022.817811,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material.ts": 1781346022.817811,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-reception.ts": 1781496419.5874305,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-reception.ts": 1781496419.5874305,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts": 1781272556.6719868,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts": 1781272556.6719868,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1781603236.5340948,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1781698779.261544,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/auth/login/index.vue": 1781272556.6735966,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/auth/login/index.vue": 1781272556.6735966,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/refund-confirm/index.vue": 1781272556.6742516,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/refund-confirm/index.vue": 1781272556.6742516,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/my-material-usage/index.vue": 1781518736.690082,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/my-material-usage/index.vue": 1781518736.690082,
@@ -365,7 +365,7 @@
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskPaperAttachmentsPanel.vue": 1781530960.805927,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskPaperAttachmentsPanel.vue": 1781530960.805927,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue": 1781585388.7005522,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/WorkOrderTaskSubList.vue": 1781585388.7005522,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue": 1781605192.0421948,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue": 1781605192.0421948,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue": 1781601931.1752002,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue": 1781698245.2960405,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ForwardTaskDialog.vue": 1781563767.3550124,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ForwardTaskDialog.vue": 1781563767.3550124,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/OutboundPilotTab.vue": 1781583381.1088736,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/OutboundPilotTab.vue": 1781583381.1088736,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/ProductionWorkOrderTabPanel.vue": 1781583376.390064,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/tabs/ProductionWorkOrderTabPanel.vue": 1781583376.390064,
@@ -406,9 +406,9 @@
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/index/index.vue": 1781272556.8548205,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/index/index.vue": 1781272556.8548205,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1781592807.1040754,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1781592807.1040754,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskResultPanel.vue": 1781589482.0890527,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskResultPanel.vue": 1781589482.0890527,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue": 1781607988.3595095,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue": 1781698817.570356,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderFeeSummaryBar.vue": 1781585388.7152023,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderFeeSummaryBar.vue": 1781585388.7152023,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSampleGroup.vue": 1781601939.108729,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSampleGroup.vue": 1781691146.652663,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskSubList.vue": 1781590356.5447705,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskSubList.vue": 1781590356.5447705,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderCardList.vue": 1781595924.5325,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderCardList.vue": 1781595924.5325,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue": 1781588255.9076052,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue": 1781588255.9076052,
@@ -488,7 +488,7 @@
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/PilotApplicationTab.vue": 1781272557.1928792,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/PilotApplicationTab.vue": 1781272557.1928792,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue": 1781682542.890803,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue": 1781682542.890803,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/InfusionScheduleTab.vue": 1781600431.8481567,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/InfusionScheduleTab.vue": 1781600431.8481567,
-  "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationFormDialog.vue": 1781682540.789256,
+  "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationFormDialog.vue": 1781691994.6323533,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/CheckupManagementTab.vue": 1781435201.7785301,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/CheckupManagementTab.vue": 1781435201.7785301,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ClinicalPaymentTab.vue": 1781601413.5946639,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ClinicalPaymentTab.vue": 1781601413.5946639,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/cell-product-price/index.vue": 1781272557.4568446,
   "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/cell-product-price/index.vue": 1781272557.4568446,

+ 6 - 0
web-admin/src/api/production-work-order.ts

@@ -43,6 +43,12 @@ export interface ProductionWorkOrderItem {
   projectTitle?: string
   projectTitle?: string
   /** 基础实验出库:客户名称(列表 enrich) */
   /** 基础实验出库:客户名称(列表 enrich) */
   customerName?: string
   customerName?: string
+  /** 出库工单:输注计划细胞类型(列表 enrich,无产品明细时兜底) */
+  cellType?: string
+  /** 出库工单:输注计划规格(列表 enrich,无产品明细时兜底) */
+  specText?: string
+  /** 出库工单:输注计划关联细胞产品明细(列表 enrich) */
+  products?: OutboundQcDetailProduct[]
   paymentSummary?: QcProtocolPaymentSummary | null
   paymentSummary?: QcProtocolPaymentSummary | null
   feeSummary?: QcBasicExperimentFeeSummary | null
   feeSummary?: QcBasicExperimentFeeSummary | null
 }
 }

+ 6 - 0
web-admin/src/api/qc-work-order.ts

@@ -127,6 +127,12 @@ export type QcWorkOrder = {
   /** 出库工单:输注计划总次数 */
   /** 出库工单:输注计划总次数 */
   totalCourseCount?: number | null
   totalCourseCount?: number | null
   infusionTime?: string
   infusionTime?: string
+  /** 出库工单:输注计划细胞类型(列表 enrich,无产品明细时兜底) */
+  cellType?: string
+  /** 出库工单:输注计划规格(列表 enrich,无产品明细时兜底) */
+  specText?: string
+  /** 计划质检:输注计划关联细胞产品明细(列表 enrich) */
+  products?: OutboundQcDetailProduct[]
   triggerSource?: string
   triggerSource?: string
   triggerSourceLabel?: string
   triggerSourceLabel?: string
   productionWorkOrderId?: string
   productionWorkOrderId?: string

+ 1 - 0
web-admin/src/graphify-out/cache/ast/07468df71a20f2c75ba9ecba1b5bca2a83bf63b875c6e68b99471d2613b64293.json

@@ -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
web-admin/src/graphify-out/cache/ast/4506079098039c10a6e677f125472c7d3a685a0e496e98cd3c05dfa5bb835571.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkordersinglecard_vue", "label": "QcWorkOrderSingleCard.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
web-admin/src/graphify-out/cache/ast/5fb0f30d87bbb940863761ce01607887a6afe05edbf636d2737a59eb342f0e0f.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_productionworkordercardlist_vue", "label": "ProductionWorkOrderCardList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

+ 1 - 0
web-admin/src/graphify-out/cache/ast/686495c44689c783c66328617a5cb322c6aa79a8e840ca290342c5f1bf85359d.json

@@ -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": []}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
web-admin/src/graphify-out/cache/ast/70726652cbfe3dbe97bded90eebec9252542e32db5aa36528c947c84c4130e80.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
web-admin/src/graphify-out/cache/ast/85094e0d7ef8fc73ef477c4d44b0a8e52d14f8ac244d19055575a0806416f936.json


+ 1 - 0
web-admin/src/graphify-out/cache/ast/8569ccf4780741f68bc4f973b51e8228576837ee8c990396b4c28749c2a1049e.json

@@ -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": []}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
web-admin/src/graphify-out/cache/ast/8c12c9f17b5c67ae2fca703244f83a62ee956584c022393c68c2ac94170bb69c.json


+ 1 - 0
web-admin/src/graphify-out/cache/ast/a9bcfc991213e67bd27ede64d5fb16d000c64125160d3bd28fee64ab987a9853.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_productionworkordercardlist_vue", "label": "ProductionWorkOrderCardList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
web-admin/src/graphify-out/cache/ast/b5a7ddf14e286e97ad6202f461aabb2c4eda32e426efdcee6e2be243505431e4.json


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
web-admin/src/graphify-out/cache/ast/df70c7fe1152f96f10eccfa304bbd477f0dabdb25d41a377f622eddb772aadb2.json


+ 1 - 0
web-admin/src/graphify-out/cache/ast/e37e8fbc54360e71a9e407b4886f3b80267bbcd78929c4f949d7b4962af8acc4.json

@@ -0,0 +1 @@
+{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkordersamplegroup_vue", "label": "QcWorkOrderSampleGroup.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSampleGroup.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
web-admin/src/graphify-out/cache/ast/f7f50431271417e71aa52d0168f65dfc871f5100323ae337a66ab13435c3867b.json


+ 8 - 0
web-admin/src/utils/production-work-order-card-meta.ts

@@ -25,6 +25,8 @@ export type ProductionWorkOrderCardMetaConfig = {
   tabKey: ProductionWorkOrderTabKey
   tabKey: ProductionWorkOrderTabKey
   showProtocolFeeSummary: boolean
   showProtocolFeeSummary: boolean
   showOutboundFeeSummary: boolean
   showOutboundFeeSummary: boolean
+  /** 卡片展示关联申请单/输注计划的细胞产品明细 */
+  showCellProducts: boolean
   metaFields: CardMetaField[]
   metaFields: CardMetaField[]
   allowDispatchQc: boolean
   allowDispatchQc: boolean
   allowSampleReception: boolean
   allowSampleReception: boolean
@@ -181,6 +183,7 @@ const CARD_META_BY_TAB: Record<ProductionWorkOrderTabKey, ProductionWorkOrderCar
     tabKey: 'inbound_cell_storage',
     tabKey: 'inbound_cell_storage',
     showProtocolFeeSummary: true,
     showProtocolFeeSummary: true,
     showOutboundFeeSummary: false,
     showOutboundFeeSummary: false,
+    showCellProducts: false,
     metaFields: buildInboundMetaFields('inbound_cell_storage'),
     metaFields: buildInboundMetaFields('inbound_cell_storage'),
     allowDispatchQc: true,
     allowDispatchQc: true,
     allowSampleReception: true,
     allowSampleReception: true,
@@ -192,6 +195,7 @@ const CARD_META_BY_TAB: Record<ProductionWorkOrderTabKey, ProductionWorkOrderCar
     tabKey: 'inbound_research_donation',
     tabKey: 'inbound_research_donation',
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: false,
     showOutboundFeeSummary: false,
+    showCellProducts: false,
     metaFields: buildInboundMetaFields('inbound_research_donation'),
     metaFields: buildInboundMetaFields('inbound_research_donation'),
     allowDispatchQc: true,
     allowDispatchQc: true,
     allowSampleReception: true,
     allowSampleReception: true,
@@ -203,6 +207,7 @@ const CARD_META_BY_TAB: Record<ProductionWorkOrderTabKey, ProductionWorkOrderCar
     tabKey: 'outbound_pilot_service',
     tabKey: 'outbound_pilot_service',
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: false,
     showOutboundFeeSummary: false,
+    showCellProducts: true,
     metaFields: outboundPilotClinicalFields,
     metaFields: outboundPilotClinicalFields,
     allowDispatchQc: true,
     allowDispatchQc: true,
     allowSampleReception: false,
     allowSampleReception: false,
@@ -214,6 +219,7 @@ const CARD_META_BY_TAB: Record<ProductionWorkOrderTabKey, ProductionWorkOrderCar
     tabKey: 'outbound_basic_experiment',
     tabKey: 'outbound_basic_experiment',
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: true,
     showOutboundFeeSummary: true,
+    showCellProducts: true,
     metaFields: outboundBasicExperimentFields,
     metaFields: outboundBasicExperimentFields,
     allowDispatchQc: true,
     allowDispatchQc: true,
     allowSampleReception: false,
     allowSampleReception: false,
@@ -225,6 +231,7 @@ const CARD_META_BY_TAB: Record<ProductionWorkOrderTabKey, ProductionWorkOrderCar
     tabKey: 'outbound_clinical_application',
     tabKey: 'outbound_clinical_application',
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: true,
     showOutboundFeeSummary: true,
+    showCellProducts: false,
     metaFields: outboundPilotClinicalFields,
     metaFields: outboundPilotClinicalFields,
     allowDispatchQc: false,
     allowDispatchQc: false,
     allowSampleReception: false,
     allowSampleReception: false,
@@ -236,6 +243,7 @@ const CARD_META_BY_TAB: Record<ProductionWorkOrderTabKey, ProductionWorkOrderCar
     tabKey: 'temporary',
     tabKey: 'temporary',
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: false,
     showOutboundFeeSummary: false,
+    showCellProducts: false,
     metaFields: temporaryFields,
     metaFields: temporaryFields,
     allowDispatchQc: true,
     allowDispatchQc: true,
     allowSampleReception: false,
     allowSampleReception: false,

+ 8 - 0
web-admin/src/utils/qc-work-order-card-meta.ts

@@ -33,6 +33,8 @@ export type QcWorkOrderCardMetaConfig = {
   showTriggerTag: boolean
   showTriggerTag: boolean
   showProtocolFeeSummary: boolean
   showProtocolFeeSummary: boolean
   showOutboundFeeSummary: boolean
   showOutboundFeeSummary: boolean
+  /** 卡片展示计划质检订单细胞产品 / 生产过程质检细胞产品 */
+  showCellProducts: boolean
   metaFields: CardMetaField[]
   metaFields: CardMetaField[]
   buildGroupContextParts: (row: QcWorkOrder) => string[]
   buildGroupContextParts: (row: QcWorkOrder) => string[]
   allowEditTemporary: boolean
   allowEditTemporary: boolean
@@ -209,6 +211,7 @@ const CARD_META_BY_TAB: Record<QcWorkOrderTabKey, QcWorkOrderCardMetaConfig> = {
     showTriggerTag: true,
     showTriggerTag: true,
     showProtocolFeeSummary: true,
     showProtocolFeeSummary: true,
     showOutboundFeeSummary: false,
     showOutboundFeeSummary: false,
+    showCellProducts: false,
     metaFields: inboundGroupedFields,
     metaFields: inboundGroupedFields,
     buildGroupContextParts: buildCellStorageGroupContext,
     buildGroupContextParts: buildCellStorageGroupContext,
     allowEditTemporary: false,
     allowEditTemporary: false,
@@ -221,6 +224,7 @@ const CARD_META_BY_TAB: Record<QcWorkOrderTabKey, QcWorkOrderCardMetaConfig> = {
     showTriggerTag: true,
     showTriggerTag: true,
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: false,
     showOutboundFeeSummary: false,
+    showCellProducts: false,
     metaFields: inboundGroupedFields,
     metaFields: inboundGroupedFields,
     buildGroupContextParts: buildDonationGroupContext,
     buildGroupContextParts: buildDonationGroupContext,
     allowEditTemporary: false,
     allowEditTemporary: false,
@@ -233,6 +237,7 @@ const CARD_META_BY_TAB: Record<QcWorkOrderTabKey, QcWorkOrderCardMetaConfig> = {
     showTriggerTag: true,
     showTriggerTag: true,
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: false,
     showOutboundFeeSummary: false,
+    showCellProducts: true,
     metaFields: outboundGroupedFields,
     metaFields: outboundGroupedFields,
     buildGroupContextParts: buildOutboundPilotGroupContext,
     buildGroupContextParts: buildOutboundPilotGroupContext,
     allowEditTemporary: false,
     allowEditTemporary: false,
@@ -245,6 +250,7 @@ const CARD_META_BY_TAB: Record<QcWorkOrderTabKey, QcWorkOrderCardMetaConfig> = {
     showTriggerTag: true,
     showTriggerTag: true,
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: true,
     showOutboundFeeSummary: true,
+    showCellProducts: true,
     metaFields: outboundGroupedFields,
     metaFields: outboundGroupedFields,
     buildGroupContextParts: buildOutboundBasicExperimentGroupContext,
     buildGroupContextParts: buildOutboundBasicExperimentGroupContext,
     allowEditTemporary: false,
     allowEditTemporary: false,
@@ -257,6 +263,7 @@ const CARD_META_BY_TAB: Record<QcWorkOrderTabKey, QcWorkOrderCardMetaConfig> = {
     showTriggerTag: false,
     showTriggerTag: false,
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: true,
     showOutboundFeeSummary: true,
+    showCellProducts: false,
     metaFields: outboundPilotClinicalFields,
     metaFields: outboundPilotClinicalFields,
     buildGroupContextParts: () => [],
     buildGroupContextParts: () => [],
     allowEditTemporary: false,
     allowEditTemporary: false,
@@ -269,6 +276,7 @@ const CARD_META_BY_TAB: Record<QcWorkOrderTabKey, QcWorkOrderCardMetaConfig> = {
     showTriggerTag: true,
     showTriggerTag: true,
     showProtocolFeeSummary: false,
     showProtocolFeeSummary: false,
     showOutboundFeeSummary: false,
     showOutboundFeeSummary: false,
+    showCellProducts: false,
     metaFields: temporaryFields,
     metaFields: temporaryFields,
     buildGroupContextParts: () => [],
     buildGroupContextParts: () => [],
     allowEditTemporary: true,
     allowEditTemporary: true,

+ 129 - 0
web-admin/src/views/pilot-service/pilot-application/ApplicationFormDialog.vue

@@ -1639,6 +1639,133 @@
     return items.length ? items.join(',') : '—'
     return items.length ? items.join(',') : '—'
   })
   })
 
 
+  /** 未完成结算的输注计划(已完成/已取消不再扣减) */
+  const isPendingSettlementSchedule = (schedule: Record<string, any>): boolean => {
+    const st = normalizeInfusionStatusCode(schedule.infusionStatus)
+    return st !== 'ywc' && st !== 'yqx'
+  }
+
+  const resolveYfkUnitPrice = (cp: Record<string, any>): number => {
+    const agreement = Number(cp.agreementPrice)
+    if (agreement > 0) return agreement
+    return Number(cp.actualPrice) || 0
+  }
+
+  const calcPendingSettlementAmount = (): number =>
+    infusionSchedules.value
+      .filter(isPendingSettlementSchedule)
+      .reduce(
+        (sum, s) =>
+          sum +
+          (s.cellProducts || []).reduce((lineSum: number, cp: any) => {
+            const qty = Math.max(1, Number(cp.quantity) || 1)
+            return lineSum + resolveYfkUnitPrice(cp) * qty
+          }, 0),
+        0
+      )
+
+  type PendingProductQtyItem = {
+    label: string
+    qty: number
+    projectCode: string
+    specText: string
+    cellTypeName: string
+  }
+
+  const calcPendingSettlementQtyByProduct = (): Map<string, PendingProductQtyItem> => {
+    const map = new Map<string, PendingProductQtyItem>()
+    for (const schedule of infusionSchedules.value.filter(isPendingSettlementSchedule)) {
+      for (const cp of schedule.cellProducts || []) {
+        const projectCode = (cp.projectCode || '').trim()
+        const specText = (cp.specText || '').trim()
+        const cellTypeName = (cp.cellTypeName || '').trim()
+        const key = projectCode ? `${projectCode}|${specText}` : `${cellTypeName}|${specText}`
+        if (!key.replace('|', '').trim()) continue
+        const qty = Math.max(1, Number(cp.quantity) || 1)
+        const label = formatCellProductDisplayLabel(cp)
+        const existing = map.get(key)
+        if (existing) {
+          existing.qty += qty
+        } else {
+          map.set(key, { label, qty, projectCode, specText, cellTypeName })
+        }
+      }
+    }
+    return map
+  }
+
+  const findOrderProductCurrentQty = (
+    orderProducts: any[],
+    item: PendingProductQtyItem
+  ): number | null => {
+    const spec = item.specText
+    if (item.projectCode) {
+      const row = orderProducts.find(
+        (p) => (p.projectCode || '') === item.projectCode && (p.specText || '') === spec
+      )
+      if (row) return Number(row.currentQty) || 0
+    }
+    if (item.cellTypeName) {
+      const row = orderProducts.find(
+        (p) =>
+          (p.cellType || p.cellTypeName || '') === item.cellTypeName && (p.specText || '') === spec
+      )
+      if (row) return Number(row.currentQty) || 0
+    }
+    return null
+  }
+
+  /** 保存前校验渠道服务订单余额/份数,仅提示不阻断保存 */
+  const warnIfChannelSettlementInsufficient = async () => {
+    if (isClinical.value || isBasicExperiment.value) return
+    if (!formData.channelServiceOrderId || !formData.channelId) return
+
+    const pendingSchedules = infusionSchedules.value.filter(isPendingSettlementSchedule)
+    const hasProducts = pendingSchedules.some((s) => (s.cellProducts || []).length > 0)
+    if (!hasProducts) return
+
+    let orderDetail: any
+    try {
+      const res: any = await fetchChannelServiceOrderDetail(formData.channelServiceOrderId)
+      orderDetail = res?.data ?? res
+    } catch {
+      return
+    }
+
+    const orderType = orderDetail?.orderType || selectedServiceOrder.value?.orderType || ''
+
+    if (orderType === 'yfk') {
+      const required = calcPendingSettlementAmount()
+      if (required <= 0) return
+      const balance = Number(orderDetail.currentBalance) || 0
+      if (balance < required) {
+        ElMessage.warning(
+          `渠道服务订单余额可能不足:当前余额 ¥${formatMoneyDisplay(balance, 2)},待完成输注计划预估需 ¥${formatMoneyDisplay(required, 2)}。申请单仍会保存,完成输注时可能扣减失败。`
+        )
+      }
+      return
+    }
+
+    if (orderType === 'gmcp') {
+      const orderProducts = Array.isArray(orderDetail.products) ? orderDetail.products : []
+      const requiredMap = calcPendingSettlementQtyByProduct()
+      const shortfalls: string[] = []
+      for (const item of requiredMap.values()) {
+        const available = findOrderProductCurrentQty(orderProducts, item)
+        if (available === null) {
+          shortfalls.push(`「${item.label}」在服务订单中未找到持仓`)
+        } else if (available < item.qty) {
+          shortfalls.push(`「${item.label}」当前剩余 ${available} 份,预估需 ${item.qty} 份`)
+        }
+      }
+      if (shortfalls.length) {
+        ElMessage.warning(
+          `渠道服务订单产品份数可能不足:${shortfalls.join(';')}。申请单仍会保存,完成输注时可能扣减失败。`
+        )
+      }
+    }
+  }
+
   const getCellProductProjectKey = (cp: any): string =>
   const getCellProductProjectKey = (cp: any): string =>
     (cp?.projectCode || cp?.priceId || '').trim()
     (cp?.projectCode || cp?.priceId || '').trim()
 
 
@@ -2742,6 +2869,8 @@
       }
       }
     }
     }
 
 
+    await warnIfChannelSettlementInsufficient()
+
     submitting.value = true
     submitting.value = true
     try {
     try {
       const attachmentPayload = otherFileList.value.map((a) => ({
       const attachmentPayload = otherFileList.value.map((a) => ({

+ 243 - 118
web-admin/src/views/production/production-work-order/ProductionWorkOrderCardList.vue

@@ -6,119 +6,172 @@
         <header class="production-work-order-card__head">
         <header class="production-work-order-card__head">
           <div class="production-work-order-card__title-band">
           <div class="production-work-order-card__title-band">
             <div class="production-work-order-card__title-row">
             <div class="production-work-order-card__title-row">
-              <el-checkbox
-                v-if="!isMineMode"
-                :model-value="isSelected(row.id)"
-                class="production-work-order-card__checkbox"
-                @change="(val) => handleCardSelect(row, Boolean(val))"
-              />
-              <span class="production-work-order-card__index">{{ cardIndex(idx) }}</span>
-              <span class="production-work-order-card__no">{{ row.workOrderNo || '—' }}</span>
-              <el-tag :type="productionWoStatusTag(row.workOrderStatus).type" size="small">
-                {{ productionWoStatusTag(row.workOrderStatus).label }}
-              </el-tag>
-            </div>
-          </div>
-          <div class="production-work-order-card__head-body">
-            <div class="production-work-order-card__head-main">
-              <dl class="production-work-order-card__meta">
-                <template v-for="field in visibleMetaFields(row)" :key="field.label">
-                  <div
-                    class="production-work-order-card__meta-item"
-                    :class="{ 'production-work-order-card__meta-item--wide': field.wide }"
-                  >
-                    <dt>{{ field.label }}</dt>
-                    <dd>
-                      <el-tag
-                        v-if="field.kind === 'tag'"
-                        :type="field.getTagType?.(row) || 'info'"
-                        size="small"
-                      >
-                        {{ field.getValue(row) }}
-                      </el-tag>
-                      <template v-else>{{ field.getValue(row) }}</template>
-                    </dd>
-                  </div>
-                </template>
-              </dl>
-            </div>
-            <div v-if="showCardOpsRow" class="production-work-order-card__ops">
-            <el-button
-              v-if="isMineMode"
-              class="btn-op-item primary"
-              type="primary"
-              @click="emit('view', row)"
-            >
-              <el-icon><View /></el-icon>查看
-            </el-button>
-            <el-button
-              v-else-if="showCardDispatchComplete && canDispatchTask(row)"
-              class="btn-op-item primary"
-              type="primary"
-              @click="emit('dispatch', row)"
-            >
-              <el-icon><Position /></el-icon>派发任务
-            </el-button>
-            <el-button
-              v-else-if="!canDispatchTask(row)"
-              class="btn-op-item primary"
-              @click="emit('view', row)"
-            >
-              <el-icon><View /></el-icon>查看
-            </el-button>
-            <el-tooltip
-              v-if="!isMineMode && cardMeta.allowDispatchQc"
-              :content="dispatchQcTooltip(row)"
-              :disabled="canDispatchQc(row)"
-              placement="top"
-            >
-              <span>
+              <div class="production-work-order-card__title-left">
+                <el-checkbox
+                  v-if="!isMineMode"
+                  :model-value="isSelected(row.id)"
+                  class="production-work-order-card__checkbox"
+                  @change="(val) => handleCardSelect(row, Boolean(val))"
+                />
+                <span class="production-work-order-card__index">{{ cardIndex(idx) }}</span>
+                <span class="production-work-order-card__no">{{ row.workOrderNo || '—' }}</span>
+                <el-tag :type="productionWoStatusTag(row.workOrderStatus).type" size="small">
+                  {{ productionWoStatusTag(row.workOrderStatus).label }}
+                </el-tag>
+              </div>
+              <div v-if="showCardOpsRow" class="production-work-order-card__ops">
+                <el-button
+                  v-if="isMineMode"
+                  class="btn-op-item primary"
+                  type="primary"
+                  @click="emit('view', row)"
+                >
+                  <el-icon><View /></el-icon>查看
+                </el-button>
+                <el-button
+                  v-else-if="showCardDispatchComplete && canDispatchTask(row)"
+                  class="btn-op-item primary"
+                  type="primary"
+                  @click="emit('dispatch', row)"
+                >
+                  <el-icon><Position /></el-icon>派发任务
+                </el-button>
+                <el-button
+                  v-else-if="!canDispatchTask(row)"
+                  class="btn-op-item primary"
+                  @click="emit('view', row)"
+                >
+                  <el-icon><View /></el-icon>查看
+                </el-button>
+                <el-tooltip
+                  v-if="!isMineMode && cardMeta.allowDispatchQc"
+                  :content="dispatchQcTooltip(row)"
+                  :disabled="canDispatchQc(row)"
+                  placement="top"
+                >
+                  <span>
+                    <el-button
+                      class="btn-op-item"
+                      :disabled="!canDispatchQc(row)"
+                      @click="emit('dispatch-qc', row)"
+                    >
+                      <el-icon><Document /></el-icon>派发质检工单
+                    </el-button>
+                  </span>
+                </el-tooltip>
                 <el-button
                 <el-button
+                  v-if="cardMeta.allowSampleReception"
                   class="btn-op-item"
                   class="btn-op-item"
-                  :disabled="!canDispatchQc(row)"
-                  @click="emit('dispatch-qc', row)"
+                  @click="emit('open-sample-reception', row)"
                 >
                 >
-                  <el-icon><Document /></el-icon>派发质检工单
+                  <el-icon><EditPen /></el-icon>样本接收信息填写
                 </el-button>
                 </el-button>
-              </span>
-            </el-tooltip>
-            <el-button
-              v-if="cardMeta.allowSampleReception"
-              class="btn-op-item"
-              @click="emit('open-sample-reception', row)"
-            >
-              <el-icon><EditPen /></el-icon>样本接收信息填写
-            </el-button>
-            <el-button
-              v-if="!isMineMode && showCardDispatchComplete && canComplete(row)"
-              class="btn-op-item"
-              @click="emit('complete', row)"
-            >
-              <el-icon><Check /></el-icon>完成工单
-            </el-button>
-            <el-dropdown
-              v-if="!isMineMode && hasMoreActions(row)"
-              trigger="hover"
-              @command="(cmd) => emit('command', cmd, row)"
-            >
-              <el-button class="btn-op-item">
-                <el-icon><More /></el-icon>
-              </el-button>
-              <template #dropdown>
-                <el-dropdown-menu>
-                  <el-dropdown-item v-if="canDispatchTask(row)" command="view">
-                    <el-icon><View /></el-icon>查看
-                  </el-dropdown-item>
-                  <el-dropdown-item v-if="canEditWorkOrder(row)" command="edit">
-                    <el-icon><EditPen /></el-icon>编辑
-                  </el-dropdown-item>
-                  <el-dropdown-item v-if="canDeleteWorkOrder(row)" command="delete" divided>
-                    <el-icon><Delete /></el-icon>删除
-                  </el-dropdown-item>
-                </el-dropdown-menu>
-              </template>
-            </el-dropdown>
+                <el-button
+                  v-if="!isMineMode && showCardDispatchComplete && canComplete(row)"
+                  class="btn-op-item"
+                  @click="emit('complete', row)"
+                >
+                  <el-icon><Check /></el-icon>完成工单
+                </el-button>
+                <el-dropdown
+                  v-if="!isMineMode && hasMoreActions(row)"
+                  trigger="hover"
+                  @command="(cmd) => emit('command', cmd, row)"
+                >
+                  <el-button class="btn-op-item">
+                    <el-icon><More /></el-icon>
+                  </el-button>
+                  <template #dropdown>
+                    <el-dropdown-menu>
+                      <el-dropdown-item v-if="canDispatchTask(row)" command="view">
+                        <el-icon><View /></el-icon>查看
+                      </el-dropdown-item>
+                      <el-dropdown-item v-if="canEditWorkOrder(row)" command="edit">
+                        <el-icon><EditPen /></el-icon>编辑
+                      </el-dropdown-item>
+                      <el-dropdown-item v-if="canDeleteWorkOrder(row)" command="delete" divided>
+                        <el-icon><Delete /></el-icon>删除
+                      </el-dropdown-item>
+                    </el-dropdown-menu>
+                  </template>
+                </el-dropdown>
+              </div>
+            </div>
           </div>
           </div>
+          <div class="production-work-order-card__head-body">
+            <dl class="production-work-order-card__meta">
+              <template v-for="field in visibleMetaFields(row)" :key="field.label">
+                <div
+                  class="production-work-order-card__meta-item"
+                  :class="{ 'production-work-order-card__meta-item--wide': field.wide }"
+                >
+                  <dt>{{ field.label }}</dt>
+                  <dd>
+                    <el-tag
+                      v-if="field.kind === 'tag'"
+                      :type="field.getTagType?.(row) || 'info'"
+                      size="small"
+                    >
+                      {{ field.getValue(row) }}
+                    </el-tag>
+                    <template v-else>{{ field.getValue(row) }}</template>
+                  </dd>
+                </div>
+              </template>
+            </dl>
+            <section v-if="cardMeta.showCellProducts" class="production-work-order-card__cells">
+              <div v-if="cellProducts(row).length" class="production-work-order-card__cell-list">
+                <div
+                  v-for="(product, pi) in cellProducts(row)"
+                  :key="`${product.projectCode || product.projectName || pi}`"
+                  class="production-work-order-card__cell-item"
+                >
+                  <dl class="production-work-order-card__cell-fields">
+                    <div class="production-work-order-card__cell-field">
+                      <dt>项目编码</dt>
+                      <dd>{{ product.projectCode || '—' }}</dd>
+                    </div>
+                    <div class="production-work-order-card__cell-field">
+                      <dt>项目名称</dt>
+                      <dd>{{ product.projectName || '—' }}</dd>
+                    </div>
+                    <div class="production-work-order-card__cell-field">
+                      <dt>细胞类型</dt>
+                      <dd>{{ product.cellTypeName || row.cellType || '—' }}</dd>
+                    </div>
+                    <div class="production-work-order-card__cell-field">
+                      <dt>规格</dt>
+                      <dd>{{ product.specText || row.specText || '—' }}</dd>
+                    </div>
+                    <div class="production-work-order-card__cell-field">
+                      <dt>数量</dt>
+                      <dd>{{ product.quantity != null ? product.quantity : '—' }}</dd>
+                    </div>
+                    <div class="production-work-order-card__cell-field">
+                      <dt>产品状态</dt>
+                      <dd>
+                        <el-tag
+                          v-if="product.productStatus"
+                          :type="productStatusTagType(product.productStatus) as TagType"
+                          size="small"
+                        >
+                          {{ productStatusLabel(product.productStatus, row.bizType || '') }}
+                        </el-tag>
+                        <span v-else>—</span>
+                      </dd>
+                    </div>
+                  </dl>
+                </div>
+              </div>
+              <p v-else class="production-work-order-card__cells-fallback">
+                <template v-if="row.cellType || row.specText">
+                  <span v-if="row.cellType">细胞类型:{{ row.cellType }}</span>
+                  <span v-if="row.cellType && row.specText"> · </span>
+                  <span v-if="row.specText">规格:{{ row.specText }}</span>
+                </template>
+                <template v-else>暂无细胞产品明细</template>
+              </p>
+            </section>
           </div>
           </div>
         </header>
         </header>
         <QcWorkOrderProtocolPaymentPanel
         <QcWorkOrderProtocolPaymentPanel
@@ -168,9 +221,11 @@
   import { computed } from 'vue'
   import { computed } from 'vue'
   import { View, Position, Document, Check, More, EditPen, Delete } from '@element-plus/icons-vue'
   import { View, Position, Document, Check, More, EditPen, Delete } from '@element-plus/icons-vue'
   import type { ProductionWorkOrderItem } from '@/api/production-work-order'
   import type { ProductionWorkOrderItem } from '@/api/production-work-order'
+  import type { OutboundQcDetailProduct } from '@/api/qc-work-order'
   import type { ProductionWorkOrderCardMetaConfig } from '@/utils/production-work-order-card-meta'
   import type { ProductionWorkOrderCardMetaConfig } from '@/utils/production-work-order-card-meta'
   import { getCardMetaConfigForLegacyTab } from '@/utils/production-work-order-card-meta'
   import { getCardMetaConfigForLegacyTab } from '@/utils/production-work-order-card-meta'
   import type { TagType } from '@/utils/status-tag-registry'
   import type { TagType } from '@/utils/status-tag-registry'
+  import { productStatusLabel, productStatusTagType } from '@/utils/status-tag-registry'
   import WorkOrderTaskSubList from './WorkOrderTaskSubList.vue'
   import WorkOrderTaskSubList from './WorkOrderTaskSubList.vue'
   import QcWorkOrderFeeSummaryBar from '@/views/quality-inspection/qc-work-order/QcWorkOrderFeeSummaryBar.vue'
   import QcWorkOrderFeeSummaryBar from '@/views/quality-inspection/qc-work-order/QcWorkOrderFeeSummaryBar.vue'
   import QcWorkOrderProtocolPaymentPanel from '@/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue'
   import QcWorkOrderProtocolPaymentPanel from '@/views/quality-inspection/qc-work-order/QcWorkOrderProtocolPaymentPanel.vue'
@@ -238,6 +293,10 @@
     return cardMeta.value.showOutboundFeeSummary && !!row.feeSummary
     return cardMeta.value.showOutboundFeeSummary && !!row.feeSummary
   }
   }
 
 
+  function cellProducts(row: ProductionWorkOrderItem): OutboundQcDetailProduct[] {
+    return row.products?.length ? row.products : []
+  }
+
   function productionWoStatusTag(status: string): { label: string; type: TagType } {
   function productionWoStatusTag(status: string): { label: string; type: TagType } {
     const map: Record<string, { label: string; type: TagType }> = {
     const map: Record<string, { label: string; type: TagType }> = {
       dpz: { label: '待配置', type: 'info' },
       dpz: { label: '待配置', type: 'info' },
@@ -351,23 +410,27 @@
 
 
   .production-work-order-card__head-body {
   .production-work-order-card__head-body {
     display: flex;
     display: flex;
-    flex-wrap: wrap;
-    align-items: flex-start;
-    gap: 12px 20px;
-    padding: 14px 16px;
+    flex-direction: column;
+    gap: 12px;
+    padding: 12px 16px;
     border-bottom: 1px solid #e2e8f0;
     border-bottom: 1px solid #e2e8f0;
   }
   }
 
 
-  .production-work-order-card__head-main {
-    flex: 1;
-    min-width: 280px;
+  .production-work-order-card__title-row {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    justify-content: space-between;
+    gap: 8px 12px;
   }
   }
 
 
-  .production-work-order-card__title-row {
+  .production-work-order-card__title-left {
     display: flex;
     display: flex;
     flex-wrap: wrap;
     flex-wrap: wrap;
     align-items: center;
     align-items: center;
     gap: 8px;
     gap: 8px;
+    flex: 1;
+    min-width: 0;
   }
   }
 
 
   .production-work-order-card__checkbox {
   .production-work-order-card__checkbox {
@@ -423,14 +486,76 @@
     }
     }
   }
   }
 
 
+  .production-work-order-card__cells {
+    padding: 0;
+  }
+
+  .production-work-order-card__cell-list {
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+  }
+
+  .production-work-order-card__cell-item {
+    &:not(:last-child) {
+      margin-bottom: 8px;
+    }
+  }
+
+  .production-work-order-card__cell-fields {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 10px 12px;
+    margin: 0;
+  }
+
+  .production-work-order-card__cell-field {
+    display: inline-flex;
+    align-items: center;
+    gap: 6px;
+    min-width: 0;
+    max-width: 100%;
+
+    dt {
+      flex-shrink: 0;
+      margin: 0;
+      font-size: 11px;
+      font-weight: 500;
+      color: #b91c1c;
+      line-height: 1.4;
+      white-space: nowrap;
+
+      &::after {
+        content: ':';
+      }
+    }
+
+    dd {
+      margin: 0;
+      font-size: 13px;
+      font-weight: 600;
+      color: #dc2626;
+      line-height: 1.4;
+      word-break: break-word;
+    }
+  }
+
+  .production-work-order-card__cells-fallback {
+    margin: 0;
+    font-size: 13px;
+    color: #dc2626;
+    line-height: 1.5;
+  }
+
   .production-work-order-card__ops {
   .production-work-order-card__ops {
     display: flex;
     display: flex;
     flex-wrap: wrap;
     flex-wrap: wrap;
     align-items: center;
     align-items: center;
     justify-content: flex-end;
     justify-content: flex-end;
-    gap: 8px;
+    gap: 6px;
     flex-shrink: 0;
     flex-shrink: 0;
-    align-self: flex-end;
+    margin-left: auto;
   }
   }
 
 
   :deep(.production-work-order-card__fee-in-head) {
   :deep(.production-work-order-card__fee-in-head) {

+ 164 - 1
web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSingleCard.vue

@@ -42,6 +42,76 @@
               </div>
               </div>
             </template>
             </template>
           </dl>
           </dl>
+          <section
+            v-if="showCellProductSection(row)"
+            class="qc-work-order-card__cells"
+          >
+            <template v-if="isScheduleTrigger(row)">
+              <div v-if="scheduleCellProducts(row).length" class="qc-work-order-card__cell-list">
+                <div
+                  v-for="(product, pi) in scheduleCellProducts(row)"
+                  :key="`${product.projectCode || product.projectName || pi}`"
+                  class="qc-work-order-card__cell-item"
+                >
+                  <dl class="qc-work-order-card__cell-fields">
+                    <div class="qc-work-order-card__cell-field">
+                      <dt>项目编码</dt>
+                      <dd>{{ product.projectCode || '—' }}</dd>
+                    </div>
+                    <div class="qc-work-order-card__cell-field">
+                      <dt>项目名称</dt>
+                      <dd>{{ product.projectName || '—' }}</dd>
+                    </div>
+                    <div class="qc-work-order-card__cell-field">
+                      <dt>细胞类型</dt>
+                      <dd>{{ product.cellTypeName || row.cellType || '—' }}</dd>
+                    </div>
+                    <div class="qc-work-order-card__cell-field">
+                      <dt>规格</dt>
+                      <dd>{{ product.specText || row.specText || '—' }}</dd>
+                    </div>
+                    <div class="qc-work-order-card__cell-field">
+                      <dt>数量</dt>
+                      <dd>{{ product.quantity != null ? product.quantity : '—' }}</dd>
+                    </div>
+                    <div class="qc-work-order-card__cell-field">
+                      <dt>产品状态</dt>
+                      <dd>
+                        <el-tag
+                          v-if="product.productStatus"
+                          :type="productStatusTagType(product.productStatus) as TagType"
+                          size="small"
+                        >
+                          {{ productStatusLabel(product.productStatus, row.bizType || '') }}
+                        </el-tag>
+                        <span v-else>—</span>
+                      </dd>
+                    </div>
+                  </dl>
+                </div>
+              </div>
+              <p v-else class="qc-work-order-card__cells-fallback">
+                <template v-if="row.cellType || row.specText">
+                  <span v-if="row.cellType">细胞类型:{{ row.cellType }}</span>
+                  <span v-if="row.cellType && row.specText"> · </span>
+                  <span v-if="row.specText">规格:{{ row.specText }}</span>
+                </template>
+                <template v-else>暂无细胞产品明细</template>
+              </p>
+            </template>
+            <template v-else-if="isProductionTrigger(row)">
+              <dl class="qc-work-order-card__cell-fields">
+                <div
+                  v-for="field in productionCellProductFields(row)"
+                  :key="field.label"
+                  class="qc-work-order-card__cell-field"
+                >
+                  <dt>{{ field.label }}</dt>
+                  <dd>{{ field.value }}</dd>
+                </div>
+              </dl>
+            </template>
+          </section>
         </div>
         </div>
         <div
         <div
           v-if="showWorkOrderOps || showWorkOrderViewOnly"
           v-if="showWorkOrderOps || showWorkOrderViewOnly"
@@ -122,10 +192,11 @@
 <script setup lang="ts">
 <script setup lang="ts">
   import { computed } from 'vue'
   import { computed } from 'vue'
   import { Setting, View, More, EditPen, Delete } from '@element-plus/icons-vue'
   import { Setting, View, More, EditPen, Delete } from '@element-plus/icons-vue'
-  import type { QcWorkOrder } from '@/api/qc-work-order'
+  import type { QcWorkOrder, OutboundQcDetailProduct } from '@/api/qc-work-order'
   import type { QcTask } from '@/api/qc-task'
   import type { QcTask } from '@/api/qc-task'
   import { workOrderStatusLabel } from '@/utils/qc-labels'
   import { workOrderStatusLabel } from '@/utils/qc-labels'
   import type { TagType } from '@/utils/status-tag-registry'
   import type { TagType } from '@/utils/status-tag-registry'
+  import { productStatusLabel, productStatusTagType } from '@/utils/status-tag-registry'
   import type { QcWorkOrderCardMetaConfig } from '@/utils/qc-work-order-card-meta'
   import type { QcWorkOrderCardMetaConfig } from '@/utils/qc-work-order-card-meta'
   import { triggerSourceTabLabel, temporaryQcSourceLabel, temporaryQcSourceTagType } from '@/utils/qc-work-order-groups'
   import { triggerSourceTabLabel, temporaryQcSourceLabel, temporaryQcSourceTagType } from '@/utils/qc-work-order-groups'
   import QcWorkOrderTaskSubList from './QcWorkOrderTaskSubList.vue'
   import QcWorkOrderTaskSubList from './QcWorkOrderTaskSubList.vue'
@@ -181,6 +252,35 @@
     return 'success'
     return 'success'
   }
   }
 
 
+  function showCellProductSection(row: QcWorkOrder) {
+    if (!props.cardMetaConfig.showCellProducts) return false
+    const ts = row.triggerSource || ''
+    return ts === 'schedule' || ts === 'production'
+  }
+
+  function isScheduleTrigger(row: QcWorkOrder) {
+    return (row.triggerSource || '') === 'schedule'
+  }
+
+  function isProductionTrigger(row: QcWorkOrder) {
+    return row.triggerSource === 'production'
+  }
+
+  function scheduleCellProducts(row: QcWorkOrder): OutboundQcDetailProduct[] {
+    return row.products?.length ? row.products : []
+  }
+
+  function productionCellProductFields(row: QcWorkOrder) {
+    const val = (v?: string | null) => String(v || '').trim() || '—'
+    return [
+      { label: '产品编码', value: val(row.cellProductCode) },
+      { label: '产品名称', value: val(row.cellProductName) },
+      { label: '产品类型', value: val(row.cellProductTypeName) },
+      { label: '代次', value: val(row.cellProductPassage) },
+      { label: '规格', value: val(row.cellProductSpecName) }
+    ]
+  }
+
   function resolveTriggerTagLabel(row: QcWorkOrder) {
   function resolveTriggerTagLabel(row: QcWorkOrder) {
     if (props.cardMetaConfig.tabKey === 'temporary') {
     if (props.cardMetaConfig.tabKey === 'temporary') {
       return temporaryQcSourceLabel(row)
       return temporaryQcSourceLabel(row)
@@ -325,6 +425,69 @@
     }
     }
   }
   }
 
 
+  .qc-work-order-card__cells {
+    margin-top: 10px;
+    padding: 0;
+  }
+
+  .qc-work-order-card__cell-list {
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+  }
+
+  .qc-work-order-card__cell-item {
+    &:not(:last-child) {
+      margin-bottom: 8px;
+    }
+  }
+
+  .qc-work-order-card__cell-fields {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 10px 12px;
+    margin: 0;
+  }
+
+  .qc-work-order-card__cell-field {
+    display: inline-flex;
+    align-items: center;
+    gap: 6px;
+    min-width: 0;
+    max-width: 100%;
+
+    dt {
+      flex-shrink: 0;
+      margin: 0;
+      font-size: 11px;
+      font-weight: 500;
+      color: #b91c1c;
+      line-height: 1.4;
+      white-space: nowrap;
+
+      &::after {
+        content: ':';
+      }
+    }
+
+    dd {
+      margin: 0;
+      font-size: 13px;
+      font-weight: 600;
+      color: #dc2626;
+      line-height: 1.4;
+      word-break: break-word;
+    }
+  }
+
+  .qc-work-order-card__cells-fallback {
+    margin: 0;
+    font-size: 13px;
+    color: #dc2626;
+    line-height: 1.5;
+  }
+
   :deep(.qc-work-order-card__fee-in-head) {
   :deep(.qc-work-order-card__fee-in-head) {
     align-self: stretch;
     align-self: stretch;
     width: calc(100% + 32px);
     width: calc(100% + 32px);

Некоторые файлы не были показаны из-за большого количества измененных файлов