提交
This commit is contained in:
1 parent
53d3b21f57
commit
1f5de9030e
54 files changed
+10954
-4322
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -6893,21 +6893,21 @@
|
||||
"label": "fetchQcWorkOrderList()",
|
||||
"file_type": "code",
|
||||
"source_file": "api/qc-work-order.ts",
|
||||
"source_location": "L89"
|
||||
"source_location": "L104"
|
||||
},
|
||||
{
|
||||
"id": "api_qc_work_order_fetchqcworkorderdetail",
|
||||
"label": "fetchQcWorkOrderDetail()",
|
||||
"file_type": "code",
|
||||
"source_file": "api/qc-work-order.ts",
|
||||
"source_location": "L101"
|
||||
"source_location": "L116"
|
||||
},
|
||||
{
|
||||
"id": "api_qc_work_order_fetchconfigureqcworkorder",
|
||||
"label": "fetchConfigureQcWorkOrder()",
|
||||
"file_type": "code",
|
||||
"source_file": "api/qc-work-order.ts",
|
||||
"source_location": "L107"
|
||||
"source_location": "L122"
|
||||
},
|
||||
{
|
||||
"id": "views_auth_login_index_vue",
|
||||
@@ -8300,21 +8300,21 @@
|
||||
"label": "if()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L1055"
|
||||
"source_location": "L1068"
|
||||
},
|
||||
{
|
||||
"id": "process_route_index_get",
|
||||
"label": "get()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L1256"
|
||||
"source_location": "L1269"
|
||||
},
|
||||
{
|
||||
"id": "process_route_index_set",
|
||||
"label": "set()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L1266"
|
||||
"source_location": "L1279"
|
||||
},
|
||||
{
|
||||
"id": "views_production_sop_index_vue",
|
||||
@@ -8393,6 +8393,13 @@
|
||||
"source_file": "views/production/production-work-order/StepDetailPanel.vue",
|
||||
"source_location": "L1"
|
||||
},
|
||||
{
|
||||
"id": "production_work_order_stepdetailpanel_for",
|
||||
"label": "for()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/production/production-work-order/StepDetailPanel.vue",
|
||||
"source_location": "L362"
|
||||
},
|
||||
{
|
||||
"id": "views_production_production_work_order_taskexecutiondialog_vue",
|
||||
"label": "TaskExecutionDialog.vue",
|
||||
@@ -8414,6 +8421,13 @@
|
||||
"source_file": "views/production/production-work-order/DispatchTaskDialog.vue",
|
||||
"source_location": "L203"
|
||||
},
|
||||
{
|
||||
"id": "views_production_production_work_order_taskoutputproductdialog_vue",
|
||||
"label": "TaskOutputProductDialog.vue",
|
||||
"file_type": "code",
|
||||
"source_file": "views/production/production-work-order/TaskOutputProductDialog.vue",
|
||||
"source_location": "L1"
|
||||
},
|
||||
{
|
||||
"id": "views_production_consumable_pack_index_vue",
|
||||
"label": "index.vue",
|
||||
@@ -8456,6 +8470,83 @@
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L1"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_handlereceptionattachmentpreview",
|
||||
"label": "handleReceptionAttachmentPreview()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L278"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_handlereceptionattachmentdownload",
|
||||
"label": "handleReceptionAttachmentDownload()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L283"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_taskattachmentfullurl",
|
||||
"label": "taskAttachmentFullUrl()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L287"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_handletaskattachmentpreview",
|
||||
"label": "handleTaskAttachmentPreview()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L294"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_handletaskattachmentdownload",
|
||||
"label": "handleTaskAttachmentDownload()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L299"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_formatconfiguredlabitems",
|
||||
"label": "formatConfiguredLabItems()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L315"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_formatconfiguredthirditems",
|
||||
"label": "formatConfiguredThirdItems()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L321"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_buildtasktypesections",
|
||||
"label": "buildTaskTypeSections()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L329"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_buildconfiguredonlysection",
|
||||
"label": "buildConfiguredOnlySection()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L388"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_formatdetectmethods",
|
||||
"label": "formatDetectMethods()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L406"
|
||||
},
|
||||
{
|
||||
"id": "qc_work_order_index_formatmetric",
|
||||
"label": "formatMetric()",
|
||||
"file_type": "code",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L413"
|
||||
},
|
||||
{
|
||||
"id": "views_quality_inspection_sample_reception_check_item_index_vue",
|
||||
"label": "index.vue",
|
||||
@@ -21269,7 +21360,7 @@
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "api/qc-work-order.ts",
|
||||
"source_location": "L89",
|
||||
"source_location": "L104",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -21278,7 +21369,7 @@
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "api/qc-work-order.ts",
|
||||
"source_location": "L101",
|
||||
"source_location": "L116",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -21287,7 +21378,7 @@
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "api/qc-work-order.ts",
|
||||
"source_location": "L107",
|
||||
"source_location": "L122",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -23846,13 +23937,40 @@
|
||||
"source_location": "L504",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_production_process_route_index_vue",
|
||||
"target": "icons_vue",
|
||||
"relation": "imports_from",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L729",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_production_process_route_index_vue",
|
||||
"target": "views_cell_storage_protocol_protocolattachmentreadonly_vue",
|
||||
"relation": "imports_from",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L730",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_production_process_route_index_vue",
|
||||
"target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_download_attachment",
|
||||
"relation": "imports_from",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L731",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_production_process_route_index_vue",
|
||||
"target": "vue_draggable_plus",
|
||||
"relation": "imports_from",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L719",
|
||||
"source_location": "L732",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -23861,7 +23979,7 @@
|
||||
"relation": "imports_from",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L720",
|
||||
"source_location": "L733",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -23870,7 +23988,7 @@
|
||||
"relation": "imports_from",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L743",
|
||||
"source_location": "L756",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -23879,7 +23997,7 @@
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L1055",
|
||||
"source_location": "L1068",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -23888,7 +24006,7 @@
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L1172",
|
||||
"source_location": "L1185",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -23897,7 +24015,7 @@
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L1256",
|
||||
"source_location": "L1269",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -23906,7 +24024,7 @@
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/process-route/index.vue",
|
||||
"source_location": "L1266",
|
||||
"source_location": "L1279",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -23990,6 +24108,15 @@
|
||||
"source_location": "L57",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_production_production_work_order_stepdetailpanel_vue",
|
||||
"target": "production_work_order_stepdetailpanel_for",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/production/production-work-order/StepDetailPanel.vue",
|
||||
"source_location": "L362",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_production_production_work_order_dispatchtaskdialog_vue",
|
||||
"target": "production_work_order_dispatchtaskdialog_if",
|
||||
@@ -23999,6 +24126,114 @@
|
||||
"source_location": "L203",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_handlereceptionattachmentpreview",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L278",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_handlereceptionattachmentdownload",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L283",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_taskattachmentfullurl",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L287",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_handletaskattachmentpreview",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L294",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_handletaskattachmentdownload",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L299",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_formatconfiguredlabitems",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L315",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_formatconfiguredthirditems",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L321",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_buildtasktypesections",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L329",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_buildconfiguredonlysection",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L388",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_formatdetectmethods",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L406",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_work_order_index_vue",
|
||||
"target": "qc_work_order_index_formatmetric",
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L413",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "qc_work_order_index_handletaskattachmentpreview",
|
||||
"target": "qc_work_order_index_taskattachmentfullurl",
|
||||
"relation": "calls",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L295",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "views_quality_inspection_qc_material_index_vue",
|
||||
"target": "element_plus",
|
||||
@@ -29514,7 +29749,7 @@
|
||||
"confidence": "INFERRED",
|
||||
"confidence_score": 0.8,
|
||||
"source_file": "api/qc-work-order.ts",
|
||||
"source_location": "L95",
|
||||
"source_location": "L110",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -29524,7 +29759,7 @@
|
||||
"confidence": "INFERRED",
|
||||
"confidence_score": 0.8,
|
||||
"source_file": "api/qc-work-order.ts",
|
||||
"source_location": "L102",
|
||||
"source_location": "L117",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
@@ -30327,6 +30562,46 @@
|
||||
"source_location": "L571",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "production_work_order_stepdetailpanel_for",
|
||||
"target": "storage_facility_index_push",
|
||||
"relation": "calls",
|
||||
"confidence": "INFERRED",
|
||||
"confidence_score": 0.8,
|
||||
"source_file": "views/production/production-work-order/StepDetailPanel.vue",
|
||||
"source_location": "L364",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "qc_work_order_index_handlereceptionattachmentdownload",
|
||||
"target": "utils_download_attachment_downloadattachmentfile",
|
||||
"relation": "calls",
|
||||
"confidence": "INFERRED",
|
||||
"confidence_score": 0.8,
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L284",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "qc_work_order_index_handletaskattachmentdownload",
|
||||
"target": "utils_download_attachment_downloadattachmentfile",
|
||||
"relation": "calls",
|
||||
"confidence": "INFERRED",
|
||||
"confidence_score": 0.8,
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L300",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "qc_work_order_index_formatdetectmethods",
|
||||
"target": "storage_facility_index_push",
|
||||
"relation": "calls",
|
||||
"confidence": "INFERRED",
|
||||
"confidence_score": 0.8,
|
||||
"source_file": "views/quality-inspection/qc-work-order/index.vue",
|
||||
"source_location": "L408",
|
||||
"weight": 1.0
|
||||
},
|
||||
{
|
||||
"source": "qc_task_index_handlereceptionattachmentdownload",
|
||||
"target": "utils_download_attachment_downloadattachmentfile",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Graph Report - src (2026-05-22)
|
||||
|
||||
## Corpus Check
|
||||
- Large corpus: 580 files · ~303,483 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
- Large corpus: 581 files · ~306,392 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
|
||||
## Summary
|
||||
- 1416 nodes · 1764 edges · 42 communities detected
|
||||
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 386 edges (avg confidence: 0.8)
|
||||
- 1429 nodes · 1782 edges · 44 communities detected
|
||||
- Extraction: 78% EXTRACTED · 22% INFERRED · 0% AMBIGUOUS · INFERRED: 390 edges (avg confidence: 0.8)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
@@ -34,10 +34,10 @@
|
||||
- [[_COMMUNITY_Community 22|Community 22]]
|
||||
- [[_COMMUNITY_Community 23|Community 23]]
|
||||
- [[_COMMUNITY_Community 24|Community 24]]
|
||||
- [[_COMMUNITY_Community 26|Community 26]]
|
||||
- [[_COMMUNITY_Community 25|Community 25]]
|
||||
- [[_COMMUNITY_Community 27|Community 27]]
|
||||
- [[_COMMUNITY_Community 28|Community 28]]
|
||||
- [[_COMMUNITY_Community 30|Community 30]]
|
||||
- [[_COMMUNITY_Community 29|Community 29]]
|
||||
- [[_COMMUNITY_Community 31|Community 31]]
|
||||
- [[_COMMUNITY_Community 32|Community 32]]
|
||||
- [[_COMMUNITY_Community 33|Community 33]]
|
||||
@@ -45,17 +45,19 @@
|
||||
- [[_COMMUNITY_Community 35|Community 35]]
|
||||
- [[_COMMUNITY_Community 36|Community 36]]
|
||||
- [[_COMMUNITY_Community 37|Community 37]]
|
||||
- [[_COMMUNITY_Community 38|Community 38]]
|
||||
- [[_COMMUNITY_Community 39|Community 39]]
|
||||
- [[_COMMUNITY_Community 40|Community 40]]
|
||||
- [[_COMMUNITY_Community 41|Community 41]]
|
||||
- [[_COMMUNITY_Community 42|Community 42]]
|
||||
- [[_COMMUNITY_Community 43|Community 43]]
|
||||
- [[_COMMUNITY_Community 44|Community 44]]
|
||||
- [[_COMMUNITY_Community 52|Community 52]]
|
||||
- [[_COMMUNITY_Community 45|Community 45]]
|
||||
- [[_COMMUNITY_Community 47|Community 47]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `get()` - 155 edges
|
||||
2. `WebSocketClient` - 26 edges
|
||||
3. `push()` - 18 edges
|
||||
3. `push()` - 20 edges
|
||||
4. `withFullscreenLoading()` - 16 edges
|
||||
5. `MenuProcessor` - 16 edges
|
||||
6. `VersionManager` - 15 edges
|
||||
@@ -68,7 +70,7 @@
|
||||
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
|
||||
views/system/site/index.vue → utils/fullscreen-loading.ts
|
||||
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
|
||||
views/basic-info/customer/index.vue → utils/fullscreen-loading.ts
|
||||
views/basic-info/collection-region/index.vue → utils/fullscreen-loading.ts
|
||||
- `handleUnauthorizedResponse()` --calls--> `fetchRefreshToken()` [INFERRED]
|
||||
utils/http/index.ts → api/auth.ts
|
||||
- `fetchLeaveList()` --calls--> `get()` [INFERRED]
|
||||
@@ -80,215 +82,225 @@
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.01
|
||||
Nodes (80): fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchCellSampleTypeList(), fetchChannelAgreementPrices() (+72 more)
|
||||
Nodes (78): fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchCellSampleTypeList(), fetchChannelAgreementPrices() (+70 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.03
|
||||
Nodes (57): fetchBatchGenerateProtocolNos(), fetchBatchGenerateSampleCodes(), fetchBatchIssueProtocolNos(), fetchBatchIssueSampleCodes(), fetchCollectionProtocolNoList(), fetchCreateCollectionProtocolNo(), fetchCreateSampleCode(), fetchDeleteCollectionProtocolNo() (+49 more)
|
||||
Nodes (56): fetchBatchGenerateProtocolNos(), fetchBatchGenerateSampleCodes(), fetchBatchIssueProtocolNos(), fetchBatchIssueSampleCodes(), fetchCollectionProtocolNoList(), fetchCreateCollectionProtocolNo(), fetchCreateSampleCode(), fetchDeleteCollectionProtocolNo() (+48 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.04
|
||||
Nodes (31): fetchGetUserInfo(), clickTab(), handleTouchEnd(), setTransition(), IframeRouteManager, RouteRegistry, useCommon(), setupAfterEachGuard() (+23 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.05
|
||||
Nodes (32): fetchDeptTree(), fetchPostBoundUserIds(), FireworkSystem, reload(), formatUserLabel(), loadDeptData(), onPersonSelectChange(), subscribeTokenRefresh() (+24 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.04
|
||||
Nodes (18): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents(), fetchAllSopDocuments(), fetchConsumableList() (+10 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.05
|
||||
Nodes (33): fetchDeptTree(), fetchPostBoundUserIds(), FireworkSystem, reload(), formatUserLabel(), loadDeptData(), onPersonSelectChange(), subscribeTokenRefresh() (+25 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.05
|
||||
Nodes (40): fetchAccessLogList(), fetchCleanRoomList(), fetchCreateAccessLog(), fetchCreateCleanRoom(), fetchCreateSite(), fetchDeleteAccessLog(), fetchDeleteCleanRoom(), fetchDeleteSite() (+32 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.09
|
||||
Nodes (31): toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes() (+23 more)
|
||||
Nodes (5): handleReset(), handleSearch(), WebSocketClient, TableCache, emit()
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.06
|
||||
Nodes (10): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), TableCache, themeAnimation() (+2 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (18): fetchAttachmentDownloadUrl(), fetchOrderAdjusts(), fetchOrderDetail(), fetchOrderItems(), fetchOrderList(), fetchPriceVersionDetail(), fetchSystemConfig(), handleReceptionAttachmentDownload() (+10 more)
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.09
|
||||
Nodes (8): getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager, validateStorageData()
|
||||
Nodes (31): toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes() (+23 more)
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.13
|
||||
Nodes (4): handleReset(), handleSearch(), WebSocketClient, emit()
|
||||
Cohesion: 0.09
|
||||
Nodes (8): getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager, validateStorageData()
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.07
|
||||
Nodes (13): fetchAttachmentDownloadUrl(), fetchOrderAdjusts(), fetchOrderDetail(), fetchOrderItems(), fetchOrderList(), fetchPriceVersionDetail(), fetchSystemConfig(), handleReceptionAttachmentDownload() (+5 more)
|
||||
Cohesion: 0.08
|
||||
Nodes (10): collectIds(), useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), themeAnimation() (+2 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.09
|
||||
Nodes (9): fetchGetAllUpgradeLogs(), fetchGetChinaMapData(), fetchGetComments(), fetchGetCurrentVersion(), fetchGetLatestUpgradeLog(), fetchGetSystemConfig(), fetchGetUpgradeLogs(), systemUpgrade() (+1 more)
|
||||
Cohesion: 0.1
|
||||
Nodes (16): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), postorder(), preorder(), applyTreeExpand(), handleAdd() (+8 more)
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.1
|
||||
Nodes (13): fetchEthnicList(), addSample(), applyDonationBundleToForm(), confirmBatchTransportApply(), createEmptyTransport(), ensureDonationProtocolNoInOptions(), ensureEthnicOptionInList(), handleOpenForAdd() (+5 more)
|
||||
Nodes (8): fetchGetMenuList(), fetchGetMenuCodes(), fetchLogin(), fetchRefreshToken(), MenuProcessor, useAppMode(), useAuth(), buildWebEncryptedLoginBody()
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.1
|
||||
Nodes (8): fetchGetMenuList(), fetchGetMenuCodes(), fetchLogin(), fetchRefreshToken(), MenuProcessor, useAppMode(), useAuth(), buildWebEncryptedLoginBody()
|
||||
Nodes (13): fetchEthnicList(), addSample(), applyDonationBundleToForm(), confirmBatchTransportApply(), createEmptyTransport(), ensureDonationProtocolNoInOptions(), ensureEthnicOptionInList(), handleOpenForAdd() (+5 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.08
|
||||
Nodes (14): fetchLeaveDetail(), fetchLeaveList(), fetchApproveTask(), fetchBusinessAllTasks(), fetchCompletedTasks(), fetchDesignerData(), fetchInstanceDetail(), fetchMyInstances() (+6 more)
|
||||
Cohesion: 0.1
|
||||
Nodes (19): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchHospitalList(), fetchUpdateCollectionRegion(), fetchUpdateHospital() (+11 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.1
|
||||
Nodes (18): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchHospitalList(), fetchUpdateCollectionRegion(), fetchUpdateHospital() (+10 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.13
|
||||
Nodes (16): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), postorder(), preorder(), applyTreeExpand(), handleAdd() (+8 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.14
|
||||
Nodes (17): fetchCreateUser(), fetchDeleteUser(), fetchResetPassword(), fetchUpdateUser(), fetchUserDetail(), formatUserDeptDisplay(), handleAdd(), handleDelete() (+9 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.16
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.14
|
||||
Nodes (9): auditorDisplay(), dash(), expressName(), formatYm(), formatYmd(), sampleTypeName(), siteName(), unitName() (+1 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.19
|
||||
Nodes (3): fetchGetLatestUpgradeLog(), systemUpgrade(), VersionManager
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.11
|
||||
Nodes (8): fetchCollectionProtocolAuditList(), fetchCollectionProtocolDetail(), fetchCollectionProtocolList(), fetchPaymentDetailList(), fetchReceiptConfirmedList(), fetchReceiptConfirmList(), fetchRefundConfirmedList(), fetchRefundConfirmList()
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.12
|
||||
Nodes (12): fetchApproveTask(), fetchBusinessAllTasks(), fetchCompletedTasks(), fetchDesignerData(), fetchInstanceDetail(), fetchMyInstances(), fetchPendingTasks(), fetchProcessDefList() (+4 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.16
|
||||
Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.12
|
||||
Nodes (2): fetchProductionTaskDetail(), fetchProductionTaskList()
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.2
|
||||
Nodes (2): ComponentLoader, RouteTransformer
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.27
|
||||
Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.17
|
||||
Nodes (12): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), handleAdd(), handleBatchDelete(), handleDelete(), handleReset() (+4 more)
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.18
|
||||
Nodes (11): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), handleAdd(), handleDelete(), handleReset(), handleSearch() (+3 more)
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.17
|
||||
Nodes (3): fetchResearchDonationAuditList(), fetchResearchDonationDetail(), fetchResearchDonationList()
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.18
|
||||
Nodes (3): fetchMaterialList(), fetchMaterialOptions(), fetchMaterialStockLogList()
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.2
|
||||
Nodes (2): fetchQcMaterialList(), fetchQcMaterialOptions()
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.39
|
||||
Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.36
|
||||
Nodes (1): TableHeightCalculator
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): logApiCryptoBootInfo(), maskSecret()
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.29
|
||||
Nodes (2): fetchLeaveDetail(), fetchLeaveList()
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.47
|
||||
Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchSampleReceptionCheckItemList(), fetchSampleReceptionCheckItemsActive()
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
### Community 37 - "Community 37"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchQcThirdPartyItemList(), fetchQcThirdPartyItemsActive()
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
### Community 38 - "Community 38"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchQcLabTestItemList(), fetchQcLabTestItemsActive()
|
||||
|
||||
### Community 37 - "Community 37"
|
||||
### Community 39 - "Community 39"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchQcTaskDetail(), fetchQcTaskList()
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
### Community 40 - "Community 40"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchQcThirdPartyCompanyList(), fetchQcThirdPartyCompanyOptions()
|
||||
|
||||
### Community 42 - "Community 42"
|
||||
Cohesion: 0.53
|
||||
Nodes (4): isEtaTodayOrEarlier(), parseEtaLocalDate(), sampleReceptionRowClassName(), startOfLocalDay()
|
||||
|
||||
### Community 40 - "Community 40"
|
||||
### Community 43 - "Community 43"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): focusInput(), handleKeydown()
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
### Community 44 - "Community 44"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): if(), logSidebarMenu()
|
||||
|
||||
### Community 42 - "Community 42"
|
||||
### Community 45 - "Community 45"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): getSettingDefaults(), resetToDefaults()
|
||||
|
||||
### Community 44 - "Community 44"
|
||||
### Community 47 - "Community 47"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
|
||||
|
||||
### Community 52 - "Community 52"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): fetchDeleteProjectSystemItem(), if()
|
||||
|
||||
## Knowledge Gaps
|
||||
- **Thin community `Community 21`** (17 nodes): `fetchAddTaskStep()`, `fetchCopyTaskStep()`, `fetchCreateProductionTask()`, `fetchDeleteTaskStep()`, `fetchProductionTaskDetail()`, `fetchProductionTaskList()`, `fetchReorderTaskSteps()`, `fetchReplaceStepConsumables()`, `fetchReplaceStepDataCollection()`, `fetchReplaceStepOutputs()`, `fetchReplaceStepReagents()`, `fetchSetTaskInputs()`, `fetchSetTaskOutputs()`, `fetchUpdateProductionTask()`, `fetchUpdateProductionTaskStatus()`, `fetchUpdateTaskStep()`, `production-task.ts`
|
||||
- **Thin community `Community 22`** (17 nodes): `fetchAddTaskStep()`, `fetchCopyTaskStep()`, `fetchCreateProductionTask()`, `fetchDeleteTaskStep()`, `fetchProductionTaskDetail()`, `fetchProductionTaskList()`, `fetchReorderTaskSteps()`, `fetchReplaceStepConsumables()`, `fetchReplaceStepDataCollection()`, `fetchReplaceStepOutputs()`, `fetchReplaceStepReagents()`, `fetchSetTaskInputs()`, `fetchSetTaskOutputs()`, `fetchUpdateProductionTask()`, `fetchUpdateProductionTaskStatus()`, `fetchUpdateTaskStep()`, `production-task.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 22`** (17 nodes): `ComponentLoader`, `.constructor()`, `.createEmptyComponent()`, `.createErrorComponent()`, `.load()`, `.loadIframe()`, `.loadLayout()`, `.load()`, `RouteTransformer`, `.extractFirstSegment()`, `.handleFirstLevelRoute()`, `.handleIframeRoute()`, `.handleNormalRoute()`, `.isFirstLevelRoute()`, `.transform()`, `ComponentLoader.ts`, `RouteTransformer.ts`
|
||||
- **Thin community `Community 23`** (17 nodes): `ComponentLoader`, `.constructor()`, `.createEmptyComponent()`, `.createErrorComponent()`, `.load()`, `.loadIframe()`, `.loadLayout()`, `.load()`, `RouteTransformer`, `.extractFirstSegment()`, `.handleFirstLevelRoute()`, `.handleIframeRoute()`, `.handleNormalRoute()`, `.isFirstLevelRoute()`, `.transform()`, `ComponentLoader.ts`, `RouteTransformer.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 28`** (10 nodes): `fetchBatchStockInQcMaterial()`, `fetchBatchStockOutQcMaterial()`, `fetchCreateQcMaterial()`, `fetchDeleteQcMaterial()`, `fetchQcMaterialList()`, `fetchQcMaterialOptions()`, `fetchStockInQcMaterial()`, `fetchStockOutQcMaterial()`, `fetchUpdateQcMaterial()`, `qc-material.ts`
|
||||
- **Thin community `Community 29`** (10 nodes): `fetchBatchStockInQcMaterial()`, `fetchBatchStockOutQcMaterial()`, `fetchCreateQcMaterial()`, `fetchDeleteQcMaterial()`, `fetchQcMaterialList()`, `fetchQcMaterialOptions()`, `fetchStockInQcMaterial()`, `fetchStockOutQcMaterial()`, `fetchUpdateQcMaterial()`, `qc-material.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 31`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
|
||||
- **Thin community `Community 32`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 32`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
|
||||
- **Thin community `Community 33`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 34`** (6 nodes): `fetchCreateSampleReceptionCheckItem()`, `fetchDeleteSampleReceptionCheckItem()`, `fetchSampleReceptionCheckItemList()`, `fetchSampleReceptionCheckItemsActive()`, `fetchUpdateSampleReceptionCheckItem()`, `sample-reception-check-item.ts`
|
||||
- **Thin community `Community 34`** (7 nodes): `fetchCreateLeave()`, `fetchDeleteLeave()`, `fetchLeaveDetail()`, `fetchLeaveList()`, `fetchSubmitLeave()`, `fetchUpdateLeave()`, `leave.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 35`** (6 nodes): `fetchCreateQcThirdPartyItem()`, `fetchDeleteQcThirdPartyItem()`, `fetchQcThirdPartyItemList()`, `fetchQcThirdPartyItemsActive()`, `fetchUpdateQcThirdPartyItem()`, `qc-third-party-item.ts`
|
||||
- **Thin community `Community 36`** (6 nodes): `fetchCreateSampleReceptionCheckItem()`, `fetchDeleteSampleReceptionCheckItem()`, `fetchSampleReceptionCheckItemList()`, `fetchSampleReceptionCheckItemsActive()`, `fetchUpdateSampleReceptionCheckItem()`, `sample-reception-check-item.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 36`** (6 nodes): `fetchCreateQcLabTestItem()`, `fetchDeleteQcLabTestItem()`, `fetchQcLabTestItemList()`, `fetchQcLabTestItemsActive()`, `fetchUpdateQcLabTestItem()`, `qc-lab-test-item.ts`
|
||||
- **Thin community `Community 37`** (6 nodes): `fetchCreateQcThirdPartyItem()`, `fetchDeleteQcThirdPartyItem()`, `fetchQcThirdPartyItemList()`, `fetchQcThirdPartyItemsActive()`, `fetchUpdateQcThirdPartyItem()`, `qc-third-party-item.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 37`** (6 nodes): `fetchCompleteQcTask()`, `fetchQcTaskDetail()`, `fetchQcTaskList()`, `fetchStartQcTask()`, `fetchUpdateQcTaskAttachments()`, `qc-task.ts`
|
||||
- **Thin community `Community 38`** (6 nodes): `fetchCreateQcLabTestItem()`, `fetchDeleteQcLabTestItem()`, `fetchQcLabTestItemList()`, `fetchQcLabTestItemsActive()`, `fetchUpdateQcLabTestItem()`, `qc-lab-test-item.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 40`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
|
||||
- **Thin community `Community 39`** (6 nodes): `fetchCompleteQcTask()`, `fetchQcTaskDetail()`, `fetchQcTaskList()`, `fetchStartQcTask()`, `fetchUpdateQcTaskAttachments()`, `qc-task.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 41`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
|
||||
- **Thin community `Community 40`** (6 nodes): `fetchCreateQcThirdPartyCompany()`, `fetchDeleteQcThirdPartyCompany()`, `fetchQcThirdPartyCompanyList()`, `fetchQcThirdPartyCompanyOptions()`, `fetchUpdateQcThirdPartyCompany()`, `qc-third-party-company.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 42`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
- **Thin community `Community 43`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 44`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
|
||||
- **Thin community `Community 44`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 52`** (3 nodes): `fetchDeleteProjectSystemItem()`, `if()`, `index.vue`
|
||||
- **Thin community `Community 45`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 47`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 17`, `Community 18`, `Community 21`, `Community 24`, `Community 26`, `Community 27`, `Community 28`, `Community 34`, `Community 35`, `Community 36`, `Community 37`?**
|
||||
_High betweenness centrality (0.267) - this node is a cross-community bridge._
|
||||
- **Why does `push()` connect `Community 3` to `Community 0`, `Community 2`, `Community 7`, `Community 9`, `Community 11`, `Community 12`?**
|
||||
_High betweenness centrality (0.075) - this node is a cross-community bridge._
|
||||
- **Why does `buildTreePathLabelFromFlat()` connect `Community 3` to `Community 0`, `Community 7`?**
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 7`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 22`, `Community 25`, `Community 27`, `Community 28`, `Community 29`, `Community 34`, `Community 36`, `Community 37`, `Community 38`, `Community 39`, `Community 40`?**
|
||||
_High betweenness centrality (0.266) - this node is a cross-community bridge._
|
||||
- **Why does `push()` connect `Community 4` to `Community 0`, `Community 2`, `Community 6`, `Community 7`, `Community 10`, `Community 13`, `Community 18`?**
|
||||
_High betweenness centrality (0.091) - this node is a cross-community bridge._
|
||||
- **Why does `fetchGetLatestUpgradeLog()` connect `Community 18` to `Community 0`?**
|
||||
_High betweenness centrality (0.042) - this node is a cross-community bridge._
|
||||
- **Are the 154 inferred relationships involving `get()` (e.g. with `buildTreePathLabelFromFlat()` and `alignFqPaymentDetailsForDisplay()`) actually correct?**
|
||||
_`get()` has 154 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 17 inferred relationships involving `push()` (e.g. with `fetchAllPagedRecords()` and `calcInstallmentDueAmounts()`) actually correct?**
|
||||
_`push()` has 17 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 19 inferred relationships involving `push()` (e.g. with `fetchAllPagedRecords()` and `calcInstallmentDueAmounts()`) actually correct?**
|
||||
_`push()` has 19 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 15 inferred relationships involving `withFullscreenLoading()` (e.g. with `.close()` and `handleSubmit()`) actually correct?**
|
||||
_`withFullscreenLoading()` has 15 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Should `Community 0` be split into smaller, more focused modules?**
|
||||
|
||||
@@ -57,12 +57,12 @@
|
||||
</div>
|
||||
<div id="legend"></div>
|
||||
</div>
|
||||
<div id="stats">1416 nodes · 1764 edges · 243 communities</div>
|
||||
<div id="stats">1429 nodes · 1782 edges · 244 communities</div>
|
||||
</div>
|
||||
<script>
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 32, "community_name": "Community 32", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 32, "community_name": "Community 32", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 32, "community_name": "Community 32", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 32, "community_name": "Community 32", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 32, "community_name": "Community 32", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#E15759", "border": "#E15759", "highlight": {"background": "#ffffff", "border": "#E15759"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 32, "community_name": "Community 32", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#9C755F", "border": "#9C755F", "highlight": {"background": "#ffffff", "border": "#9C755F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 78, "community_name": "Community 78", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 79, "community_name": "Community 79", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 16, "community_name": "Community 16", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.7, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 16, "community_name": "Community 16", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 16, "community_name": "Community 16", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 16, "community_name": "Community 16", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 16, "community_name": "Community 16", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective", "laLine truncated
|
||||
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 233}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 99}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 69}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 53}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 53}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 51}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 39}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 39}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 36}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 34}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 32}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 31}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 27}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 27}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 26}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 26}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 22}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 21}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 20}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 18}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 17}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 17}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 17}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 16}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 16}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 13}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 12}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 11}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 10}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 9}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 8}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 8}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 7}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 6}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 6}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 6}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 6}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 6}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 6}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 6}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 5}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 4}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 3}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 3}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 3}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 3}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 3}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 3}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 3}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 3}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 3}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 3}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 3}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 2}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 2}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 2}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 2}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 2}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 2}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 2}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 2}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 2}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 2}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 2}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 2}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 2}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 2}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 2}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 2}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "count": 2}, {"cLine truncated
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 33, "community_name": "Community 33", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 33, "community_name": "Community 33", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 33, "community_name": "Community 33", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 33, "community_name": "Community 33", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 33, "community_name": "Community 33", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 33, "community_name": "Community 33", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 80, "community_name": "Community 80", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 81, "community_name": "Community 81", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 15, "community_name": "Community 15", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.7, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 15, "community_name": "Community 15", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 15, "community_name": "Community 15", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 15, "community_name": "Community 15", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 15, "community_name": "Community 15", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective", "laLine truncated
|
||||
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 223}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 87}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 69}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 53}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 53}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 51}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 48}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 44}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 39}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 36}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 30}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 28}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 27}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 27}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 27}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 22}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 20}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 20}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 19}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 19}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 19}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 17}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 17}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 17}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 16}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 15}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 13}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 12}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 11}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 10}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 9}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 8}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 8}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 7}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 7}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 6}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 6}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 6}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 6}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 6}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 6}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 6}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 6}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 5}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 4}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 3}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 3}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 3}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 3}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 3}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 3}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 3}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 3}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 3}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 3}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 2}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 2}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 2}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 2}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 2}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 2}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 2}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 2}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 2}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 2}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 2}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 2}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 2}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 2}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "count": 2}, {"Line truncated
|
||||
|
||||
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
|
||||
function esc(s) {
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1778582068.0254738,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1778582068.026978,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1778582068.0246184,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1779420283.3944924,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1779420178.0237312,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1779430756.3392074,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1779425860.6226668,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1778582068.0293276,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1778775532.895603,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1778582068.004279,
|
||||
@@ -175,7 +175,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/order-operate-local-dialog/index.vue": 1778582866.2188683,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/product-view-dialog/index.vue": 1779370638.1218963,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-payment-detail-readonly/index.vue": 1779006281.4220655,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/system-view-dialog/index.vue": 1779370582.0906003,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/system-view-dialog/index.vue": 1779431181.051139,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/order-operate-dialog-bridge/index.vue": 1778582865.387537,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/index.ts": 1778582068.0037498,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/hooks/core/useFastEnter.ts": 1778582068.0014625,
|
||||
@@ -215,14 +215,14 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material.ts": 1779283387.4196396,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-reception.ts": 1779255909.8553667,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/auth.ts": 1778582067.8610063,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1779180968.908451,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1779431182.2279022,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/auth/login/index.vue": 1778910234.3222458,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/refund-confirm/index.vue": 1779269573.509064,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/index.vue": 1779329214.96505,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolStatusChangeDialog.vue": 1779161483.857836,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue": 1779370679.264584,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue": 1779424053.799563,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue": 1778938314.528914,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue": 1779371035.181769,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue": 1779423155.778682,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue": 1778978838.5026703,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-info/index.vue": 1779313518.430975,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/storage-cert/index.vue": 1779313530.5053055,
|
||||
@@ -240,7 +240,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/index.vue": 1779227504.1804457,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationAuditDialog.vue": 1779112125.8042517,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationViewDialog.vue": 1779370624.9148946,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue": 1779371033.7577431,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue": 1779423240.754115,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationStatusChangeDialog.vue": 1779161484.445245,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/dashboard/console/index.vue": 1779227504.1947322,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/tenant/index.vue": 1779257191.2809517,
|
||||
@@ -268,29 +268,30 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/dict/index.vue": 1779259163.7560437,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/leave/index.vue": 1779257191.2768743,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/output-type/index.vue": 1779307917.5932443,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/index.vue": 1779341967.5417912,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue": 1779408337.9346838,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/index.vue": 1779423904.9098966,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue": 1779422386.4775605,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/sop/index.vue": 1779309609.6630545,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-task/index.vue": 1779414898.4036016,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/reagent/index.vue": 1779307562.93293,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue": 1779361308.0826523,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1779418710.3300052,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1779408746.3297637,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue": 1779415778.1769066,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue": 1779414418.4825,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1779426103.3002872,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1779422456.542952,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue": 1779426096.2213817,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue": 1779422457.5458138,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue": 1779431206.6225035,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/consumable-pack/index.vue": 1779307488.018346,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/consumable/index.vue": 1779307921.9798808,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process/index.vue": 1779309524.0991938,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process/index.vue": 1779423832.810978,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-category/index.vue": 1779307716.2724373,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/index/index.vue": 1778582068.070888,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1779269560.7371643,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1779431280.842699,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception-check-item/index.vue": 1779269589.0865154,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/lab-test-item/index.vue": 1779331328.5864854,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/lab-test-item/index.vue": 1779423712.9489925,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/index.vue": 1779284232.1223023,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue": 1779333860.8573632,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue": 1779423489.553699,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue": 1779371036.8066387,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material-usage/index.vue": 1779285902.4727366,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/third-party-company/index.vue": 1779330250.9402645,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/third-party-company/index.vue": 1779423582.5553048,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/500/index.vue": 1778582068.0705533,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/403/index.vue": 1778582068.0697916,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/404/index.vue": 1778582068.0701847,
|
||||
|
||||
@@ -32,6 +32,10 @@ export function fetchUpdateProductionTaskStatus(id: string, data: Record<string,
|
||||
return request.put({ url: `/web/admin/production-tasks/${id}/status`, params: data })
|
||||
}
|
||||
|
||||
export function fetchDeleteProductionTask(id: string) {
|
||||
return request.del({ url: `/web/admin/production-tasks/${id}` })
|
||||
}
|
||||
|
||||
export function fetchAddTaskStep(taskId: string, data: Record<string, any>) {
|
||||
return request.post({ url: `/web/admin/production-tasks/${taskId}/steps`, params: data })
|
||||
}
|
||||
|
||||
@@ -83,7 +83,22 @@ export type QcWorkOrderDetail = QcWorkOrder & {
|
||||
itemName: string
|
||||
feeAmount: number
|
||||
}>
|
||||
tasks?: Array<Record<string, unknown>>
|
||||
tasks?: Array<{
|
||||
id: string
|
||||
taskNo: string
|
||||
taskType: string
|
||||
itemCode?: string
|
||||
itemName?: string
|
||||
companyId?: string
|
||||
companyName?: string
|
||||
feeAmount?: number
|
||||
taskStatus: string
|
||||
conclusion?: string
|
||||
conclusionLabel?: string
|
||||
remark?: string
|
||||
completedTime?: string
|
||||
attachmentFiles?: Array<{ id?: string; fileUrl?: string; fileName?: string }>
|
||||
}>
|
||||
}
|
||||
|
||||
export function fetchQcWorkOrderList(params?: {
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
/** 弹窗宽度,默认 720px */
|
||||
width?: string
|
||||
/** 字段栅格列数,默认 2 */
|
||||
gridColumns?: 2 | 3
|
||||
gridColumns?: 2 | 3 | 4
|
||||
/** 挂到 body,用于嵌套在其它弹窗内时叠层展示 */
|
||||
appendToBody?: boolean
|
||||
/** 遮罩类名(可配合全局样式提高 z-index) */
|
||||
@@ -240,6 +240,10 @@
|
||||
&--cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
&--cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.view-kv {
|
||||
@@ -301,15 +305,23 @@
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.view-kv-grid--cols-4 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.view-kv-grid--cols-3 {
|
||||
.view-kv-grid--cols-3,
|
||||
.view-kv-grid--cols-4 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.view-kv-grid,
|
||||
.view-kv-grid--cols-3 {
|
||||
.view-kv-grid--cols-3,
|
||||
.view-kv-grid--cols-4 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "label": "StepDetailPanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L1"}, {"id": "production_work_order_stepdetailpanel_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L362"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "target": "production_work_order_stepdetailpanel_for", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L362", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "push", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L364"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "decodeURIComponent", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L368"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "pop", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L368"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "split", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L368"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L368"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "label": "DispatchTaskDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L1"}, {"id": "production_work_order_dispatchtaskdialog_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L203"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_dispatchtaskdialog_vue", "target": "production_work_order_dispatchtaskdialog_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L203", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_dispatchtaskdialog_if", "callee": "initEditor", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue", "source_location": "L207"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskexecutiondialog_vue", "label": "TaskExecutionDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "label": "qc-work-order.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L1"}, {"id": "api_qc_work_order_fetchqcworkorderlist", "label": "fetchQcWorkOrderList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L104"}, {"id": "api_qc_work_order_fetchqcworkorderdetail", "label": "fetchQcWorkOrderDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L116"}, {"id": "api_qc_work_order_fetchconfigureqcworkorder", "label": "fetchConfigureQcWorkOrder()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L122"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchqcworkorderlist", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L104", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchqcworkorderdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L116", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order_ts", "target": "api_qc_work_order_fetchconfigureqcworkorder", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L122", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_qc_work_order_fetchqcworkorderlist", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L110"}, {"caller_nid": "api_qc_work_order_fetchqcworkorderdetail", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L117"}, {"caller_nid": "api_qc_work_order_fetchconfigureqcworkorder", "callee": "post", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts", "source_location": "L131"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_system_view_dialog_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/system-view-dialog/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1"}, {"id": "process_route_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1068"}, {"id": "process_route_index_get", "label": "get()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1269"}, {"id": "process_route_index_set", "label": "set()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1279"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L729", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolattachmentreadonly_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L730", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_download_attachment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L731", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "vue_draggable_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L732", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L733", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L756", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "process_route_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1068", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "process_route_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1185", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "process_route_index_get", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1269", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "process_route_index_set", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1279", "weight": 1.0}], "raw_calls": [{"caller_nid": "process_route_index_if", "callee": "some", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1069"}, {"caller_nid": "process_route_index_if", "callee": "mapReagentOptionLabel", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1071"}, {"caller_nid": "process_route_index_if", "callee": "some", "source_file": "/UsLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_third_party_company_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/third-party-company/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_cell_product_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1"}, {"id": "qc_work_order_index_handlereceptionattachmentpreview", "label": "handleReceptionAttachmentPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L278"}, {"id": "qc_work_order_index_handlereceptionattachmentdownload", "label": "handleReceptionAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L283"}, {"id": "qc_work_order_index_taskattachmentfullurl", "label": "taskAttachmentFullUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L287"}, {"id": "qc_work_order_index_handletaskattachmentpreview", "label": "handleTaskAttachmentPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L294"}, {"id": "qc_work_order_index_handletaskattachmentdownload", "label": "handleTaskAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L299"}, {"id": "qc_work_order_index_formatconfiguredlabitems", "label": "formatConfiguredLabItems()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L315"}, {"id": "qc_work_order_index_formatconfiguredthirditems", "label": "formatConfiguredThirdItems()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L321"}, {"id": "qc_work_order_index_buildtasktypesections", "label": "buildTaskTypeSections()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L329"}, {"id": "qc_work_order_index_buildconfiguredonlysection", "label": "buildConfiguredOnlySection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L388"}, {"id": "qc_work_order_index_formatdetectmethods", "label": "formatDetectMethods()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L406"}, {"id": "qc_work_order_index_formatmetric", "label": "formatMetric()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L413"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handlereceptionattachmentpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L278", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handlereceptionattachmentdownload", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L283", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_taskattachmentfullurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L287", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handletaskattachmentpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L294", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handletaskattachmentdownload", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue",Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_lab_test_item_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/lab-test-item/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputproductdialog_vue", "label": "TaskOutputProductDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_research_donation_list_donationformdialog_vue", "label": "DonationFormDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1"}, {"id": "list_donationformdialog_createemptytransport", "label": "createEmptyTransport()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1019"}, {"id": "list_donationformdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1039"}, {"id": "list_donationformdialog_loadethnicoptions", "label": "loadEthnicOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1046"}, {"id": "list_donationformdialog_ensureethnicoptioninlist", "label": "ensureEthnicOptionInList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1057"}, {"id": "list_donationformdialog_samplerowlabel", "label": "sampleRowLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1085"}, {"id": "list_donationformdialog_resetform", "label": "resetForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1169"}, {"id": "list_donationformdialog_scrolltosection", "label": "scrollToSection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1224"}, {"id": "list_donationformdialog_normalizesamplerow", "label": "normalizeSampleRow()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1232"}, {"id": "list_donationformdialog_openbatchtransportdialog", "label": "openBatchTransportDialog()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1255"}, {"id": "list_donationformdialog_confirmbatchtransportapply", "label": "confirmBatchTransportApply()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1263"}, {"id": "list_donationformdialog_addsample", "label": "addSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1277"}, {"id": "list_donationformdialog_removesample", "label": "removeSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1291"}, {"id": "list_donationformdialog_handleopenforadd", "label": "handleOpenForAdd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1295"}, {"id": "list_donationformdialog_applydonationbundletoform", "label": "applyDonationBundleToForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1343"}, {"id": "list_donationformdialog_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1978"}, {"id": "list_donationformdialog_ensuredonationprotocolnoinoptions", "label": "ensureDonationProtocolNoInOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L2051"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_research_donation_list_donationformdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L935", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_research_donation_list_donationformdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L28"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L50"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L57"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_toapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L28", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_fromapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_normalizestepsubtables", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_parseordernum", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L50", "weight": 1.0}, {"source": "users_wuzilong_woLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "label": "ProtocolFormDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1"}, {"id": "protocol_protocolformdialog_createemptytransport", "label": "createEmptyTransport()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1287"}, {"id": "protocol_protocolformdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1307"}, {"id": "protocol_protocolformdialog_loadethnicoptions", "label": "loadEthnicOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1314"}, {"id": "protocol_protocolformdialog_ensureethnicoptioninlist", "label": "ensureEthnicOptionInList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1325"}, {"id": "protocol_protocolformdialog_expressname", "label": "expressName()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1353"}, {"id": "protocol_protocolformdialog_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L2021"}, {"id": "protocol_protocolformdialog_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L2557"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1181", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1182", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_collection_protocol", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1194", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_business", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1200", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_download_attachment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1201", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1202", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_ethnic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1215", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user", "relation": "imports_from", "confidence": "EXTRACTED", "sLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L1"}, {"id": "sample_reception_index_parseetalocaldate", "label": "parseEtaLocalDate()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L683"}, {"id": "sample_reception_index_startoflocalday", "label": "startOfLocalDay()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L697"}, {"id": "sample_reception_index_isetatodayorearlier", "label": "isEtaTodayOrEarlier()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L701"}, {"id": "sample_reception_index_samplereceptionrowclassname", "label": "sampleReceptionRowClassName()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L707"}, {"id": "sample_reception_index_pad", "label": "pad()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L832"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_parseetalocaldate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L683", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_startoflocalday", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L697", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_isetatodayorearlier", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L701", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_samplereceptionrowclassname", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L707", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_pad", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L832", "weight": 1.0}, {"source": "sample_reception_index_isetatodayorearlier", "target": "sample_reception_index_parseetalocaldate", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L702", "weight": 1.0}, {"source": "sample_reception_index_isetatodayorearlier", "target": "sample_reception_index_startoflocalday", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L704", "weight": 1.0}, {"source": "sample_reception_index_samplereceptionrowclassname", "target": "sample_reception_index_isetatodayorearlier", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L709", "weight": 1.0}], "raw_calls": [{"caller_nid": "sample_reception_index_parseetalocaldate", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L684"}, {"caller_nid": "sample_reception_index_parseetalocaldate", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L684"}, {"caller_nid": "sample_reception_index_parseetalocaldate", "callee": "slice", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/srLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "label": "ProtocolViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1"}, {"id": "protocol_protocolviewdialog_dash", "label": "dash()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L324"}, {"id": "protocol_protocolviewdialog_handleclosed", "label": "handleClosed()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1643"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "target": "protocol_protocolviewdialog_dash", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L324", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "target": "protocol_protocolviewdialog_handleclosed", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1643", "weight": 1.0}], "raw_calls": [{"caller_nid": "protocol_protocolviewdialog_dash", "callee": "unitName", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L324"}]}
|
||||
@@ -1416,12 +1416,15 @@
|
||||
activeSubTab.value = 'reagent'
|
||||
processSearchKeyword.value = ''
|
||||
configSuppressSave.value = true
|
||||
stepList.value = []
|
||||
stepListLoading.value = true
|
||||
configDialogVisible.value = true
|
||||
|
||||
try {
|
||||
await loadConfigReferenceData()
|
||||
await loadRouteDetail()
|
||||
} finally {
|
||||
stepListLoading.value = false
|
||||
await nextTick()
|
||||
configSuppressSave.value = false
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="process-dialog-body min-h-[200px]">
|
||||
<DialogFormBody :loading="dialogLoading" body-class="process-dialog-body min-h-[200px]">
|
||||
<div class="detail-tabs">
|
||||
<div
|
||||
v-for="tab in formTabs"
|
||||
@@ -482,7 +482,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</DialogFormBody>
|
||||
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="dialogVisible = false">
|
||||
@@ -687,6 +687,7 @@
|
||||
import { fetchItemUnitList } from '@/api/admin'
|
||||
import { packTypeLabel, packTypeTagType } from '@/utils/status-tag-registry'
|
||||
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
|
||||
import DialogFormBody from '@/components/business/dialog-form-body/index.vue'
|
||||
|
||||
defineOptions({ name: 'ProductionProcess' })
|
||||
|
||||
@@ -699,6 +700,7 @@
|
||||
|
||||
// 弹窗状态
|
||||
const dialogVisible = ref(false)
|
||||
const dialogLoading = ref(false)
|
||||
const dialogTitle = ref('新增工艺')
|
||||
const isEdit = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
@@ -1018,13 +1020,15 @@
|
||||
isEdit.value = false
|
||||
dialogTitle.value = '新增工艺'
|
||||
resetForm()
|
||||
await loadReferenceData()
|
||||
await searchItemUnitOptions('')
|
||||
dialogVisible.value = true
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate?.()
|
||||
initDataCollectionSortable()
|
||||
})
|
||||
dialogLoading.value = true
|
||||
nextTick(() => formRef.value?.clearValidate?.())
|
||||
try {
|
||||
await Promise.all([loadReferenceData(), searchItemUnitOptions('')])
|
||||
} finally {
|
||||
dialogLoading.value = false
|
||||
nextTick(() => initDataCollectionSortable())
|
||||
}
|
||||
}
|
||||
|
||||
async function handleEdit(row: any) {
|
||||
@@ -1032,9 +1036,14 @@
|
||||
isEdit.value = true
|
||||
dialogTitle.value = '编辑工艺'
|
||||
editingId.value = row.id
|
||||
await loadReferenceData()
|
||||
dialogVisible.value = true
|
||||
dialogLoading.value = true
|
||||
nextTick(() => formRef.value?.clearValidate?.())
|
||||
try {
|
||||
const detail = await fetchProcessDetail(row.id)
|
||||
const [, detail] = await Promise.all([
|
||||
loadReferenceData(),
|
||||
fetchProcessDetail(row.id)
|
||||
])
|
||||
formData.processCode = detail.processCode || ''
|
||||
formData.processName = detail.processName || ''
|
||||
formData.categoryId = detail.categoryId || ''
|
||||
@@ -1067,12 +1076,10 @@
|
||||
ensureDataCollectionUnitOptions()
|
||||
} catch {
|
||||
ElMessage.error('加载详情失败')
|
||||
} finally {
|
||||
dialogLoading.value = false
|
||||
nextTick(() => initDataCollectionSortable())
|
||||
}
|
||||
dialogVisible.value = true
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate?.()
|
||||
initDataCollectionSortable()
|
||||
})
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="phase === 'form' ? '派发生产任务' : `编辑生产任务 — ${createdTaskName || editTaskName || ''}`"
|
||||
:title="
|
||||
phase === 'form' ? '派发生产任务' : `编辑生产任务 — ${createdTaskName || editTaskName || ''}`
|
||||
"
|
||||
:width="phase === 'form' ? '560px' : '90vw'"
|
||||
:class="phase === 'edit' ? 'dialog-form-layout dispatch-route-dialog' : 'dispatch-dialog'"
|
||||
align-center
|
||||
@@ -12,27 +14,60 @@
|
||||
>
|
||||
<!-- 阶段一:表单 -->
|
||||
<div v-if="phase === 'form'" class="dispatch-dialog-work-order-info">
|
||||
<span>工单号:<b>{{ workOrderRow?.workOrderNo }}</b></span>
|
||||
<span
|
||||
>工单号:<b>{{ workOrderRow?.workOrderNo }}</b></span
|
||||
>
|
||||
<span>协议号:{{ workOrderRow?.protocolNo || '—' }}</span>
|
||||
<span>样本编码:{{ workOrderRow?.sampleCode || '—' }}</span>
|
||||
</div>
|
||||
<el-form v-if="phase === 'form'" ref="formRef" :model="form" :rules="rules" label-position="top" class="dialog-form-layout">
|
||||
<el-form
|
||||
v-if="phase === 'form'"
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="dialog-form-layout"
|
||||
>
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="任务名称" prop="taskName" class="form-col-span-2">
|
||||
<el-input v-model="form.taskName" placeholder="请输入任务名称" maxlength="200" />
|
||||
</el-form-item>
|
||||
<el-form-item label="工艺路线" class="form-col-span-2">
|
||||
<template #label>工艺路线<span class="form-label-tip">可选,不选则从零创建工艺过程</span></template>
|
||||
<el-select v-model="form.routeId" placeholder="请选择工艺路线(可选)" filterable clearable style="width: 100%">
|
||||
<el-option v-for="route in routeOptions" :key="route.id"
|
||||
:label="`${route.routeName}(${route.routeCode})`" :value="route.id" />
|
||||
<template #label
|
||||
>工艺路线<span class="form-label-tip">可选,不选则从零创建工艺过程</span></template
|
||||
>
|
||||
<el-select
|
||||
v-model="form.routeId"
|
||||
placeholder="请选择工艺路线(可选)"
|
||||
filterable
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="route in routeOptions"
|
||||
:key="route.id"
|
||||
:label="`${route.routeName}(${route.routeCode})`"
|
||||
:value="route.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" class="form-col-span-2">
|
||||
<el-input v-model="form.description" type="textarea" :rows="3" placeholder="可选,填写任务描述" maxlength="500" />
|
||||
<el-input
|
||||
v-model="form.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="可选,填写任务描述"
|
||||
maxlength="500"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" class="form-col-span-2">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="可选,填写备注" maxlength="512" />
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="可选,填写备注"
|
||||
maxlength="512"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
@@ -40,76 +75,121 @@
|
||||
<template #footer>
|
||||
<div v-if="phase === 'form'">
|
||||
<el-button class="btn-ghost" @click="handleClose">取消</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="submitting" @click="handleCreateTask">生成任务</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="submitting" @click="handleCreateTask"
|
||||
>生成任务</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 阶段二:三栏编辑 -->
|
||||
<div v-if="phase === 'edit'" class="route-config-body">
|
||||
<div class="route-config-left">
|
||||
<div class="route-config-left-header">
|
||||
<h3>工艺库</h3>
|
||||
<el-input v-model="editor.processSearchKeyword.value" placeholder="搜索工序" clearable size="small"
|
||||
class="route-config-search" />
|
||||
</div>
|
||||
<el-collapse v-model="editor.expandedCategories.value" class="route-config-collapse">
|
||||
<el-collapse-item v-for="cat in editor.filteredProcessLibrary.value" :key="cat.categoryId"
|
||||
:name="cat.categoryId">
|
||||
<template #title>
|
||||
<span class="route-config-cat-bar"></span>
|
||||
<span>{{ cat.categoryName }}</span>
|
||||
</template>
|
||||
<div v-for="(proc, procIndex) in cat.processes" :key="proc.id" class="route-config-process-card"
|
||||
draggable="true" @dragstart="editor.onDragStart($event, proc)">
|
||||
<div class="route-config-process-info">
|
||||
<div class="route-config-process-name">
|
||||
<span class="route-config-process-seq">{{ procIndex + 1 }}</span>{{ proc.processName }}
|
||||
</div>
|
||||
<div class="route-config-process-code">{{ proc.processCode }}</div>
|
||||
</div>
|
||||
<el-icon class="route-config-process-drag"><Rank /></el-icon>
|
||||
</div>
|
||||
<el-empty v-if="!cat.processes.length" description="该分类暂无工序" :image-size="32" />
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<el-empty v-if="!editor.filteredProcessLibrary.value.length" description="暂无工艺数据" :image-size="48" />
|
||||
<div class="route-config-left">
|
||||
<div class="route-config-left-header">
|
||||
<h3>工艺库</h3>
|
||||
<el-input
|
||||
v-model="editor.processSearchKeyword.value"
|
||||
placeholder="搜索工序"
|
||||
clearable
|
||||
size="small"
|
||||
class="route-config-search"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="route-config-center" v-loading="editor.stepListLoading.value" @dragover.prevent @drop="editor.onDropToCenter">
|
||||
<div class="route-config-center-header">
|
||||
<h3>工序步骤</h3>
|
||||
<span class="route-config-step-count">共 {{ editor.stepList.value.length }} 步</span>
|
||||
</div>
|
||||
<div class="route-config-center-dropzone">
|
||||
<draggable v-model="editor.stepList.value" handle=".route-config-step-handle" :animation="200"
|
||||
@end="editor.onStepDragEnd">
|
||||
<div v-for="(element, index) in editor.stepList.value" :key="element.id"
|
||||
class="route-config-step-card" :class="{ 'is-active': editor.selectedStepId.value === element.id }"
|
||||
@click="editor.selectStep(element)">
|
||||
<div class="route-config-step-handle"><el-icon><Rank /></el-icon></div>
|
||||
<div class="route-config-step-order">{{ index + 1 }}</div>
|
||||
<div class="route-config-step-info">
|
||||
<div class="route-config-step-name">{{ element.processName || '未命名步骤' }}</div>
|
||||
<div class="route-config-step-desc">{{ element.description || '' }}</div>
|
||||
</div>
|
||||
<el-button link type="primary" size="small" title="复制步骤"
|
||||
@click.stop="editor.handleCopyStep(element)">
|
||||
<el-icon><CopyDocument /></el-icon>
|
||||
</el-button>
|
||||
<el-button link type="danger" size="small" @click.stop="editor.handleDeleteStep(element)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
<el-collapse v-model="editor.expandedCategories.value" class="route-config-collapse">
|
||||
<el-collapse-item
|
||||
v-for="cat in editor.filteredProcessLibrary.value"
|
||||
:key="cat.categoryId"
|
||||
:name="cat.categoryId"
|
||||
>
|
||||
<template #title>
|
||||
<span class="route-config-cat-bar"></span>
|
||||
<span>{{ cat.categoryName }}</span>
|
||||
</template>
|
||||
<div
|
||||
v-for="(proc, procIndex) in cat.processes"
|
||||
:key="proc.id"
|
||||
class="route-config-process-card"
|
||||
draggable="true"
|
||||
@dragstart="editor.onDragStart($event, proc)"
|
||||
>
|
||||
<div class="route-config-process-info">
|
||||
<div class="route-config-process-name">
|
||||
<span class="route-config-process-seq">{{ procIndex + 1 }}</span
|
||||
>{{ proc.processName }}
|
||||
</div>
|
||||
</draggable>
|
||||
<div v-if="!editor.stepList.value.length" class="route-config-center-empty">
|
||||
<el-icon :size="32" color="#a7f3d0"><Plus /></el-icon>
|
||||
<p>从左侧拖拽工序到此处,添加任务步骤</p>
|
||||
<div class="route-config-process-code">{{ proc.processCode }}</div>
|
||||
</div>
|
||||
<el-icon class="route-config-process-drag"><Rank /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StepDetailPanel :editor="editor" />
|
||||
<el-empty v-if="!cat.processes.length" description="该分类暂无工序" :image-size="32" />
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<el-empty
|
||||
v-if="!editor.filteredProcessLibrary.value.length"
|
||||
description="暂无工艺数据"
|
||||
:image-size="48"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="route-config-center"
|
||||
v-loading="editor.stepListLoading.value"
|
||||
@dragover.prevent
|
||||
@drop="editor.onDropToCenter"
|
||||
>
|
||||
<div class="route-config-center-header">
|
||||
<h3>工序步骤</h3>
|
||||
<span class="route-config-step-count">共 {{ editor.stepList.value.length }} 步</span>
|
||||
</div>
|
||||
<div class="route-config-center-dropzone">
|
||||
<draggable
|
||||
v-model="editor.stepList.value"
|
||||
handle=".route-config-step-handle"
|
||||
:animation="200"
|
||||
@end="editor.onStepDragEnd"
|
||||
>
|
||||
<div
|
||||
v-for="(element, index) in editor.stepList.value"
|
||||
:key="element.id"
|
||||
class="route-config-step-card"
|
||||
:class="{ 'is-active': editor.selectedStepId.value === element.id }"
|
||||
@click="editor.selectStep(element)"
|
||||
>
|
||||
<div class="route-config-step-handle"
|
||||
><el-icon><Rank /></el-icon
|
||||
></div>
|
||||
<div class="route-config-step-order">{{ index + 1 }}</div>
|
||||
<div class="route-config-step-info">
|
||||
<div class="route-config-step-name">{{ element.processName || '未命名步骤' }}</div>
|
||||
<div class="route-config-step-desc">{{ element.description || '' }}</div>
|
||||
</div>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
title="复制步骤"
|
||||
@click.stop="editor.handleCopyStep(element)"
|
||||
>
|
||||
<el-icon><CopyDocument /></el-icon>
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
size="small"
|
||||
@click.stop="editor.handleDeleteStep(element)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</draggable>
|
||||
<div v-if="!editor.stepList.value.length" class="route-config-center-empty">
|
||||
<el-icon :size="32" color="#a7f3d0"><Plus /></el-icon>
|
||||
<p>从左侧拖拽工序到此处,添加任务步骤</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StepDetailPanel :editor="editor" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
@@ -160,7 +240,9 @@
|
||||
try {
|
||||
const res = await fetchProcessRouteList({ page: 1, pageSize: 100 })
|
||||
routeOptions.value = res.list || []
|
||||
} catch { routeOptions.value = [] }
|
||||
} catch {
|
||||
routeOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
@@ -193,25 +275,30 @@
|
||||
await editor.initEditor(task.id)
|
||||
ElMessage.success('任务已创建,可编辑步骤')
|
||||
emit('success')
|
||||
} catch { /* global error handling */ } finally {
|
||||
} catch {
|
||||
/* global error handling */
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.visible, async (val) => {
|
||||
if (val) {
|
||||
if (props.editTaskId) {
|
||||
// 编辑已有任务:跳过阶段一,直接进入阶段二
|
||||
createdTaskName.value = props.editTaskName || ''
|
||||
phase.value = 'edit'
|
||||
await editor.initEditor(props.editTaskId)
|
||||
} else {
|
||||
// 新建任务:走阶段一表单
|
||||
phase.value = 'form'
|
||||
loadRouteOptions()
|
||||
watch(
|
||||
() => props.visible,
|
||||
async (val) => {
|
||||
if (val) {
|
||||
if (props.editTaskId) {
|
||||
// 编辑已有任务:跳过阶段一,直接进入阶段二
|
||||
createdTaskName.value = props.editTaskName || ''
|
||||
phase.value = 'edit'
|
||||
await editor.initEditor(props.editTaskId)
|
||||
} else {
|
||||
// 新建任务:走阶段一表单
|
||||
phase.value = 'form'
|
||||
loadRouteOptions()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<!-- 编辑生产任务基本信息弹窗 -->
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="编辑任务基本信息"
|
||||
width="560px"
|
||||
destroy-on-close
|
||||
align-center
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="dialog-form-layout"
|
||||
>
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="任务名称" prop="taskName" class="form-col-span-2">
|
||||
<el-input v-model="form.taskName" placeholder="请输入任务名称" maxlength="200" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" class="form-col-span-2">
|
||||
<el-input
|
||||
v-model="form.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="可选,填写任务描述"
|
||||
maxlength="500"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" class="form-col-span-2">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="可选,填写备注"
|
||||
maxlength="512"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="handleClose">取消</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="submitting" @click="handleSubmit"
|
||||
>保存</el-button
|
||||
>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
fetchProductionTaskDetail,
|
||||
fetchUpdateProductionTask
|
||||
} from '@/api/production-task'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
taskId: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void
|
||||
(e: 'success'): void
|
||||
}>()
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: (val) => emit('update:visible', val)
|
||||
})
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const submitting = ref(false)
|
||||
const form = reactive({ taskName: '', description: '', remark: '' })
|
||||
const rules: FormRules = {
|
||||
taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
async function loadTaskDetail() {
|
||||
if (!props.taskId) return
|
||||
try {
|
||||
const res = await fetchProductionTaskDetail(props.taskId)
|
||||
form.taskName = res.taskName || ''
|
||||
form.description = res.description || ''
|
||||
form.remark = res.remark || ''
|
||||
} catch {
|
||||
/* global error handling */
|
||||
}
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
dialogVisible.value = false
|
||||
form.taskName = ''
|
||||
form.description = ''
|
||||
form.remark = ''
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!formRef.value) return
|
||||
await formRef.value.validate()
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
await fetchUpdateProductionTask(props.taskId, {
|
||||
task_name: form.taskName,
|
||||
description: form.description || undefined,
|
||||
remark: form.remark || undefined
|
||||
})
|
||||
ElMessage.success('任务信息已更新')
|
||||
emit('success')
|
||||
handleClose()
|
||||
} catch {
|
||||
/* global error handling */
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
async (val) => {
|
||||
if (val && props.taskId) {
|
||||
await loadTaskDetail()
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
@@ -6,13 +6,18 @@
|
||||
<h3>步骤详情</h3>
|
||||
<div v-if="!readonly" class="step-status-bar">
|
||||
<template v-if="editor.mode.value === 'execution' && editor.selectedStep.value">
|
||||
<el-tag :type="editor.stepStatusTag(editor.selectedStep.value.stepStatus || 'dzx').type" size="small">
|
||||
<el-tag
|
||||
:type="editor.stepStatusTag(editor.selectedStep.value.stepStatus || 'dzx').type"
|
||||
size="small"
|
||||
>
|
||||
{{ editor.stepStatusTag(editor.selectedStep.value.stepStatus || 'dzx').label }}
|
||||
</el-tag>
|
||||
<el-button
|
||||
v-if="editor.selectedStep.value.stepStatus === 'dzx'"
|
||||
class="btn-op-item primary"
|
||||
size="small"
|
||||
:loading="editor.stepActionLoading.value"
|
||||
:disabled="editor.manualSaveLoading.value"
|
||||
@click="editor.handleStartStep(editor.selectedStep.value)"
|
||||
>
|
||||
<el-icon><VideoPlay /></el-icon>执行此步骤
|
||||
@@ -22,6 +27,8 @@
|
||||
class="btn-op-item"
|
||||
size="small"
|
||||
type="success"
|
||||
:loading="editor.stepActionLoading.value"
|
||||
:disabled="editor.manualSaveLoading.value"
|
||||
@click="editor.handleCompleteStep(editor.selectedStep.value)"
|
||||
>
|
||||
<el-icon><Check /></el-icon>完成步骤
|
||||
@@ -32,6 +39,7 @@
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="editor.manualSaveLoading.value"
|
||||
:disabled="editor.stepActionLoading.value"
|
||||
@click="editor.handleManualSave"
|
||||
>
|
||||
<el-icon><Check /></el-icon>保存
|
||||
@@ -45,9 +53,17 @@
|
||||
<el-input v-model="editor.selectedStep.value.processName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="工序分类">
|
||||
<el-select v-model="editor.selectedStep.value.categoryId" placeholder="选择分类" clearable>
|
||||
<el-option v-for="cat in editor.categoryOptions.value" :key="cat.id"
|
||||
:label="cat.categoryName || cat.name" :value="cat.id" />
|
||||
<el-select
|
||||
v-model="editor.selectedStep.value.categoryId"
|
||||
placeholder="选择分类"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="cat in editor.categoryOptions.value"
|
||||
:key="cat.id"
|
||||
:label="cat.categoryName || cat.name"
|
||||
:value="cat.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备名称">
|
||||
@@ -57,14 +73,27 @@
|
||||
<el-input v-model="editor.selectedStep.value.estimatedDuration" placeholder="如 2h" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" class="step-form-col-half">
|
||||
<el-input v-model="editor.selectedStep.value.description" type="textarea" :rows="2"
|
||||
placeholder="工序描述" />
|
||||
<el-input
|
||||
v-model="editor.selectedStep.value.description"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="工序描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="关联SOP" class="step-form-col-half">
|
||||
<el-select v-model="editor.sopIdsArr.value" multiple placeholder="选择SOP文档" filterable
|
||||
@change="editor.onSopIdsChange">
|
||||
<el-option v-for="sop in editor.sopOptions.value" :key="sop.id"
|
||||
:label="sop.sopName || sop.name" :value="sop.id" />
|
||||
<el-select
|
||||
v-model="editor.sopIdsArr.value"
|
||||
multiple
|
||||
placeholder="选择SOP文档"
|
||||
filterable
|
||||
@change="editor.onSopIdsChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="sop in editor.sopOptions.value"
|
||||
:key="sop.id"
|
||||
:label="sop.sopName || sop.name"
|
||||
:value="sop.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div
|
||||
@@ -87,8 +116,19 @@
|
||||
<el-icon><Plus /></el-icon>添加试剂
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="editor.selectedStep.value.reagents" stripe class="inner-table" max-height="300">
|
||||
<el-table-column type="index" label="序号" width="56" align="center" :index="(i: number) => i + 1" />
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.reagents"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
width="56"
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="试剂编码" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.reagentCode" placeholder="编码" class="inner-cell-input" />
|
||||
@@ -96,12 +136,25 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="试剂名称" min-width="160" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.reagentId" placeholder="请选择试剂" clearable filterable remote
|
||||
reserve-keyword class="inner-cell-select" :loading="editor.reagentOptionsLoading.value"
|
||||
<el-select
|
||||
v-model="row.reagentId"
|
||||
placeholder="请选择试剂"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
class="inner-cell-select"
|
||||
:loading="editor.reagentOptionsLoading.value"
|
||||
:remote-method="editor.searchReagentOptions"
|
||||
@visible-change="(vis: boolean) => vis && editor.searchReagentOptions('')"
|
||||
@change="(val: string) => editor.onReagentRowSelect(row, val)">
|
||||
<el-option v-for="item in editor.reagentOptions.value" :key="item.id" :label="item.label" :value="item.id" />
|
||||
@change="(val: string) => editor.onReagentRowSelect(row, val)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in editor.reagentOptions.value"
|
||||
:key="item.id"
|
||||
:label="item.label"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -112,21 +165,48 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="标准用量" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.standardUsage" placeholder="用量" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-model="row.standardUsage"
|
||||
placeholder="用量"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="editor.mode.value === 'execution'" label="实际用量" min-width="110" align="center">
|
||||
<el-table-column
|
||||
v-if="editor.mode.value === 'execution'"
|
||||
label="实际用量"
|
||||
min-width="110"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.actualUsage" placeholder="实际用量" class="inner-cell-input" @change="editor.saveReagents" />
|
||||
<el-input
|
||||
v-model="row.actualUsage"
|
||||
placeholder="实际用量"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveReagents"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.unit" placeholder="请选择单位" clearable filterable remote
|
||||
reserve-keyword class="inner-cell-select" :loading="editor.itemUnitOptionsLoading.value"
|
||||
<el-select
|
||||
v-model="row.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
class="inner-cell-select"
|
||||
:loading="editor.itemUnitOptionsLoading.value"
|
||||
:remote-method="editor.searchItemUnitOptions"
|
||||
@visible-change="(vis: boolean) => vis && editor.searchItemUnitOptions('')">
|
||||
<el-option v-for="item in editor.itemUnitOptions.value" :key="item.value" :label="item.label" :value="item.value" />
|
||||
@visible-change="(vis: boolean) => vis && editor.searchItemUnitOptions('')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in editor.itemUnitOptions.value"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -142,59 +222,133 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" size="small" @click="editor.removeReagentRow($index)">删除</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="editor.removeReagentRow($index)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!editor.selectedStep.value.reagents?.length" description="暂无试剂" :image-size="48" />
|
||||
<el-empty
|
||||
v-if="!editor.selectedStep.value.reagents?.length"
|
||||
description="暂无试剂"
|
||||
:image-size="48"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="耗材" name="consumable">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<el-button class="btn-main" type="primary" size="small" @click="editor.addConsumableRow">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="editor.addConsumableRow"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>添加耗材
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="editor.selectedStep.value.consumables" stripe class="inner-table" max-height="300">
|
||||
<el-table-column type="index" label="序号" width="56" align="center" :index="(i: number) => i + 1" />
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.consumables"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
width="56"
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="耗材编码" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.consumableCode" placeholder="编码" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-model="row.consumableCode"
|
||||
placeholder="编码"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="耗材名称" min-width="160" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.consumableId" placeholder="请选择耗材" clearable filterable remote
|
||||
reserve-keyword class="inner-cell-select" :loading="editor.consumableOptionsLoading.value"
|
||||
<el-select
|
||||
v-model="row.consumableId"
|
||||
placeholder="请选择耗材"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
class="inner-cell-select"
|
||||
:loading="editor.consumableOptionsLoading.value"
|
||||
:remote-method="editor.searchConsumableOptions"
|
||||
@visible-change="(vis: boolean) => vis && editor.searchConsumableOptions('')"
|
||||
@change="(val: string) => editor.onConsumableRowSelect(row, val)">
|
||||
<el-option v-for="item in editor.consumableOptions.value" :key="item.id" :label="item.label" :value="item.id" />
|
||||
@change="(val: string) => editor.onConsumableRowSelect(row, val)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in editor.consumableOptions.value"
|
||||
:key="item.id"
|
||||
:label="item.label"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格型号" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.specModel" placeholder="规格型号" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-model="row.specModel"
|
||||
placeholder="规格型号"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标准用量" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.standardUsage" placeholder="用量" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-model="row.standardUsage"
|
||||
placeholder="用量"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="editor.mode.value === 'execution'" label="实际用量" min-width="110" align="center">
|
||||
<el-table-column
|
||||
v-if="editor.mode.value === 'execution'"
|
||||
label="实际用量"
|
||||
min-width="110"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.actualUsage" placeholder="实际用量" class="inner-cell-input" @change="editor.saveConsumables" />
|
||||
<el-input
|
||||
v-model="row.actualUsage"
|
||||
placeholder="实际用量"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveConsumables"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.unit" placeholder="请选择单位" clearable filterable remote
|
||||
reserve-keyword class="inner-cell-select" :loading="editor.itemUnitOptionsLoading.value"
|
||||
<el-select
|
||||
v-model="row.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
class="inner-cell-select"
|
||||
:loading="editor.itemUnitOptionsLoading.value"
|
||||
:remote-method="editor.searchItemUnitOptions"
|
||||
@visible-change="(vis: boolean) => vis && editor.searchItemUnitOptions('')">
|
||||
<el-option v-for="item in editor.itemUnitOptions.value" :key="item.value" :label="item.label" :value="item.value" />
|
||||
@visible-change="(vis: boolean) => vis && editor.searchItemUnitOptions('')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in editor.itemUnitOptions.value"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -210,11 +364,21 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" size="small" @click="editor.removeConsumableRow($index)">删除</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="editor.removeConsumableRow($index)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!editor.selectedStep.value.consumables?.length" description="暂无耗材" :image-size="48" />
|
||||
<el-empty
|
||||
v-if="!editor.selectedStep.value.consumables?.length"
|
||||
description="暂无耗材"
|
||||
:image-size="48"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="产出物" name="output">
|
||||
@@ -223,87 +387,182 @@
|
||||
<el-icon><Plus /></el-icon>添加产出物
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="editor.selectedStep.value.outputs" stripe class="inner-table" max-height="300">
|
||||
<el-table-column type="index" label="序号" width="56" align="center" :index="(i: number) => i + 1" />
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.outputs"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
width="56"
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="产出类型" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.outputTypeId" placeholder="选择类型" filterable class="inner-cell-select"
|
||||
@change="editor.saveOutputs">
|
||||
<el-option v-for="ot in editor.outputTypeOptions.value" :key="ot.id"
|
||||
:label="ot.typeName || ot.name" :value="ot.id" />
|
||||
<el-select
|
||||
v-model="row.outputTypeId"
|
||||
placeholder="选择类型"
|
||||
filterable
|
||||
class="inner-cell-select"
|
||||
@change="editor.saveOutputs"
|
||||
>
|
||||
<el-option
|
||||
v-for="ot in editor.outputTypeOptions.value"
|
||||
:key="ot.id"
|
||||
:label="ot.typeName || ot.name"
|
||||
:value="ot.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.outputName" placeholder="名称" class="inner-cell-input"
|
||||
@change="editor.saveOutputs" />
|
||||
<el-input
|
||||
v-model="row.outputName"
|
||||
placeholder="名称"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveOutputs"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="条码" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.outputBarcode" placeholder="条码" class="inner-cell-input"
|
||||
@change="editor.saveOutputs" />
|
||||
<el-input
|
||||
v-model="row.outputBarcode"
|
||||
placeholder="条码"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveOutputs"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.remark" placeholder="备注" class="inner-cell-input"
|
||||
@change="editor.saveOutputs" />
|
||||
<el-input
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveOutputs"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" size="small" @click="editor.removeOutputRow($index)">删除</el-button>
|
||||
<el-button link type="danger" size="small" @click="editor.removeOutputRow($index)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!editor.selectedStep.value.outputs?.length" description="暂无产出物" :image-size="48" />
|
||||
<el-empty
|
||||
v-if="!editor.selectedStep.value.outputs?.length"
|
||||
description="暂无产出物"
|
||||
:image-size="48"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="数据采集" name="dataCollection">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<el-button class="btn-main" type="primary" size="small" @click="editor.addDataCollectionRow">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="editor.addDataCollectionRow"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>添加采集项
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="editor.selectedStep.value.dataCollections" stripe class="inner-table" max-height="300">
|
||||
<el-table-column type="index" label="序号" width="56" align="center" :index="(i: number) => i + 1" />
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.dataCollections"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
width="56"
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="采集名称" min-width="180" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.collectionName" placeholder="采集项名称" class="inner-cell-input"
|
||||
@change="editor.saveDataCollections" />
|
||||
<el-input
|
||||
v-model="row.collectionName"
|
||||
placeholder="采集项名称"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveDataCollections"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.unit" placeholder="请选择单位" clearable filterable remote
|
||||
reserve-keyword class="inner-cell-select" :loading="editor.itemUnitOptionsLoading.value"
|
||||
<el-select
|
||||
v-model="row.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
class="inner-cell-select"
|
||||
:loading="editor.itemUnitOptionsLoading.value"
|
||||
:remote-method="editor.searchItemUnitOptions"
|
||||
@visible-change="(vis: boolean) => vis && editor.searchItemUnitOptions('')"
|
||||
@change="editor.saveDataCollections">
|
||||
<el-option v-for="item in editor.itemUnitOptions.value" :key="item.value" :label="item.label" :value="item.value" />
|
||||
@change="editor.saveDataCollections"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in editor.itemUnitOptions.value"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="editor.mode.value === 'execution'" label="采集值" min-width="140" align="center">
|
||||
<el-table-column
|
||||
v-if="editor.mode.value === 'execution'"
|
||||
label="采集值"
|
||||
min-width="140"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.collectionValue" placeholder="输入采集值" class="inner-cell-input" @change="editor.saveDataCollections" />
|
||||
<el-input
|
||||
v-model="row.collectionValue"
|
||||
placeholder="输入采集值"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveDataCollections"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.remark" placeholder="备注" class="inner-cell-input"
|
||||
@change="editor.saveDataCollections" />
|
||||
<el-input
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveDataCollections"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" size="small" @click="editor.removeDataCollectionRow($index)">删除</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="editor.removeDataCollectionRow($index)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!editor.selectedStep.value.dataCollections?.length" description="暂无采集项" :image-size="48" />
|
||||
<el-empty
|
||||
v-if="!editor.selectedStep.value.dataCollections?.length"
|
||||
description="暂无采集项"
|
||||
:image-size="48"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -364,8 +623,7 @@
|
||||
files.push({
|
||||
id: f.id || `${sop.id}_${f.fileUrl}`,
|
||||
fileName:
|
||||
f.fileName ||
|
||||
decodeURIComponent(String(f.fileUrl).split('/').pop() || '文档'),
|
||||
f.fileName || decodeURIComponent(String(f.fileUrl).split('/').pop() || '文档'),
|
||||
fileUrl: f.fileUrl,
|
||||
fileType: f.fileType || ''
|
||||
})
|
||||
|
||||
@@ -12,9 +12,15 @@
|
||||
<!-- 顶部:任务基本信息一行展示 -->
|
||||
<div v-if="editor.taskDetail.value" class="execution-header">
|
||||
<div class="execution-header-row">
|
||||
<span class="execution-header-item">任务号:<b>{{ editor.taskDetail.value.taskNo }}</b></span>
|
||||
<span class="execution-header-item">工单号:{{ editor.taskDetail.value.workOrderNo || '—' }}</span>
|
||||
<span class="execution-header-item">任务名称:{{ editor.taskDetail.value.taskName || '—' }}</span>
|
||||
<span class="execution-header-item"
|
||||
>任务号:<b>{{ editor.taskDetail.value.taskNo }}</b></span
|
||||
>
|
||||
<span class="execution-header-item"
|
||||
>工单号:{{ editor.taskDetail.value.workOrderNo || '—' }}</span
|
||||
>
|
||||
<span class="execution-header-item"
|
||||
>任务名称:{{ editor.taskDetail.value.taskName || '—' }}</span
|
||||
>
|
||||
<el-tag :type="editor.taskStatusTag(editor.taskDetail.value.taskStatus).type">
|
||||
{{ editor.taskStatusTag(editor.taskDetail.value.taskStatus).label }}
|
||||
</el-tag>
|
||||
@@ -24,6 +30,7 @@
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="editor.taskActionLoading.value"
|
||||
@click="editor.handleStartTask"
|
||||
>
|
||||
<el-icon><VideoPlay /></el-icon>开始执行
|
||||
@@ -33,13 +40,16 @@
|
||||
class="btn-main"
|
||||
type="success"
|
||||
size="small"
|
||||
:loading="editor.taskActionLoading.value"
|
||||
@click="editor.handleCompleteTask"
|
||||
>
|
||||
<el-icon><Check /></el-icon>完成任务
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="editor.taskDetail.value.remark" class="execution-header-remark">{{ editor.taskDetail.value.remark }}</div>
|
||||
<div v-if="editor.taskDetail.value.remark" class="execution-header-remark">{{
|
||||
editor.taskDetail.value.remark
|
||||
}}</div>
|
||||
</div>
|
||||
|
||||
<!-- 主体:可折叠工艺库 + 步骤列表 + 详情面板 -->
|
||||
@@ -48,44 +58,79 @@
|
||||
<template v-if="!readonly">
|
||||
<transition name="slide-lib">
|
||||
<div v-show="editor.processLibraryExpanded.value" class="execution-lib-panel">
|
||||
<div class="execution-lib-header">
|
||||
<h3>工艺库</h3>
|
||||
<el-input v-model="editor.processSearchKeyword.value" placeholder="搜索工序" clearable size="small" class="route-config-search" />
|
||||
</div>
|
||||
<el-collapse v-model="editor.expandedCategories.value" class="route-config-collapse">
|
||||
<el-collapse-item v-for="cat in editor.filteredProcessLibrary.value" :key="cat.categoryId" :name="cat.categoryId">
|
||||
<template #title>
|
||||
<span class="route-config-cat-bar"></span>
|
||||
<span>{{ cat.categoryName }}</span>
|
||||
</template>
|
||||
<div v-for="(proc, procIndex) in cat.processes" :key="proc.id" class="route-config-process-card" draggable="true" @dragstart="editor.onDragStart($event, proc)">
|
||||
<div class="route-config-process-info">
|
||||
<div class="route-config-process-name">
|
||||
<span class="route-config-process-seq">{{ procIndex + 1 }}</span>{{ proc.processName }}
|
||||
<div class="execution-lib-header">
|
||||
<h3>工艺库</h3>
|
||||
<el-input
|
||||
v-model="editor.processSearchKeyword.value"
|
||||
placeholder="搜索工序"
|
||||
clearable
|
||||
size="small"
|
||||
class="route-config-search"
|
||||
/>
|
||||
</div>
|
||||
<el-collapse v-model="editor.expandedCategories.value" class="route-config-collapse">
|
||||
<el-collapse-item
|
||||
v-for="cat in editor.filteredProcessLibrary.value"
|
||||
:key="cat.categoryId"
|
||||
:name="cat.categoryId"
|
||||
>
|
||||
<template #title>
|
||||
<span class="route-config-cat-bar"></span>
|
||||
<span>{{ cat.categoryName }}</span>
|
||||
</template>
|
||||
<div
|
||||
v-for="(proc, procIndex) in cat.processes"
|
||||
:key="proc.id"
|
||||
class="route-config-process-card"
|
||||
draggable="true"
|
||||
@dragstart="editor.onDragStart($event, proc)"
|
||||
>
|
||||
<div class="route-config-process-info">
|
||||
<div class="route-config-process-name">
|
||||
<span class="route-config-process-seq">{{ procIndex + 1 }}</span
|
||||
>{{ proc.processName }}
|
||||
</div>
|
||||
<div class="route-config-process-code">{{ proc.processCode }}</div>
|
||||
</div>
|
||||
<div class="route-config-process-code">{{ proc.processCode }}</div>
|
||||
<el-icon class="route-config-process-drag"><Rank /></el-icon>
|
||||
</div>
|
||||
<el-icon class="route-config-process-drag"><Rank /></el-icon>
|
||||
</div>
|
||||
<el-empty v-if="!cat.processes.length" description="该分类暂无工序" :image-size="32" />
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<el-empty v-if="!editor.filteredProcessLibrary.value.length" description="暂无工艺数据" :image-size="48" />
|
||||
</div>
|
||||
</transition>
|
||||
<el-empty
|
||||
v-if="!cat.processes.length"
|
||||
description="该分类暂无工序"
|
||||
:image-size="32"
|
||||
/>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<el-empty
|
||||
v-if="!editor.filteredProcessLibrary.value.length"
|
||||
description="暂无工艺数据"
|
||||
:image-size="48"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 工艺库折叠把手 -->
|
||||
<div class="execution-lib-toggle" @click="editor.processLibraryExpanded.value = !editor.processLibraryExpanded.value">
|
||||
<span>{{ editor.processLibraryExpanded.value ? '‹' : '›' }}</span>
|
||||
<span class="execution-lib-toggle-text">工艺库</span>
|
||||
</div>
|
||||
<!-- 工艺库折叠把手 -->
|
||||
<div
|
||||
class="execution-lib-toggle"
|
||||
@click="editor.processLibraryExpanded.value = !editor.processLibraryExpanded.value"
|
||||
>
|
||||
<span>{{ editor.processLibraryExpanded.value ? '‹' : '›' }}</span>
|
||||
<span class="execution-lib-toggle-text">工艺库</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 步骤列表 -->
|
||||
<div class="execution-step-list" v-loading="editor.stepListLoading.value" @dragover.prevent @drop="editor.onDropToCenter">
|
||||
<div
|
||||
class="execution-step-list"
|
||||
v-loading="editor.stepListLoading.value"
|
||||
@dragover.prevent
|
||||
@drop="editor.onDropToCenter"
|
||||
>
|
||||
<div class="execution-step-list-header">
|
||||
<h3>工序步骤</h3>
|
||||
<span class="execution-progress-badge">{{ editor.completedStepCount.value }}/{{ editor.stepList.value.length }}</span>
|
||||
<span class="execution-progress-badge"
|
||||
>{{ editor.completedStepCount.value }}/{{ editor.stepList.value.length }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="execution-step-list-body">
|
||||
<!-- 虚拟步骤:任务输入 -->
|
||||
@@ -96,9 +141,11 @@
|
||||
>
|
||||
<div class="virtual-step-icon virtual-step-icon--input">📥</div>
|
||||
<div class="virtual-step-info">
|
||||
<div class="virtual-step-name">任务输入</div>
|
||||
<div class="virtual-step-name">原材料(样本/细胞产品)</div>
|
||||
</div>
|
||||
<el-tag v-if="editor.virtualInputStatus.value === 'filled'" type="success" size="small">已填写 {{ editor.taskInputs.value.length }} 项</el-tag>
|
||||
<el-tag v-if="editor.virtualInputStatus.value === 'filled'" type="success" size="small"
|
||||
>已填写 {{ editor.taskInputs.value.length }} 项</el-tag
|
||||
>
|
||||
<el-tag v-else type="danger" size="small">未填写</el-tag>
|
||||
</div>
|
||||
|
||||
@@ -110,9 +157,11 @@
|
||||
>
|
||||
<div class="virtual-step-icon virtual-step-icon--output">📤</div>
|
||||
<div class="virtual-step-info">
|
||||
<div class="virtual-step-name">任务输出</div>
|
||||
<div class="virtual-step-name">产出(细胞产品)</div>
|
||||
</div>
|
||||
<el-tag v-if="editor.virtualOutputStatus.value === 'filled'" type="success" size="small">已填写 {{ editor.taskOutputs.value.length }} 项</el-tag>
|
||||
<el-tag v-if="editor.virtualOutputStatus.value === 'filled'" type="success" size="small"
|
||||
>已填写 {{ editor.taskOutputs.value.length }} 项</el-tag
|
||||
>
|
||||
<el-tag v-else type="danger" size="small">未填写</el-tag>
|
||||
</div>
|
||||
|
||||
@@ -120,7 +169,12 @@
|
||||
<div class="step-divider"><span>工艺步骤</span></div>
|
||||
|
||||
<!-- 真实步骤列表 -->
|
||||
<draggable v-model="editor.stepList.value" :handle="readonly ? undefined : '.route-config-step-handle'" :animation="200" @end="editor.onStepDragEnd">
|
||||
<draggable
|
||||
v-model="editor.stepList.value"
|
||||
:handle="readonly ? undefined : '.route-config-step-handle'"
|
||||
:animation="200"
|
||||
@end="editor.onStepDragEnd"
|
||||
>
|
||||
<div
|
||||
v-for="(element, index) in editor.stepList.value"
|
||||
:key="element.id"
|
||||
@@ -131,18 +185,39 @@
|
||||
}"
|
||||
@click="editor.selectStep(element)"
|
||||
>
|
||||
<div v-if="!readonly" class="route-config-step-handle"><el-icon><Rank /></el-icon></div>
|
||||
<div class="route-config-step-order" :class="stepSeqClass(element.stepStatus)">{{ element.stepStatus === 'ywc' ? '✓' : index + 1 }}</div>
|
||||
<div v-if="!readonly" class="route-config-step-handle"
|
||||
><el-icon><Rank /></el-icon
|
||||
></div>
|
||||
<div class="route-config-step-order" :class="stepSeqClass(element.stepStatus)">{{
|
||||
element.stepStatus === 'ywc' ? '✓' : index + 1
|
||||
}}</div>
|
||||
<div class="route-config-step-info">
|
||||
<div class="route-config-step-name">{{ element.processName || '未命名步骤' }}</div>
|
||||
<div class="route-config-step-desc">{{ element.description || '' }}</div>
|
||||
</div>
|
||||
<el-tag v-if="element.stepStatus" :type="editor.stepStatusTag(element.stepStatus).type" size="small" style="margin-right:4px">{{ editor.stepStatusTag(element.stepStatus).label }}</el-tag>
|
||||
<el-tag
|
||||
v-if="element.stepStatus"
|
||||
:type="editor.stepStatusTag(element.stepStatus).type"
|
||||
size="small"
|
||||
style="margin-right: 4px"
|
||||
>{{ editor.stepStatusTag(element.stepStatus).label }}</el-tag
|
||||
>
|
||||
<template v-if="!readonly">
|
||||
<el-button link type="primary" size="small" title="复制步骤" @click.stop="editor.handleCopyStep(element)">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
title="复制步骤"
|
||||
@click.stop="editor.handleCopyStep(element)"
|
||||
>
|
||||
<el-icon><CopyDocument /></el-icon>
|
||||
</el-button>
|
||||
<el-button link type="danger" size="small" @click.stop="editor.handleDeleteStep(element)">
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
size="small"
|
||||
@click.stop="editor.handleDeleteStep(element)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -161,116 +236,62 @@
|
||||
<!-- 任务输入详情 -->
|
||||
<template v-if="editor.selectedVirtualStep.value === 'input'">
|
||||
<div class="execution-detail-header">
|
||||
<h3>📥 任务输入</h3>
|
||||
<h3>📥 原材料(样本/细胞产品)<span class="virtual-step-required">必填</span></h3>
|
||||
<div v-if="!readonly" class="execution-detail-actions">
|
||||
<el-button class="btn-main" type="primary" size="small" @click="editor.addTaskInputRow"><el-icon><Plus /></el-icon>添加输入</el-button>
|
||||
<el-button class="btn-main" type="primary" size="small" :loading="editor.inputSaving.value" @click="editor.saveTaskInputs"><el-icon><Check /></el-icon>保存</el-button>
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="editor.inputSaving.value"
|
||||
@click="editor.saveTaskInputs"
|
||||
><el-icon><Check /></el-icon>保存</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="execution-detail-body">
|
||||
<el-table :data="editor.taskInputs.value" stripe class="inner-table">
|
||||
<el-table-column type="index" label="序号" width="56" align="center" :index="(i: number) => i + 1" />
|
||||
<el-table-column label="输入类型" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="readonly">{{ row.inputType === 'sample' ? '样本' : row.inputType === 'cell_product' ? '细胞产品' : row.inputType }}</span>
|
||||
<el-select v-else v-model="row.inputType" placeholder="选择类型" class="inner-cell-select" @change="() => { row.inputId = ''; row.inputCode = ''; row.inputName = '' }">
|
||||
<el-option label="样本" value="sample" />
|
||||
<el-option label="细胞产品" value="cell_product" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="输入编码" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.inputType === 'cell_product'">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.inputId"
|
||||
placeholder="选择细胞产品"
|
||||
filterable
|
||||
class="inner-cell-select"
|
||||
:loading="editor.cellProductOptionsLoading.value"
|
||||
@change="(val: string) => editor.onCellProductInputSelect(row, val)"
|
||||
>
|
||||
<el-option v-for="opt in editor.cellProductOptions.value" :key="opt.id" :label="opt.label" :value="opt.id" />
|
||||
</el-select>
|
||||
<span v-else>{{ row.inputCode || '—' }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>{{ row.inputCode || '—' }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="输入名称" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="readonly">{{ row.inputName || '—' }}</span>
|
||||
<el-input v-else v-model="row.inputName" placeholder="输入名称" class="inner-cell-input" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="readonly">{{ row.remark || '—' }}</span>
|
||||
<el-input v-else v-model="row.remark" placeholder="备注" class="inner-cell-input" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" size="small" @click="editor.removeTaskInputRow($index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!editor.taskInputs.value.length" description="暂无输入项" :image-size="48" />
|
||||
<TaskInputEnhancedTable
|
||||
:items="editor.taskInputs.value"
|
||||
:loading="false"
|
||||
:disabled="readonly"
|
||||
:sample-code="editor.taskDetail.value?.sampleCode || ''"
|
||||
:cell-product-options="editor.cellProductOptions.value"
|
||||
:cell-product-options-loading="editor.cellProductOptionsLoading.value"
|
||||
@update:items="(items: Record<string, any>[]) => (editor.taskInputs.value = items)"
|
||||
@search-cell-product="(q: string) => editor.searchCellProductOptions(q)"
|
||||
@save="editor.saveTaskInputs"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 任务输出详情 -->
|
||||
<template v-else-if="editor.selectedVirtualStep.value === 'output'">
|
||||
<div class="execution-detail-header">
|
||||
<h3>📤 任务输出</h3>
|
||||
<h3>📤 产出(细胞产品)<span class="virtual-step-required">必填</span></h3>
|
||||
<div v-if="!readonly" class="execution-detail-actions">
|
||||
<el-button class="btn-main" type="primary" size="small" @click="editor.addTaskOutputRow"><el-icon><Plus /></el-icon>添加输出</el-button>
|
||||
<el-button class="btn-main" type="primary" size="small" :loading="editor.outputSaving.value" @click="editor.saveTaskOutputs"><el-icon><Check /></el-icon>保存</el-button>
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="editor.outputSaving.value"
|
||||
@click="editor.saveTaskOutputs"
|
||||
><el-icon><Check /></el-icon>保存</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="execution-detail-body">
|
||||
<el-table :data="editor.taskOutputs.value" stripe class="inner-table">
|
||||
<el-table-column type="index" label="序号" width="56" align="center" :index="(i: number) => i + 1" />
|
||||
<el-table-column label="输出名称" min-width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="readonly">{{ row.outputName || '—' }}</span>
|
||||
<el-input v-else v-model="row.outputName" placeholder="输出名称" class="inner-cell-input" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="输出条码" min-width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="readonly">{{ row.outputBarcode || '—' }}</span>
|
||||
<el-input v-else v-model="row.outputBarcode" placeholder="扫码或输入" class="inner-cell-input" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="readonly">{{ row.quantity ?? '—' }}</span>
|
||||
<el-input-number v-else v-model="row.quantity" :min="0" :precision="2" controls-position="right" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="readonly">{{ row.unit || '—' }}</span>
|
||||
<el-input v-else v-model="row.unit" placeholder="单位" class="inner-cell-input" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="readonly">{{ row.remark || '—' }}</span>
|
||||
<el-input v-else v-model="row.remark" placeholder="备注" class="inner-cell-input" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" size="small" @click="editor.removeTaskOutputRow($index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!editor.taskOutputs.value.length" description="暂无输出项" :image-size="48" />
|
||||
<TaskOutputList
|
||||
:items="editor.taskOutputs.value"
|
||||
:loading="false"
|
||||
:product-type-options="editor.productTypeOptions.value"
|
||||
:spec-options="editor.specOptions.value"
|
||||
:storage-slot-options="editor.storageSlotOptions.value"
|
||||
:unit-options="editor.itemUnitOptions.value"
|
||||
:unit-options-loading="editor.itemUnitOptionsLoading.value"
|
||||
:disabled="readonly"
|
||||
@update:items="(items: Record<string, any>[]) => (editor.taskOutputs.value = items)"
|
||||
@search-unit="(q: string) => editor.searchItemUnitOptions(q)"
|
||||
@save="editor.saveTaskOutputs"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -288,6 +309,8 @@
|
||||
import { VueDraggable as draggable } from 'vue-draggable-plus'
|
||||
import { useTaskStepEditor } from './useTaskStepEditor'
|
||||
import StepDetailPanel from './StepDetailPanel.vue'
|
||||
import TaskInputEnhancedTable from './TaskInputEnhancedTable.vue'
|
||||
import TaskOutputList from './TaskOutputList.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
@@ -319,11 +342,14 @@
|
||||
return 'seq-pending'
|
||||
}
|
||||
|
||||
watch(() => props.visible, async (val) => {
|
||||
if (val && props.taskId) {
|
||||
await editor.initEditor(props.taskId, { mode: 'execution' })
|
||||
watch(
|
||||
() => props.visible,
|
||||
async (val) => {
|
||||
if (val && props.taskId) {
|
||||
await editor.initEditor(props.taskId, { mode: 'execution' })
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -349,135 +375,280 @@
|
||||
border: 1px solid #d1fae5;
|
||||
}
|
||||
.execution-header-row {
|
||||
display: flex; align-items: center; gap: 16px;
|
||||
font-size: 13px; color: #475569;
|
||||
b { color: #1e293b; }
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
font-size: 13px;
|
||||
color: #475569;
|
||||
b {
|
||||
color: #1e293b;
|
||||
}
|
||||
}
|
||||
.execution-header-item {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.execution-header-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.execution-header-remark {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
.execution-header-item { white-space: nowrap; }
|
||||
.execution-header-actions { display: flex; gap: 8px; margin-left: auto; }
|
||||
.execution-header-remark { margin-top: 4px; font-size: 12px; color: #64748b; }
|
||||
|
||||
/* 主体三区域 */
|
||||
.execution-main {
|
||||
display: flex; gap: 0;
|
||||
display: flex;
|
||||
gap: 0;
|
||||
height: calc(96vh - 110px);
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
/* 工艺库折叠侧栏 */
|
||||
.execution-lib-panel {
|
||||
width: 260px; flex-shrink: 0;
|
||||
display: flex; flex-direction: column;
|
||||
border: 1px solid #e5e7eb; border-radius: 8px;
|
||||
width: 260px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.execution-lib-header {
|
||||
padding: 10px 12px; border-bottom: 1px solid #e5e7eb; flex-shrink: 0;
|
||||
h3 { margin: 0 0 6px; font-size: 13px; font-weight: 600; color: #1f2937; }
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
flex-shrink: 0;
|
||||
h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
}
|
||||
|
||||
.slide-lib-enter-active, .slide-lib-leave-active {
|
||||
transition: width 0.2s ease, opacity 0.2s ease;
|
||||
.slide-lib-enter-active,
|
||||
.slide-lib-leave-active {
|
||||
transition:
|
||||
width 0.2s ease,
|
||||
opacity 0.2s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
.slide-lib-enter-from, .slide-lib-leave-to {
|
||||
width: 0 !important; opacity: 0;
|
||||
.slide-lib-enter-from,
|
||||
.slide-lib-leave-to {
|
||||
width: 0 !important;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* 工艺库折叠把手 */
|
||||
.execution-lib-toggle {
|
||||
width: 24px; flex-shrink: 0;
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
gap: 4px; cursor: pointer;
|
||||
background: #f9fafb; border-left: 1px solid #e5e7eb; border-right: 1px solid #e5e7eb;
|
||||
width: 24px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
background: #f9fafb;
|
||||
border-left: 1px solid #e5e7eb;
|
||||
border-right: 1px solid #e5e7eb;
|
||||
transition: background 0.15s;
|
||||
font-size: 14px; color: #9ca3af;
|
||||
&:hover { background: #ecfdf5; color: #10b981; }
|
||||
font-size: 14px;
|
||||
color: #9ca3af;
|
||||
&:hover {
|
||||
background: #ecfdf5;
|
||||
color: #10b981;
|
||||
}
|
||||
}
|
||||
.execution-lib-toggle-text {
|
||||
writing-mode: vertical-lr; font-size: 11px; letter-spacing: 2px;
|
||||
writing-mode: vertical-lr;
|
||||
font-size: 11px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* 步骤列表 */
|
||||
.execution-step-list {
|
||||
width: 340px; flex-shrink: 0;
|
||||
display: flex; flex-direction: column;
|
||||
border: 1px solid #e5e7eb; border-radius: 8px;
|
||||
overflow: hidden; margin: 0 8px;
|
||||
width: 340px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin: 0 8px;
|
||||
}
|
||||
.execution-step-list-header {
|
||||
padding: 10px 14px; border-bottom: 1px solid #e5e7eb;
|
||||
display: flex; align-items: center; justify-content: space-between; flex-shrink: 0;
|
||||
h3 { margin: 0; font-size: 14px; font-weight: 600; color: #1f2937; }
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
}
|
||||
.execution-progress-badge {
|
||||
font-size: 12px; color: #6b7280; background: #f3f4f6;
|
||||
padding: 2px 10px; border-radius: 10px;
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
background: #f3f4f6;
|
||||
padding: 2px 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.execution-step-list-body {
|
||||
flex: 1; overflow-y: auto; padding: 8px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 8px;
|
||||
}
|
||||
.execution-step-list-empty {
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
padding: 40px 0; color: #9ca3af;
|
||||
p { margin-top: 8px; font-size: 13px; }
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 0;
|
||||
color: #9ca3af;
|
||||
p {
|
||||
margin-top: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 虚拟步骤卡片 */
|
||||
/* 虚拟步骤卡片(原材料 / 产出 — 红色系强调必填) */
|
||||
.virtual-step-card {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 10px 12px; margin-bottom: 4px;
|
||||
border-radius: 8px; cursor: pointer;
|
||||
border: 2px solid #bfdbfe; background: #eff6ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 4px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
border: 2px solid #fecaca;
|
||||
background: #fef2f2;
|
||||
transition: all 0.15s;
|
||||
&:hover { border-color: #93c5fd; background: #dbeafe; }
|
||||
&.is-active { border-color: #3b82f6; background: #dbeafe; box-shadow: 0 0 0 1px #3b82f6; }
|
||||
&:hover {
|
||||
border-color: #fca5a5;
|
||||
background: #fee2e2;
|
||||
}
|
||||
&.is-active {
|
||||
border-color: #ef4444;
|
||||
background: #fee2e2;
|
||||
box-shadow: 0 0 0 1px #ef4444;
|
||||
}
|
||||
}
|
||||
.virtual-step-icon {
|
||||
width: 28px; height: 28px; border-radius: 6px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 14px; flex-shrink: 0;
|
||||
&--input { background: #dbeafe; }
|
||||
&--output { background: #fef3c7; }
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
&--input,
|
||||
&--output {
|
||||
background: #fecaca;
|
||||
}
|
||||
}
|
||||
.virtual-step-required {
|
||||
margin-left: 8px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: #dc2626;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
background: #fee2e2;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
.virtual-step-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.virtual-step-name {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #1e293b;
|
||||
}
|
||||
.virtual-step-info { flex: 1; min-width: 0; }
|
||||
.virtual-step-name { font-size: 13px; font-weight: 500; color: #1e293b; }
|
||||
|
||||
/* 分割线 */
|
||||
.step-divider {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
margin: 8px 0; padding: 0 4px;
|
||||
color: #9ca3af; font-size: 11px;
|
||||
&::before, &::after { content: ''; flex: 1; height: 1px; background: #e5e7eb; }
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 8px 0;
|
||||
padding: 0 4px;
|
||||
color: #9ca3af;
|
||||
font-size: 11px;
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: #e5e7eb;
|
||||
}
|
||||
}
|
||||
|
||||
/* 步骤序号状态 */
|
||||
.seq-done { background: #d1fae5 !important; color: #059669 !important; }
|
||||
.seq-running { background: #10b981 !important; color: #fff !important; }
|
||||
.seq-pending { background: #f3f4f6 !important; color: #6b7280 !important; }
|
||||
.route-config-step-card.is-done { opacity: 0.6; }
|
||||
.seq-done {
|
||||
background: #d1fae5 !important;
|
||||
color: #059669 !important;
|
||||
}
|
||||
.seq-running {
|
||||
background: #10b981 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.seq-pending {
|
||||
background: #f3f4f6 !important;
|
||||
color: #6b7280 !important;
|
||||
}
|
||||
.route-config-step-card.is-done {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* 右侧详情面板 */
|
||||
.execution-detail-panel {
|
||||
flex: 1; min-width: 0;
|
||||
display: flex; flex-direction: column;
|
||||
border: 1px solid #e5e7eb; border-radius: 8px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.execution-detail-header {
|
||||
padding: 10px 16px; border-bottom: 1px solid #e5e7eb;
|
||||
display: flex; align-items: center; justify-content: space-between; flex-shrink: 0;
|
||||
h3 { margin: 0; font-size: 14px; font-weight: 600; color: #1e293b; }
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
}
|
||||
.execution-detail-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.execution-detail-actions { display: flex; gap: 8px; }
|
||||
.execution-detail-body {
|
||||
flex: 1; overflow-y: auto; padding: 16px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.section-bar {
|
||||
display: inline-block; width: 3px; height: 14px;
|
||||
background: #10b981; border-radius: 2px; margin-right: 8px;
|
||||
display: inline-block;
|
||||
width: 3px;
|
||||
height: 14px;
|
||||
background: #10b981;
|
||||
border-radius: 2px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,361 @@
|
||||
<template>
|
||||
<div class="task-input-table">
|
||||
<!-- 工具栏 -->
|
||||
<div class="panel-top">
|
||||
<h2 class="title-bar-heading">原材料(样本/细胞产品)</h2>
|
||||
<div v-if="!disabled" class="panel-top-actions">
|
||||
<el-button type="primary" size="small" @click="handleAddSample">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加样本
|
||||
</el-button>
|
||||
<el-button size="small" @click="handleAddCellProduct">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加细胞产品
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table :data="items" size="small" stripe border v-loading="loading" empty-text="暂无原材料">
|
||||
<el-table-column type="index" label="序号" width="56" align="center" />
|
||||
<el-table-column label="类型" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="inputTypeTagType(row.inputType)" size="small">
|
||||
{{ row.inputType === 'cell_product' ? '细胞产品' : '样本' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="编码" min-width="140" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.inputType === 'sample' && !disabled">
|
||||
<el-input v-model="row.inputCode" placeholder="请输入编码" />
|
||||
</template>
|
||||
<template v-else>{{ row.inputCode || row.productCode || '—' }}</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.inputType === 'sample' && !disabled">
|
||||
<el-input v-model="row.inputName" placeholder="请输入名称" />
|
||||
</template>
|
||||
<template v-else>{{ row.inputName || row.productName || '—' }}</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 细胞产品特有字段 -->
|
||||
<el-table-column label="产品类型" min-width="90" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ row.productTypeName || '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="代次" width="70" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.passage || '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="储物格位置" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ row.storageSlotName || '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.inputType === 'cell_product'">
|
||||
<el-tag :type="inputStatusTagType(row.productStatus)" size="small">
|
||||
{{ inputStatusLabel(row.productStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template v-else>—</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<template v-if="!disabled">
|
||||
<el-input v-model="row.remark" placeholder="备注" />
|
||||
</template>
|
||||
<template v-else>{{ row.remark || '—' }}</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="!disabled" label="操作" width="80" fixed="right" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button type="danger" link size="small" @click="handleDelete($index)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 细胞产品选择弹窗 -->
|
||||
<el-dialog
|
||||
v-model="selectDialogVisible"
|
||||
title="选择细胞产品"
|
||||
width="1160px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="select-dialog-body">
|
||||
<!-- 搜索框 -->
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索产品编码或名称"
|
||||
clearable
|
||||
style="margin-bottom: 12px"
|
||||
@input="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<!-- 可选列表 -->
|
||||
<div class="list-body list-wrap">
|
||||
<el-table
|
||||
ref="selectTableRef"
|
||||
class="list-table"
|
||||
:data="filteredCellProducts"
|
||||
stripe
|
||||
row-key="id"
|
||||
height="380"
|
||||
v-loading="cellProductOptionsLoading"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
type="selection"
|
||||
width="45"
|
||||
align="center"
|
||||
:selectable="isRowSelectable"
|
||||
/>
|
||||
<el-table-column
|
||||
label="产品编码"
|
||||
prop="productCode"
|
||||
min-width="210"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="产品名称"
|
||||
prop="productName"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="产品类型"
|
||||
prop="productTypeName"
|
||||
min-width="90"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="代次" prop="passage" width="70" align="center" />
|
||||
<el-table-column
|
||||
label="储物格位置"
|
||||
prop="storageSlotName"
|
||||
min-width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="状态" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="inputStatusTagType(row.productStatus)" size="small">
|
||||
{{ inputStatusLabel(row.productStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="selectDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :disabled="!selectedRows.length" @click="confirmSelect">
|
||||
确定(已选 {{ selectedRows.length }} 项)
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Plus, Search } from '@element-plus/icons-vue'
|
||||
|
||||
defineOptions({ name: 'TaskInputEnhancedTable' })
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
items: Record<string, any>[]
|
||||
loading: boolean
|
||||
disabled?: boolean
|
||||
sampleCode?: string
|
||||
cellProductOptions: {
|
||||
id: string
|
||||
label: string
|
||||
productCode?: string
|
||||
productName?: string
|
||||
productTypeName?: string
|
||||
passage?: string
|
||||
storageSlotName?: string
|
||||
productStatus?: string
|
||||
}[]
|
||||
cellProductOptionsLoading: boolean
|
||||
}>(),
|
||||
{ disabled: false, sampleCode: '' }
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:items': [items: Record<string, any>[]]
|
||||
searchCellProduct: [query: string]
|
||||
save: []
|
||||
}>()
|
||||
|
||||
const selectDialogVisible = ref(false)
|
||||
const selectTableRef = ref()
|
||||
const searchKeyword = ref('')
|
||||
const selectedRows = ref<Record<string, any>[]>([])
|
||||
|
||||
/** 样本 success(绿),细胞产品 warning(琥珀),避免双绿相近 */
|
||||
function inputTypeTagType(inputType: string) {
|
||||
return inputType === 'cell_product' ? 'warning' : 'success'
|
||||
}
|
||||
|
||||
function inputStatusTagType(status: string) {
|
||||
const map: Record<string, string> = {
|
||||
frozen: 'success',
|
||||
in_production: 'warning',
|
||||
voided: 'danger',
|
||||
thawed: 'info'
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
|
||||
function inputStatusLabel(status: string) {
|
||||
const map: Record<string, string> = {
|
||||
frozen: '已冻存',
|
||||
in_production: '生产中',
|
||||
voided: '已作废',
|
||||
thawed: '已复苏'
|
||||
}
|
||||
return map[status] || status || '—'
|
||||
}
|
||||
|
||||
const filteredCellProducts = computed(() => {
|
||||
const keyword = searchKeyword.value?.trim().toLowerCase() || ''
|
||||
if (!keyword) return props.cellProductOptions
|
||||
return props.cellProductOptions.filter(
|
||||
(cp) =>
|
||||
(cp.productCode || '').toLowerCase().includes(keyword) ||
|
||||
(cp.productName || '').toLowerCase().includes(keyword) ||
|
||||
(cp.label || '').toLowerCase().includes(keyword)
|
||||
)
|
||||
})
|
||||
|
||||
function isRowSelectable(row: Record<string, any>) {
|
||||
const existingIds = new Set(
|
||||
props.items.filter((i) => i.inputType === 'cell_product').map((i) => i.inputId)
|
||||
)
|
||||
return !existingIds.has(row.id)
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
emit('searchCellProduct', searchKeyword.value)
|
||||
}
|
||||
|
||||
function handleAddSample() {
|
||||
const newItems = [...props.items]
|
||||
// 自动填入样本编码:如果工单关联了样本编码,且尚未添加过,则自动填入
|
||||
let autoCode = ''
|
||||
const code = (props.sampleCode || '').trim()
|
||||
if (code) {
|
||||
const alreadyAdded = props.items.some(
|
||||
(i) => i.inputType === 'sample' && (i.inputCode || '').trim() === code
|
||||
)
|
||||
if (!alreadyAdded) {
|
||||
autoCode = code
|
||||
}
|
||||
}
|
||||
newItems.push({
|
||||
inputType: 'sample',
|
||||
inputId: '',
|
||||
inputCode: autoCode,
|
||||
inputName: '',
|
||||
remark: ''
|
||||
})
|
||||
emit('update:items', newItems)
|
||||
}
|
||||
|
||||
function handleAddCellProduct() {
|
||||
searchKeyword.value = ''
|
||||
selectedRows.value = []
|
||||
selectDialogVisible.value = true
|
||||
emit('searchCellProduct', '')
|
||||
}
|
||||
|
||||
function handleSelectionChange(rows: Record<string, any>[]) {
|
||||
selectedRows.value = rows
|
||||
}
|
||||
|
||||
function confirmSelect() {
|
||||
if (!selectedRows.value.length) return
|
||||
const existingIds = new Set(
|
||||
props.items.filter((i) => i.inputType === 'cell_product').map((i) => i.inputId)
|
||||
)
|
||||
const newItems = [...props.items]
|
||||
let addedCount = 0
|
||||
for (const row of selectedRows.value) {
|
||||
if (existingIds.has(row.id)) continue
|
||||
newItems.push({
|
||||
inputType: 'cell_product',
|
||||
inputId: row.id,
|
||||
inputCode: row.productCode || '',
|
||||
inputName: row.productName || '',
|
||||
productCode: row.productCode || '',
|
||||
productName: row.productName || '',
|
||||
productTypeName: row.productTypeName || '',
|
||||
passage: row.passage || '',
|
||||
storageSlotName: row.storageSlotName || '',
|
||||
productStatus: row.productStatus || '',
|
||||
remark: ''
|
||||
})
|
||||
addedCount++
|
||||
}
|
||||
emit('update:items', newItems)
|
||||
selectDialogVisible.value = false
|
||||
if (addedCount === 0) {
|
||||
ElMessage.info('所选细胞产品已在列表中')
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete(index: number) {
|
||||
await ElMessageBox.confirm('确定要删除该原材料吗?', '提示', {
|
||||
type: 'warning'
|
||||
})
|
||||
const newItems = [...props.items]
|
||||
newItems.splice(index, 1)
|
||||
emit('update:items', newItems)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.task-input-table {
|
||||
.panel-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.panel-top-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.select-dialog-body {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
// 样本行输入框高度适配
|
||||
:deep(.el-table__cell) {
|
||||
.el-input {
|
||||
.el-input__wrapper {
|
||||
min-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div class="task-output-list">
|
||||
<!-- 工具栏 -->
|
||||
<div class="panel-top">
|
||||
<h2 class="title-bar-heading">产出(细胞产品)</h2>
|
||||
<el-button v-if="!disabled" type="primary" size="small" @click="handleAdd">
|
||||
<el-icon><Plus /></el-icon>
|
||||
新增产出
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table
|
||||
:data="items"
|
||||
size="small"
|
||||
stripe
|
||||
border
|
||||
v-loading="loading"
|
||||
empty-text="暂无产出产品"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="56" align="center" />
|
||||
<el-table-column label="产品编码" prop="productCode" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="产品名称" prop="productName" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="产品类型" min-width="100" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ row.productTypeName || '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格" min-width="80" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ row.specName || '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="代次" prop="passage" width="70" align="center" />
|
||||
<el-table-column label="批次号" prop="batchNo" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column label="储物格位置" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ row.storageSlotName || '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.quantity ?? '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" prop="unit" width="70" align="center" />
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusTagType(row.productStatus)" size="small">
|
||||
{{ statusLabel(row.productStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="!disabled" label="操作" width="140" fixed="right" align="center">
|
||||
<template #default="{ row, $index }">
|
||||
<el-button type="primary" link size="small" @click="handleEdit(row, $index)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" link size="small" @click="handleDelete($index)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<TaskOutputProductDialog
|
||||
v-model="dialogVisible"
|
||||
:edit-item="editingItem"
|
||||
:product-type-options="productTypeOptions"
|
||||
:spec-options="specOptions"
|
||||
:storage-slot-options="storageSlotOptions"
|
||||
:unit-options="unitOptions"
|
||||
:unit-options-loading="unitOptionsLoading"
|
||||
@confirm="handleDialogConfirm"
|
||||
@search-unit="(q: string) => emit('searchUnit', q)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import TaskOutputProductDialog from './TaskOutputProductDialog.vue'
|
||||
|
||||
defineOptions({ name: 'TaskOutputList' })
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
items: Record<string, any>[]
|
||||
loading: boolean
|
||||
productTypeOptions: Array<{ id: string; productTypeName: string }>
|
||||
specOptions: Array<{ id: string; specName: string }>
|
||||
storageSlotOptions: Array<{ id: string; name: string }>
|
||||
unitOptions: { label: string; value: string }[]
|
||||
unitOptionsLoading: boolean
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{ disabled: false }
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:items': [items: Record<string, any>[]]
|
||||
searchUnit: [query: string]
|
||||
save: []
|
||||
}>()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const editingItem = ref<Record<string, any> | null>(null)
|
||||
const editingIndex = ref(-1)
|
||||
|
||||
function statusTagType(status: string) {
|
||||
const map: Record<string, string> = {
|
||||
frozen: 'success',
|
||||
in_production: 'warning',
|
||||
voided: 'danger',
|
||||
thawed: 'info'
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
|
||||
function statusLabel(status: string) {
|
||||
const map: Record<string, string> = {
|
||||
frozen: '已冻存',
|
||||
in_production: '生产中',
|
||||
voided: '已作废',
|
||||
thawed: '已复苏'
|
||||
}
|
||||
return map[status] || status || '—'
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
editingItem.value = null
|
||||
editingIndex.value = -1
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleEdit(row: Record<string, any>, index: number) {
|
||||
editingItem.value = { ...row }
|
||||
editingIndex.value = index
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
async function handleDelete(index: number) {
|
||||
await ElMessageBox.confirm('确定要删除该产出产品吗?删除后将在保存时生效。', '提示', {
|
||||
type: 'warning'
|
||||
})
|
||||
const newItems = [...props.items]
|
||||
newItems.splice(index, 1)
|
||||
emit('update:items', newItems)
|
||||
}
|
||||
|
||||
function handleDialogConfirm(data: Record<string, any>) {
|
||||
const newItems = [...props.items]
|
||||
if (editingIndex.value >= 0) {
|
||||
// 编辑:替换原有项,保留 cellProductId
|
||||
newItems[editingIndex.value] = {
|
||||
...newItems[editingIndex.value],
|
||||
...data
|
||||
}
|
||||
} else {
|
||||
// 新增:添加到列表
|
||||
newItems.push(data)
|
||||
}
|
||||
emit('update:items', newItems)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.task-output-list {
|
||||
.panel-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,654 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
:title="isEdit ? '编辑产出产品' : '新增产出产品'"
|
||||
width="1000px"
|
||||
class="dialog-form-layout task-output-product-dialog"
|
||||
align-center
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-position="top"
|
||||
class="task-output-form"
|
||||
scroll-to-error
|
||||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
||||
>
|
||||
<div class="form-grid-3">
|
||||
<!-- 产品编码 -->
|
||||
<el-form-item label="产品编码" prop="productCode">
|
||||
<el-input v-model="formData.productCode" placeholder="请输入产品编码" />
|
||||
</el-form-item>
|
||||
<!-- 产品名称 -->
|
||||
<el-form-item label="产品名称" prop="productName">
|
||||
<el-input v-model="formData.productName" placeholder="请输入产品名称" />
|
||||
</el-form-item>
|
||||
<!-- 产品类型 -->
|
||||
<el-form-item label="产品类型">
|
||||
<el-select
|
||||
v-model="formData.productTypeId"
|
||||
placeholder="请选择产品类型"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in productTypeOptions"
|
||||
:key="item.id"
|
||||
:label="item.productTypeName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 规格 -->
|
||||
<el-form-item label="规格">
|
||||
<el-select
|
||||
v-model="formData.specId"
|
||||
placeholder="请选择规格"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in specOptions"
|
||||
:key="item.id"
|
||||
:label="item.specName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 代次 -->
|
||||
<el-form-item label="代次">
|
||||
<el-select
|
||||
v-model="formData.passage"
|
||||
placeholder="请选择代次"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option v-for="p in passageOptions" :key="p" :label="p" :value="p" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 批次号 -->
|
||||
<el-form-item label="批次号" prop="batchNo">
|
||||
<el-input v-model="formData.batchNo" placeholder="请输入批次号" />
|
||||
</el-form-item>
|
||||
<!-- 储物格位置 -->
|
||||
<el-form-item label="储物格位置">
|
||||
<el-select
|
||||
v-model="formData.storageSlotId"
|
||||
placeholder="请选择储物格"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in storageSlotOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 预计细胞数量 -->
|
||||
<el-form-item label="预计细胞数量(亿)">
|
||||
<el-input-number
|
||||
v-model="formData.expectedCellCount"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 活性 -->
|
||||
<el-form-item label="活性(%)">
|
||||
<el-input-number
|
||||
v-model="formData.viability"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="1"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 冻存日期 -->
|
||||
<el-form-item label="冻存日期">
|
||||
<el-date-picker
|
||||
v-model="formData.freezeDate"
|
||||
type="date"
|
||||
placeholder="请选择冻存日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 数量 -->
|
||||
<el-form-item label="数量">
|
||||
<el-input-number
|
||||
v-model="formData.quantity"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 单位 -->
|
||||
<el-form-item label="单位">
|
||||
<el-select
|
||||
v-model="formData.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
style="width: 100%"
|
||||
:loading="unitOptionsLoading"
|
||||
:remote-method="(q: string) => emit('searchUnit', q)"
|
||||
@visible-change="(visible: boolean) => visible && emit('searchUnit', '')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in unitOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 有效期 -->
|
||||
<el-form-item label="有效期">
|
||||
<el-date-picker
|
||||
v-model="formData.expiryDate"
|
||||
type="date"
|
||||
placeholder="请选择有效期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 产品图片 (full width) -->
|
||||
<el-form-item label="产品图片" class="form-col-span-2">
|
||||
<div class="attachment-upload">
|
||||
<div class="attachment-list">
|
||||
<el-upload
|
||||
ref="productImageUploadRef"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:multiple="true"
|
||||
accept="image/*"
|
||||
@change="handleProductImageChange"
|
||||
>
|
||||
<div class="upload-card">
|
||||
<el-icon :size="28" color="#10b981"><Plus /></el-icon>
|
||||
<span class="upload-card-text">上传图片</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div
|
||||
v-for="(file, index) in productImageFiles"
|
||||
:key="file.id || index"
|
||||
class="attachment-item"
|
||||
:class="{ 'is-uploading': file._uploading }"
|
||||
>
|
||||
<div
|
||||
class="attachment-preview"
|
||||
@click="
|
||||
file.fileUrl && !file._uploading ? handlePreviewProductImage(file) : undefined
|
||||
"
|
||||
>
|
||||
<template v-if="file.fileUrl">
|
||||
<img :src="fullFileUrl(file.fileUrl)" :alt="file.fileName" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-icon :size="24" color="#94a3b8"><Loading /></el-icon>
|
||||
</template>
|
||||
</div>
|
||||
<div class="attachment-footer">
|
||||
<span class="attachment-name" :title="file.fileName">{{ file.fileName }}</span>
|
||||
<div class="attachment-actions">
|
||||
<el-icon
|
||||
class="action-btn"
|
||||
title="下载"
|
||||
@click="handleDownloadProductImage(file)"
|
||||
>
|
||||
<Download />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="action-btn action-btn--danger"
|
||||
title="删除"
|
||||
@click="removeProductImage(index)"
|
||||
>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="upload-hint">支持图片上传,单文件不超过 10MB</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- 备注 (full width) -->
|
||||
<el-form-item label="备注" class="form-col-span-2">
|
||||
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="可选" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="emit('update:modelValue', false)">
|
||||
<el-icon><Close /></el-icon>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
|
||||
<el-icon><Check /></el-icon>
|
||||
确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 图片预览弹窗 -->
|
||||
<el-dialog v-model="previewVisible" title="图片预览" width="800px" align-center destroy-on-close>
|
||||
<div class="preview-container">
|
||||
<img
|
||||
:src="previewUrl"
|
||||
alt="预览"
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Plus, Close, Check, Delete, Download, Loading } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
|
||||
defineOptions({ name: 'TaskOutputProductDialog' })
|
||||
|
||||
type ProductImageFile = {
|
||||
id?: string
|
||||
fileName: string
|
||||
fileUrl: string
|
||||
fileType?: string
|
||||
_uploading?: boolean
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
editItem?: Record<string, any> | null
|
||||
productTypeOptions: Array<{ id: string; productTypeName: string }>
|
||||
specOptions: Array<{ id: string; specName: string }>
|
||||
storageSlotOptions: Array<{ id: string; name: string }>
|
||||
unitOptions: { label: string; value: string }[]
|
||||
unitOptionsLoading: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
confirm: [data: Record<string, any>]
|
||||
searchUnit: [query: string]
|
||||
}>()
|
||||
|
||||
const passageOptions = ['P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7', 'P8']
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
function fullFileUrl(suffix: string) {
|
||||
if (!suffix) return ''
|
||||
if (suffix.startsWith('http://') || suffix.startsWith('https://') || suffix.startsWith('/'))
|
||||
return suffix
|
||||
return `${minioBaseUrl}/${suffix}`
|
||||
}
|
||||
|
||||
const isEdit = computed(() => !!props.editItem?.cellProductId)
|
||||
const formRef = ref()
|
||||
const submitLoading = ref(false)
|
||||
const productImageUploadRef = ref()
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const productImageFiles = ref<ProductImageFile[]>([])
|
||||
|
||||
const formData = reactive({
|
||||
cellProductId: '',
|
||||
productCode: '',
|
||||
productName: '',
|
||||
productTypeId: '',
|
||||
specId: '',
|
||||
passage: '',
|
||||
batchNo: '',
|
||||
storageSlotId: '',
|
||||
expectedCellCount: undefined as number | undefined,
|
||||
viability: undefined as number | undefined,
|
||||
freezeDate: '',
|
||||
quantity: 1 as number,
|
||||
unit: '',
|
||||
expiryDate: '',
|
||||
productImages: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
productCode: [{ required: true, message: '请输入产品编码', trigger: 'blur' }],
|
||||
productName: [{ required: true, message: '请输入产品名称', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// Watch editItem to populate form
|
||||
watch(
|
||||
() => props.editItem,
|
||||
(item) => {
|
||||
if (item) {
|
||||
formData.cellProductId = item.cellProductId || ''
|
||||
formData.productCode = item.productCode || ''
|
||||
formData.productName = item.productName || ''
|
||||
formData.productTypeId = item.productTypeId || ''
|
||||
formData.specId = item.specId || ''
|
||||
formData.passage = item.passage || ''
|
||||
formData.batchNo = item.batchNo || ''
|
||||
formData.storageSlotId = item.storageSlotId || ''
|
||||
formData.expectedCellCount = item.expectedCellCount ?? undefined
|
||||
formData.viability = item.viability ?? undefined
|
||||
formData.freezeDate = item.freezeDate || ''
|
||||
formData.quantity = item.quantity ?? 1
|
||||
formData.unit = item.unit || ''
|
||||
formData.expiryDate = item.expiryDate || ''
|
||||
formData.productImages = item.productImages || ''
|
||||
formData.remark = item.remark || ''
|
||||
syncFormToImages(item.productImages || '')
|
||||
} else {
|
||||
resetForm()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function resetForm() {
|
||||
formData.cellProductId = ''
|
||||
formData.productCode = ''
|
||||
formData.productName = ''
|
||||
formData.productTypeId = ''
|
||||
formData.specId = ''
|
||||
formData.passage = ''
|
||||
formData.batchNo = ''
|
||||
formData.storageSlotId = ''
|
||||
formData.expectedCellCount = undefined
|
||||
formData.viability = undefined
|
||||
formData.freezeDate = ''
|
||||
formData.quantity = 1
|
||||
formData.unit = ''
|
||||
formData.expiryDate = ''
|
||||
formData.productImages = ''
|
||||
formData.remark = ''
|
||||
productImageFiles.value = []
|
||||
}
|
||||
|
||||
function fileNameFromUrl(url: string, index: number) {
|
||||
const segment = url.split('/').pop() || ''
|
||||
const base = decodeURIComponent(segment.split('?')[0] || '')
|
||||
return base || `图片${index + 1}`
|
||||
}
|
||||
|
||||
function syncImagesToForm() {
|
||||
formData.productImages = productImageFiles.value
|
||||
.filter((f) => f.fileUrl && !f._uploading)
|
||||
.map((f) => f.fileUrl)
|
||||
.join(',')
|
||||
}
|
||||
|
||||
function syncFormToImages(imagesStr: string) {
|
||||
productImageFiles.value = imagesStr
|
||||
? imagesStr
|
||||
.split(',')
|
||||
.map((s, i) => {
|
||||
const url = s.trim()
|
||||
if (!url) return null
|
||||
return {
|
||||
id: `existing_${i}`,
|
||||
fileName: fileNameFromUrl(url, i),
|
||||
fileUrl: url,
|
||||
fileType: 'image/'
|
||||
} as ProductImageFile
|
||||
})
|
||||
.filter((f): f is ProductImageFile => Boolean(f))
|
||||
: []
|
||||
}
|
||||
|
||||
async function handleProductImageChange(uploadFile: any) {
|
||||
const file = uploadFile.raw || uploadFile
|
||||
if (!file) return
|
||||
const maxSizeMb = 10
|
||||
if (file.size / 1024 / 1024 > maxSizeMb) {
|
||||
ElMessage.error(`图片大小不能超过 ${maxSizeMb}MB`)
|
||||
productImageUploadRef.value?.clearFiles()
|
||||
return
|
||||
}
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
||||
productImageFiles.value.push({
|
||||
id: tempId,
|
||||
fileName: file.name,
|
||||
fileUrl: '',
|
||||
fileType: file.type || 'image/',
|
||||
_uploading: true
|
||||
})
|
||||
const idx = productImageFiles.value.length - 1
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('folder', 'cell-product')
|
||||
fd.append('file', file)
|
||||
const res = await fetchUploadAttachment(fd)
|
||||
productImageFiles.value[idx] = {
|
||||
...productImageFiles.value[idx],
|
||||
id: res.id || tempId,
|
||||
fileName: file.name || res.fileName || fileNameFromUrl(res.fileUrl, idx),
|
||||
fileUrl: res.fileUrl,
|
||||
fileType: res.fileType || file.type || 'image/',
|
||||
_uploading: false
|
||||
}
|
||||
syncImagesToForm()
|
||||
} catch {
|
||||
ElMessage.error('图片上传失败')
|
||||
productImageFiles.value.splice(idx, 1)
|
||||
} finally {
|
||||
nextTick(() => productImageUploadRef.value?.clearFiles())
|
||||
}
|
||||
}
|
||||
|
||||
function removeProductImage(index: number) {
|
||||
productImageFiles.value.splice(index, 1)
|
||||
syncImagesToForm()
|
||||
}
|
||||
|
||||
function handlePreviewProductImage(file: ProductImageFile) {
|
||||
if (file._uploading || !file.fileUrl) return
|
||||
previewUrl.value = fullFileUrl(file.fileUrl)
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
function handleDownloadProductImage(file: ProductImageFile) {
|
||||
downloadAttachmentFile(file, { fullFileUrl })
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
await formRef.value?.validate?.()
|
||||
syncImagesToForm()
|
||||
submitLoading.value = true
|
||||
try {
|
||||
const result: Record<string, any> = {
|
||||
cellProductId: formData.cellProductId || undefined,
|
||||
productCode: formData.productCode.trim(),
|
||||
productName: formData.productName.trim(),
|
||||
productTypeId: formData.productTypeId || undefined,
|
||||
specId: formData.specId || undefined,
|
||||
passage: formData.passage || undefined,
|
||||
batchNo: formData.batchNo.trim() || undefined,
|
||||
storageSlotId: formData.storageSlotId || undefined,
|
||||
expectedCellCount: formData.expectedCellCount ?? undefined,
|
||||
viability: formData.viability ?? undefined,
|
||||
freezeDate: formData.freezeDate || undefined,
|
||||
quantity: formData.quantity,
|
||||
unit: formData.unit?.trim() || undefined,
|
||||
expiryDate: formData.expiryDate || undefined,
|
||||
productImages: formData.productImages?.trim() || undefined,
|
||||
productStatus: 'frozen',
|
||||
remark: formData.remark?.trim() || undefined
|
||||
}
|
||||
emit('confirm', result)
|
||||
emit('update:modelValue', false)
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.task-output-product-dialog {
|
||||
:deep(.el-dialog__body) {
|
||||
max-height: calc(100vh - 200px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-upload {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.upload-card {
|
||||
width: 140px;
|
||||
height: 96px;
|
||||
border: 1px dashed #a7f3d0;
|
||||
border-radius: 8px;
|
||||
background: #f0fdf4;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
background 0.15s;
|
||||
|
||||
&:hover {
|
||||
border-color: #10b981;
|
||||
border-style: solid;
|
||||
background: #ecfdf5;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-card-text {
|
||||
font-size: 12px;
|
||||
color: #10b981;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: #94a3b8;
|
||||
line-height: 1;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.attachment-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.attachment-item {
|
||||
width: 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
box-shadow 0.15s;
|
||||
|
||||
&:hover {
|
||||
border-color: #10b981;
|
||||
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
&.is-uploading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
width: 100%;
|
||||
height: 96px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8fafc;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.attachment-name {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 11px;
|
||||
color: #334155;
|
||||
line-height: 1.4;
|
||||
word-break: break-all;
|
||||
min-height: calc(11px * 1.4 * 2);
|
||||
}
|
||||
|
||||
.attachment-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
cursor: pointer;
|
||||
background: #f1f5f9;
|
||||
transition:
|
||||
background 0.15s,
|
||||
color 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #10b981;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&--danger:hover {
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -19,7 +19,12 @@
|
||||
<el-input v-model="searchForm.workOrderNo" placeholder="请输入工单号" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="工单状态">
|
||||
<el-select v-model="searchForm.workOrderStatus" placeholder="全部" clearable style="width: 100%">
|
||||
<el-select
|
||||
v-model="searchForm.workOrderStatus"
|
||||
placeholder="全部"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="待配置" value="dpz" />
|
||||
<el-option label="已完成" value="ywc" />
|
||||
</el-select>
|
||||
@@ -27,8 +32,12 @@
|
||||
</div>
|
||||
<div class="filter-actions-row">
|
||||
<div class="filter-actions-end">
|
||||
<el-button class="btn-ghost" @click="handleReset"><el-icon><RefreshLeft /></el-icon>重置</el-button>
|
||||
<el-button class="btn-main" type="primary" @click="handleSearch"><el-icon><Search /></el-icon>搜索</el-button>
|
||||
<el-button class="btn-ghost" @click="handleReset"
|
||||
><el-icon><RefreshLeft /></el-icon>重置</el-button
|
||||
>
|
||||
<el-button class="btn-main" type="primary" @click="handleSearch"
|
||||
><el-icon><Search /></el-icon>搜索</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
@@ -43,7 +52,13 @@
|
||||
stripe
|
||||
@row-dblclick="handleView"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="68" align="center" :index="(i) => (pagination.page - 1) * pagination.pageSize + i + 1" />
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
width="68"
|
||||
align="center"
|
||||
:index="(i) => (pagination.page - 1) * pagination.pageSize + i + 1"
|
||||
/>
|
||||
<el-table-column prop="workOrderNo" label="工单号" min-width="150" align="center" />
|
||||
<el-table-column prop="protocolNo" label="协议号" min-width="140" align="center" />
|
||||
<el-table-column prop="sampleCode" label="样本编码" min-width="120" align="center" />
|
||||
@@ -71,10 +86,20 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="170" align="center" />
|
||||
<el-table-column label="操作" width="460" fixed="right" align="center" header-align="center">
|
||||
<el-table-column
|
||||
label="操作"
|
||||
width="460"
|
||||
fixed="right"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-button class="btn-op-item" @click="handleView(row)"><el-icon><View /></el-icon>查看详情</el-button>
|
||||
<el-button class="btn-op-item" @click="handleEditTask(row)"><el-icon><EditPen /></el-icon>编辑任务</el-button>
|
||||
<el-button class="btn-op-item" @click="handleView(row)"
|
||||
><el-icon><View /></el-icon>查看详情</el-button
|
||||
>
|
||||
<el-button class="btn-op-item" @click="handleEditTask(row)"
|
||||
><el-icon><EditPen /></el-icon>编辑任务</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="row.workOrderStatus === 'dpz'"
|
||||
class="btn-op-item primary"
|
||||
@@ -144,11 +169,30 @@
|
||||
@success="loadData"
|
||||
/>
|
||||
|
||||
<!-- 编辑任务基本信息弹窗 -->
|
||||
<EditTaskInfoDialog
|
||||
v-model:visible="editTaskInfoVisible"
|
||||
:task-id="editTaskInfoId"
|
||||
@success="handleEditTaskInfoSuccess"
|
||||
/>
|
||||
|
||||
<!-- 任务列表弹窗 -->
|
||||
<el-dialog v-model="taskSelectVisible" title="生产任务列表" width="780px" destroy-on-close align-center>
|
||||
<div v-loading="taskSelectLoading" style="max-height:60vh;overflow-y:auto">
|
||||
<el-table :data="taskSelectList" stripe style="width:100%">
|
||||
<el-table-column type="index" label="序号" width="68" align="center" :index="(i) => i + 1" />
|
||||
<el-dialog
|
||||
v-model="taskSelectVisible"
|
||||
title="生产任务列表"
|
||||
width="980px"
|
||||
destroy-on-close
|
||||
align-center
|
||||
>
|
||||
<div v-loading="taskSelectLoading" style="max-height: 60vh; overflow-y: auto">
|
||||
<el-table :data="taskSelectList" stripe style="width: 100%">
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
width="68"
|
||||
align="center"
|
||||
:index="(i) => i + 1"
|
||||
/>
|
||||
<el-table-column prop="taskName" label="任务名称" min-width="140" />
|
||||
<el-table-column prop="routeName" label="工艺路线" min-width="140">
|
||||
<template #default="{ row }">{{ row.routeName || '—' }}</template>
|
||||
@@ -160,14 +204,29 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="140">
|
||||
<template #default="{ row }">{{ row.remark || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="170" align="center" />
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="openTaskEditor(row)">编辑</el-button>
|
||||
<el-button link type="primary" size="small" @click="openEditTaskInfo(row)"
|
||||
>编辑信息</el-button
|
||||
>
|
||||
<el-button link type="primary" size="small" @click="openTaskEditor(row)"
|
||||
>编辑工艺</el-button
|
||||
>
|
||||
<el-button link type="danger" size="small" @click="handleDeleteTask(row)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!taskSelectLoading && !taskSelectList.length" description="暂无任务,请先派发任务" :image-size="48" />
|
||||
<el-empty
|
||||
v-if="!taskSelectLoading && !taskSelectList.length"
|
||||
description="暂无任务,请先派发任务"
|
||||
:image-size="48"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</section>
|
||||
@@ -178,7 +237,15 @@
|
||||
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
|
||||
import type { TableInstance } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Search, RefreshLeft, View, Check, Document, Position, EditPen } from '@element-plus/icons-vue'
|
||||
import {
|
||||
Search,
|
||||
RefreshLeft,
|
||||
View,
|
||||
Check,
|
||||
Document,
|
||||
Position,
|
||||
EditPen
|
||||
} from '@element-plus/icons-vue'
|
||||
import {
|
||||
fetchProductionWorkOrderList,
|
||||
fetchProductionWorkOrderDetail,
|
||||
@@ -195,7 +262,11 @@
|
||||
import type { ViewSection } from '@/components/business/system-view-dialog/index.vue'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
import DispatchTaskDialog from './DispatchTaskDialog.vue'
|
||||
import { fetchProductionTaskList } from '@/api/production-task'
|
||||
import EditTaskInfoDialog from './EditTaskInfoDialog.vue'
|
||||
import {
|
||||
fetchProductionTaskList,
|
||||
fetchDeleteProductionTask
|
||||
} from '@/api/production-task'
|
||||
|
||||
defineOptions({ name: 'ProductionProductionWorkOrder' })
|
||||
|
||||
@@ -248,8 +319,16 @@
|
||||
await layoutWorkOrderTable()
|
||||
}
|
||||
}
|
||||
function handleSearch() { pagination.page = 1; loadData() }
|
||||
function handleReset() { searchForm.workOrderNo = ''; searchForm.workOrderStatus = ''; pagination.page = 1; loadData() }
|
||||
function handleSearch() {
|
||||
pagination.page = 1
|
||||
loadData()
|
||||
}
|
||||
function handleReset() {
|
||||
searchForm.workOrderNo = ''
|
||||
searchForm.workOrderStatus = ''
|
||||
pagination.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
const viewVisible = ref(false)
|
||||
const detail = ref<ProductionWorkOrderDetail | null>(null)
|
||||
@@ -262,12 +341,15 @@
|
||||
return r.receiveAttachmentFiles as Array<Record<string, unknown>>
|
||||
const urls = String(r.receiveAttachments || '')
|
||||
if (!urls.trim()) return []
|
||||
return urls.split(',').map((u, i) => {
|
||||
const url = u.trim()
|
||||
if (!url) return null
|
||||
const name = url.includes('/') ? url.split('/').pop() || `附件${i + 1}` : url
|
||||
return { id: `url_${i}`, fileUrl: url, fileName: name }
|
||||
}).filter(Boolean) as Array<Record<string, unknown>>
|
||||
return urls
|
||||
.split(',')
|
||||
.map((u, i) => {
|
||||
const url = u.trim()
|
||||
if (!url) return null
|
||||
const name = url.includes('/') ? url.split('/').pop() || `附件${i + 1}` : url
|
||||
return { id: `url_${i}`, fileUrl: url, fileName: name }
|
||||
})
|
||||
.filter(Boolean) as Array<Record<string, unknown>>
|
||||
})
|
||||
function receptionAttachmentFullUrl(url: string) {
|
||||
const u = String(url || '').trim()
|
||||
@@ -310,9 +392,10 @@
|
||||
)
|
||||
}
|
||||
if (r.etaTime) transportFields.push({ label: '预计到达', value: r.etaTime })
|
||||
const transportSection = transportFields.length > 1 || r.transportType
|
||||
? [{ title: '运输信息', fields: transportFields }]
|
||||
: []
|
||||
const transportSection =
|
||||
transportFields.length > 1 || r.transportType
|
||||
? [{ title: '运输信息', fields: transportFields }]
|
||||
: []
|
||||
return [
|
||||
{
|
||||
title: '基本信息',
|
||||
@@ -320,9 +403,20 @@
|
||||
{ label: '工单号', value: d.workOrderNo },
|
||||
{ label: '协议号', value: d.protocolNo },
|
||||
{ label: '样本编码', value: d.sampleCode },
|
||||
{ label: '来源类型', value: r.sourceTypeLabel || d.receptionSourceLabel || receptionSourceLabel(d.receptionSource || ''), tagType: 'primary' },
|
||||
{
|
||||
label: '来源类型',
|
||||
value:
|
||||
r.sourceTypeLabel ||
|
||||
d.receptionSourceLabel ||
|
||||
receptionSourceLabel(d.receptionSource || ''),
|
||||
tagType: 'primary'
|
||||
},
|
||||
{ label: '样本类型', value: r.sampleTypeName || d.sampleTypeName || '—' },
|
||||
{ label: '工单状态', value: woStatusTag(d.workOrderStatus).label, tagType: woStatusTag(d.workOrderStatus).type as any },
|
||||
{
|
||||
label: '工单状态',
|
||||
value: woStatusTag(d.workOrderStatus).label,
|
||||
tagType: woStatusTag(d.workOrderStatus).type as any
|
||||
},
|
||||
{ label: '创建时间', value: d.createTime || '—' },
|
||||
{ label: '备注', value: d.remark || '—', span: true }
|
||||
]
|
||||
@@ -337,7 +431,17 @@
|
||||
{ label: '接收温度(℃)', value: r.receiveTemperature ? `${r.receiveTemperature}℃` : '—' },
|
||||
{ label: '体积(ml)', value: formatMetric(r.volumeMl) },
|
||||
{ label: '净重(g)', value: formatMetric(r.netWeightG) },
|
||||
{ label: '结论', value: r.acceptConclusionLabel || acceptConclusionLabel(String(r.acceptConclusion || '')), tagType: r.acceptConclusion === 'js' ? 'success' : r.acceptConclusion === 'jj' ? 'danger' : 'info' },
|
||||
{
|
||||
label: '结论',
|
||||
value:
|
||||
r.acceptConclusionLabel || acceptConclusionLabel(String(r.acceptConclusion || '')),
|
||||
tagType:
|
||||
r.acceptConclusion === 'js'
|
||||
? 'success'
|
||||
: r.acceptConclusion === 'jj'
|
||||
? 'danger'
|
||||
: 'info'
|
||||
},
|
||||
{ label: '检查项', value: formatCheckResults(r.checkResults), span: true },
|
||||
{ label: '备注', value: r.remark || '—', span: true }
|
||||
]
|
||||
@@ -374,8 +478,10 @@
|
||||
const taskSelectVisible = ref(false)
|
||||
const taskSelectList = ref<any[]>([])
|
||||
const taskSelectLoading = ref(false)
|
||||
const currentWorkOrderRow = ref<any>(null)
|
||||
|
||||
async function handleEditTask(row: any) {
|
||||
currentWorkOrderRow.value = row
|
||||
taskSelectLoading.value = true
|
||||
taskSelectVisible.value = true
|
||||
taskSelectList.value = []
|
||||
@@ -397,6 +503,30 @@
|
||||
taskSelectVisible.value = false
|
||||
}
|
||||
|
||||
// ── 编辑任务基本信息 ──────────────────────────────────
|
||||
const editTaskInfoVisible = ref(false)
|
||||
const editTaskInfoId = ref('')
|
||||
|
||||
function openEditTaskInfo(task: any) {
|
||||
editTaskInfoId.value = task.id
|
||||
editTaskInfoVisible.value = true
|
||||
}
|
||||
|
||||
function handleEditTaskInfoSuccess() {
|
||||
if (currentWorkOrderRow.value) {
|
||||
handleEditTask(currentWorkOrderRow.value)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteTask(task: any) {
|
||||
await ElMessageBox.confirm('确定要删除该任务吗?删除后不可恢复。', '提示', { type: 'warning' })
|
||||
await fetchDeleteProductionTask(task.id)
|
||||
ElMessage.success('任务已删除')
|
||||
if (currentWorkOrderRow.value) {
|
||||
handleEditTask(currentWorkOrderRow.value)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => loadData())
|
||||
</script>
|
||||
|
||||
@@ -411,8 +541,15 @@
|
||||
font-size: 13px;
|
||||
align-items: start;
|
||||
|
||||
dt { margin: 0; color: #64748b; font-weight: 500; }
|
||||
dd { margin: 0; color: #1e293b; }
|
||||
dt {
|
||||
margin: 0;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
}
|
||||
dd {
|
||||
margin: 0;
|
||||
color: #1e293b;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -23,6 +23,13 @@ import {
|
||||
} from '@/api/production'
|
||||
import { fetchItemUnitList } from '@/api/admin'
|
||||
import { fetchCellProductList } from '@/api/cell-product'
|
||||
import {
|
||||
fetchCellProductTypeList,
|
||||
fetchCellProductSpecList,
|
||||
fetchStorageFacilityList,
|
||||
fetchStorageSlotList
|
||||
} from '@/api/admin'
|
||||
import { buildTreePathLabelFromFlat } from '@/utils/tree-path'
|
||||
|
||||
// ── helpers ──────────────────────────────────────────────────────────
|
||||
function toApiFlag(val: boolean | number | string | undefined): string {
|
||||
@@ -90,9 +97,23 @@ export function useTaskStepEditor() {
|
||||
const consumableOptionsLoading = ref(false)
|
||||
const consumableMasterById = ref<Record<string, Record<string, any>>>({})
|
||||
|
||||
const cellProductOptions = ref<{ id: string; label: string }[]>([])
|
||||
const cellProductOptions = ref<
|
||||
Array<{
|
||||
id: string
|
||||
label: string
|
||||
productCode?: string
|
||||
productName?: string
|
||||
productTypeName?: string
|
||||
passage?: string
|
||||
storageSlotName?: string
|
||||
productStatus?: string
|
||||
}>
|
||||
>([])
|
||||
const cellProductOptionsLoading = ref(false)
|
||||
const cellProductMasterById = ref<Record<string, Record<string, any>>>({})
|
||||
const productTypeOptions = ref<Array<{ id: string; productTypeName: string }>>([])
|
||||
const specOptions = ref<Array<{ id: string; specName: string }>>([])
|
||||
const storageSlotOptions = ref<Array<{ id: string; name: string }>>([])
|
||||
|
||||
function mapItemUnitOption(u: { unitName?: string; unitCode?: string }) {
|
||||
const name = (u.unitName || '').trim()
|
||||
@@ -108,7 +129,11 @@ export function useTaskStepEditor() {
|
||||
async function searchItemUnitOptions(query: string) {
|
||||
itemUnitOptionsLoading.value = true
|
||||
try {
|
||||
const res = await fetchItemUnitList({ page: 1, pageSize: 100, keyword: query?.trim() || undefined })
|
||||
const res = await fetchItemUnitList({
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
keyword: query?.trim() || undefined
|
||||
})
|
||||
itemUnitOptions.value = (res.list || []).map(mapItemUnitOption).filter((o) => o.value)
|
||||
} catch {
|
||||
itemUnitOptions.value = []
|
||||
@@ -141,7 +166,11 @@ export function useTaskStepEditor() {
|
||||
async function searchReagentOptions(query: string) {
|
||||
reagentOptionsLoading.value = true
|
||||
try {
|
||||
const res = await fetchReagentList({ page: 1, pageSize: 100, keyword: query?.trim() || undefined })
|
||||
const res = await fetchReagentList({
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
keyword: query?.trim() || undefined
|
||||
})
|
||||
const list = res.list || []
|
||||
cacheReagentMasters(list)
|
||||
reagentOptions.value = list
|
||||
@@ -158,7 +187,10 @@ export function useTaskStepEditor() {
|
||||
function ensureReagentOption(row: Record<string, any>) {
|
||||
if (row.reagentId && !String(row.reagentId).startsWith('legacy:')) {
|
||||
if (!reagentOptions.value.some((o) => o.id === row.reagentId))
|
||||
reagentOptions.value = [{ id: row.reagentId, label: mapReagentOptionLabel(row) }, ...reagentOptions.value]
|
||||
reagentOptions.value = [
|
||||
{ id: row.reagentId, label: mapReagentOptionLabel(row) },
|
||||
...reagentOptions.value
|
||||
]
|
||||
return
|
||||
}
|
||||
// reagentId 为空(后端不存储该字段)— 通过 reagentCode 反查主表真实 ID
|
||||
@@ -175,7 +207,10 @@ export function useTaskStepEditor() {
|
||||
const legacyId = `legacy:${code || name}`
|
||||
row.reagentId = legacyId
|
||||
if (!reagentOptions.value.some((o) => o.id === legacyId))
|
||||
reagentOptions.value = [{ id: legacyId, label: mapReagentOptionLabel(row) }, ...reagentOptions.value]
|
||||
reagentOptions.value = [
|
||||
{ id: legacyId, label: mapReagentOptionLabel(row) },
|
||||
...reagentOptions.value
|
||||
]
|
||||
}
|
||||
function ensureAllStepReagentOptions() {
|
||||
for (const step of stepList.value) {
|
||||
@@ -183,11 +218,17 @@ export function useTaskStepEditor() {
|
||||
}
|
||||
}
|
||||
function onReagentRowSelect(row: Record<string, any>, reagentId: string) {
|
||||
if (!reagentId) { row.reagentId = ''; return }
|
||||
if (String(reagentId).startsWith('legacy:')) { return }
|
||||
if (!reagentId) {
|
||||
row.reagentId = ''
|
||||
return
|
||||
}
|
||||
if (String(reagentId).startsWith('legacy:')) {
|
||||
return
|
||||
}
|
||||
// 去重:同一试剂不允许出现多次(排除当前行和 legacy 占位符)
|
||||
const dup = (selectedStep.value?.reagents || []).some(
|
||||
(r: Record<string, any>) => r !== row && r.reagentId === reagentId && !String(r.reagentId).startsWith('legacy:')
|
||||
(r: Record<string, any>) =>
|
||||
r !== row && r.reagentId === reagentId && !String(r.reagentId).startsWith('legacy:')
|
||||
)
|
||||
if (dup) {
|
||||
ElMessage.warning('该试剂已添加,请勿重复选择')
|
||||
@@ -195,11 +236,16 @@ export function useTaskStepEditor() {
|
||||
return
|
||||
}
|
||||
const master = reagentMasterById.value[reagentId]
|
||||
if (!master) { return }
|
||||
if (!master) {
|
||||
return
|
||||
}
|
||||
Object.assign(row, {
|
||||
reagentId: master.id, reagentCode: master.reagentCode || '',
|
||||
reagentName: master.reagentName || '', spec: master.spec || '',
|
||||
standardUsage: master.standardUsage ?? '', unit: master.unit || '',
|
||||
reagentId: master.id,
|
||||
reagentCode: master.reagentCode || '',
|
||||
reagentName: master.reagentName || '',
|
||||
spec: master.spec || '',
|
||||
standardUsage: master.standardUsage ?? '',
|
||||
unit: master.unit || '',
|
||||
remark: master.remark ?? master.description ?? ''
|
||||
})
|
||||
ensureItemUnitOption(row.unit)
|
||||
@@ -230,7 +276,11 @@ export function useTaskStepEditor() {
|
||||
async function searchConsumableOptions(query: string) {
|
||||
consumableOptionsLoading.value = true
|
||||
try {
|
||||
const res = await fetchConsumableList({ page: 1, pageSize: 100, keyword: query?.trim() || undefined })
|
||||
const res = await fetchConsumableList({
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
keyword: query?.trim() || undefined
|
||||
})
|
||||
const list = res.list || []
|
||||
cacheConsumableMasters(list)
|
||||
consumableOptions.value = list
|
||||
@@ -247,7 +297,10 @@ export function useTaskStepEditor() {
|
||||
function ensureConsumableOption(row: Record<string, any>) {
|
||||
if (row.consumableId && !String(row.consumableId).startsWith('legacy:')) {
|
||||
if (!consumableOptions.value.some((o) => o.id === row.consumableId))
|
||||
consumableOptions.value = [{ id: row.consumableId, label: mapConsumableOptionLabel(row) }, ...consumableOptions.value]
|
||||
consumableOptions.value = [
|
||||
{ id: row.consumableId, label: mapConsumableOptionLabel(row) },
|
||||
...consumableOptions.value
|
||||
]
|
||||
return
|
||||
}
|
||||
// consumableId 为空(后端不存储该字段)— 通过 consumableCode 反查主表真实 ID
|
||||
@@ -264,7 +317,10 @@ export function useTaskStepEditor() {
|
||||
const legacyId = `legacy:${code || name}`
|
||||
row.consumableId = legacyId
|
||||
if (!consumableOptions.value.some((o) => o.id === legacyId))
|
||||
consumableOptions.value = [{ id: legacyId, label: mapConsumableOptionLabel(row) }, ...consumableOptions.value]
|
||||
consumableOptions.value = [
|
||||
{ id: legacyId, label: mapConsumableOptionLabel(row) },
|
||||
...consumableOptions.value
|
||||
]
|
||||
}
|
||||
function ensureAllStepConsumableOptions() {
|
||||
for (const step of stepList.value) {
|
||||
@@ -272,11 +328,19 @@ export function useTaskStepEditor() {
|
||||
}
|
||||
}
|
||||
function onConsumableRowSelect(row: Record<string, any>, consumableId: string) {
|
||||
if (!consumableId) { row.consumableId = ''; return }
|
||||
if (String(consumableId).startsWith('legacy:')) { return }
|
||||
if (!consumableId) {
|
||||
row.consumableId = ''
|
||||
return
|
||||
}
|
||||
if (String(consumableId).startsWith('legacy:')) {
|
||||
return
|
||||
}
|
||||
// 去重:同一耗材不允许出现多次(排除当前行和 legacy 占位符)
|
||||
const dup = (selectedStep.value?.consumables || []).some(
|
||||
(c: Record<string, any>) => c !== row && c.consumableId === consumableId && !String(c.consumableId).startsWith('legacy:')
|
||||
(c: Record<string, any>) =>
|
||||
c !== row &&
|
||||
c.consumableId === consumableId &&
|
||||
!String(c.consumableId).startsWith('legacy:')
|
||||
)
|
||||
if (dup) {
|
||||
ElMessage.warning('该耗材已添加,请勿重复选择')
|
||||
@@ -284,11 +348,16 @@ export function useTaskStepEditor() {
|
||||
return
|
||||
}
|
||||
const master = consumableMasterById.value[consumableId]
|
||||
if (!master) { return }
|
||||
if (!master) {
|
||||
return
|
||||
}
|
||||
Object.assign(row, {
|
||||
consumableId: master.id, consumableCode: master.consumableCode || '',
|
||||
consumableName: master.consumableName || '', specModel: master.specModel || '',
|
||||
standardUsage: master.standardUsage ?? '', unit: master.unit || '',
|
||||
consumableId: master.id,
|
||||
consumableCode: master.consumableCode || '',
|
||||
consumableName: master.consumableName || '',
|
||||
specModel: master.specModel || '',
|
||||
standardUsage: master.standardUsage ?? '',
|
||||
unit: master.unit || '',
|
||||
remark: master.remark ?? master.description ?? ''
|
||||
})
|
||||
ensureItemUnitOption(row.unit)
|
||||
@@ -303,17 +372,46 @@ export function useTaskStepEditor() {
|
||||
async function searchCellProductOptions(query: string) {
|
||||
cellProductOptionsLoading.value = true
|
||||
try {
|
||||
const res = await fetchCellProductList({ page: 1, pageSize: 100, keyword: query?.trim() || undefined })
|
||||
const res = await fetchCellProductList({
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
keyword: query?.trim() || undefined
|
||||
})
|
||||
const list = res.list || []
|
||||
const next = { ...cellProductMasterById.value }
|
||||
for (const p of list) if (p?.id) next[p.id] = p
|
||||
cellProductMasterById.value = next
|
||||
cellProductOptions.value = list
|
||||
.filter((p) => (p.productName || '').trim() || (p.productCode || '').trim())
|
||||
.map((p) => ({ id: p.id, label: mapCellProductOptionLabel(p) }))
|
||||
.map((p) => ({
|
||||
id: p.id,
|
||||
label: mapCellProductOptionLabel(p),
|
||||
productCode: p.productCode || '',
|
||||
productName: p.productName || '',
|
||||
productTypeName: p.productTypeName || '',
|
||||
passage: p.passage || '',
|
||||
storageSlotName: p.storageSlotName || '',
|
||||
productStatus: p.productStatus || ''
|
||||
}))
|
||||
for (const row of taskInputs.value) {
|
||||
if (row.inputType === 'cell_product' && row.inputId && !cellProductOptions.value.some((o) => o.id === row.inputId)) {
|
||||
cellProductOptions.value = [{ id: row.inputId, label: row.inputName || row.inputCode || '' }, ...cellProductOptions.value]
|
||||
if (
|
||||
row.inputType === 'cell_product' &&
|
||||
row.inputId &&
|
||||
!cellProductOptions.value.some((o) => o.id === row.inputId)
|
||||
) {
|
||||
cellProductOptions.value = [
|
||||
{
|
||||
id: row.inputId,
|
||||
label: row.inputName || row.inputCode || '',
|
||||
productCode: row.inputCode || '',
|
||||
productName: row.inputName || '',
|
||||
productTypeName: '',
|
||||
passage: '',
|
||||
storageSlotName: '',
|
||||
productStatus: ''
|
||||
},
|
||||
...cellProductOptions.value
|
||||
]
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
@@ -323,7 +421,10 @@ export function useTaskStepEditor() {
|
||||
}
|
||||
}
|
||||
function onCellProductInputSelect(row: Record<string, any>, productId: string) {
|
||||
if (!productId) { row.inputId = ''; return }
|
||||
if (!productId) {
|
||||
row.inputId = ''
|
||||
return
|
||||
}
|
||||
const master = cellProductMasterById.value[productId]
|
||||
if (!master) return
|
||||
row.inputId = master.id
|
||||
@@ -335,7 +436,9 @@ export function useTaskStepEditor() {
|
||||
for (const step of stepList.value) {
|
||||
;(step.reagents || []).forEach((r: Record<string, any>) => ensureItemUnitOption(r.unit))
|
||||
;(step.consumables || []).forEach((c: Record<string, any>) => ensureItemUnitOption(c.unit))
|
||||
;(step.dataCollections || []).forEach((d: Record<string, any>) => ensureItemUnitOption(d.unit))
|
||||
;(step.dataCollections || []).forEach((d: Record<string, any>) =>
|
||||
ensureItemUnitOption(d.unit)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,16 +446,21 @@ export function useTaskStepEditor() {
|
||||
if (!selectedStepId.value) return null
|
||||
return stepList.value.find((s) => s.id === selectedStepId.value) || null
|
||||
})
|
||||
const virtualInputStatus = computed(() => taskInputs.value.length > 0 ? 'filled' : 'empty')
|
||||
const virtualOutputStatus = computed(() => taskOutputs.value.length > 0 ? 'filled' : 'empty')
|
||||
const completedStepCount = computed(() => stepList.value.filter((s) => s.stepStatus === 'ywc').length)
|
||||
const virtualInputStatus = computed(() => (taskInputs.value.length > 0 ? 'filled' : 'empty'))
|
||||
const virtualOutputStatus = computed(() => (taskOutputs.value.length > 0 ? 'filled' : 'empty'))
|
||||
const completedStepCount = computed(
|
||||
() => stepList.value.filter((s) => s.stepStatus === 'ywc').length
|
||||
)
|
||||
const sopIdsArr = computed({
|
||||
get() {
|
||||
if (!selectedStep.value) return []
|
||||
const raw = selectedStep.value.sopIds
|
||||
if (!raw) return []
|
||||
if (Array.isArray(raw)) return raw.filter(Boolean)
|
||||
return String(raw).split(',').map((s: string) => s.trim()).filter(Boolean)
|
||||
return String(raw)
|
||||
.split(',')
|
||||
.map((s: string) => s.trim())
|
||||
.filter(Boolean)
|
||||
},
|
||||
set(val: string[]) {
|
||||
if (!selectedStep.value) return
|
||||
@@ -370,34 +478,97 @@ export function useTaskStepEditor() {
|
||||
(p.processCode || '').toLowerCase().includes(keyword)
|
||||
)
|
||||
: group.processes
|
||||
return { categoryId: group.categoryId, categoryName: group.categoryName, processes: filtered }
|
||||
return {
|
||||
categoryId: group.categoryId,
|
||||
categoryName: group.categoryName,
|
||||
processes: filtered
|
||||
}
|
||||
})
|
||||
.filter((group) => group.processes.length > 0)
|
||||
})
|
||||
|
||||
async function loadProductTypeAndSpecOptions() {
|
||||
try {
|
||||
const [typeRes, specRes] = await Promise.all([
|
||||
fetchCellProductTypeList({ page: 1, pageSize: 100 }),
|
||||
fetchCellProductSpecList({ page: 1, pageSize: 100 })
|
||||
])
|
||||
productTypeOptions.value = typeRes?.list || typeRes || []
|
||||
specOptions.value = specRes?.list || specRes || []
|
||||
} catch {
|
||||
/* dropdown data failure is non-blocking */
|
||||
}
|
||||
}
|
||||
async function loadStorageSlotOptions() {
|
||||
try {
|
||||
const facilityRes = await fetchStorageFacilityList()
|
||||
const facilities = facilityRes?.list || facilityRes || []
|
||||
const leafNodes = facilities.filter((n: any) => n.isLeaf === 1)
|
||||
if (!leafNodes.length) {
|
||||
storageSlotOptions.value = []
|
||||
return
|
||||
}
|
||||
const results = await Promise.allSettled(
|
||||
leafNodes.map(async (node: any) => {
|
||||
const res: any = await fetchStorageSlotList({ facilityNodeId: node.id })
|
||||
const slots = res?.list || res || []
|
||||
const nodePath = buildTreePathLabelFromFlat(node.id, facilities, { nameKey: 'nodeName' })
|
||||
return slots.map((slot: any) => {
|
||||
const code = slot.slotCode || ''
|
||||
const name = nodePath && code ? `${nodePath} / ${code}` : code || nodePath
|
||||
return { id: slot.id, name }
|
||||
})
|
||||
})
|
||||
)
|
||||
const options: Array<{ id: string; name: string }> = []
|
||||
results.forEach((r) => {
|
||||
if (r.status === 'fulfilled') options.push(...r.value)
|
||||
})
|
||||
storageSlotOptions.value = options
|
||||
} catch {
|
||||
storageSlotOptions.value = []
|
||||
}
|
||||
}
|
||||
async function loadConfigReferenceData() {
|
||||
const [processes, categories, sops, outputTypes] = await Promise.all([
|
||||
fetchAllProcesses(), fetchAllProcessCategories(),
|
||||
fetchAllSopDocuments(), fetchAllOutputTypes()
|
||||
fetchAllProcesses(),
|
||||
fetchAllProcessCategories(),
|
||||
fetchAllSopDocuments(),
|
||||
fetchAllOutputTypes()
|
||||
])
|
||||
categoryOptions.value = categories || []
|
||||
sopOptions.value = sops || []
|
||||
outputTypeOptions.value = outputTypes || []
|
||||
|
||||
const catMap: Record<string, { categoryId: string; categoryName: string; processes: Record<string, any>[] }> = {}
|
||||
const catMap: Record<
|
||||
string,
|
||||
{ categoryId: string; categoryName: string; processes: Record<string, any>[] }
|
||||
> = {}
|
||||
const uKey = '__uncategorized__'
|
||||
catMap[uKey] = { categoryId: uKey, categoryName: '未分类', processes: [] }
|
||||
;(categories || []).forEach((cat: Record<string, any>) => {
|
||||
catMap[cat.id] = { categoryId: cat.id, categoryName: cat.categoryName || cat.name || '未命名分类', processes: [] }
|
||||
catMap[cat.id] = {
|
||||
categoryId: cat.id,
|
||||
categoryName: cat.categoryName || cat.name || '未命名分类',
|
||||
processes: []
|
||||
}
|
||||
})
|
||||
;(processes || []).forEach((proc: Record<string, any>) => {
|
||||
const catId = proc.categoryId || uKey
|
||||
if (!catMap[catId]) catMap[catId] = { categoryId: catId, categoryName: '未知分类', processes: [] }
|
||||
if (!catMap[catId])
|
||||
catMap[catId] = { categoryId: catId, categoryName: '未知分类', processes: [] }
|
||||
catMap[catId].processes.push(proc)
|
||||
})
|
||||
expandedCategories.value = Object.keys(catMap)
|
||||
processLibrary.value = Object.values(catMap)
|
||||
await Promise.all([searchItemUnitOptions(''), searchReagentOptions(''), searchConsumableOptions(''), searchCellProductOptions('')])
|
||||
await Promise.all([
|
||||
searchItemUnitOptions(''),
|
||||
searchReagentOptions(''),
|
||||
searchConsumableOptions(''),
|
||||
searchCellProductOptions(''),
|
||||
loadProductTypeAndSpecOptions(),
|
||||
loadStorageSlotOptions()
|
||||
])
|
||||
}
|
||||
async function loadTaskDetail(overrideId?: string) {
|
||||
const id = overrideId || taskId.value
|
||||
@@ -417,8 +588,15 @@ export function useTaskStepEditor() {
|
||||
taskInputs.value = (detail.inputs || []).map((i: Record<string, any>) => ({ ...i }))
|
||||
taskOutputs.value = (detail.outputs || []).map((o: Record<string, any>) => ({ ...o }))
|
||||
for (const row of taskInputs.value) {
|
||||
if (row.inputType === 'cell_product' && row.inputId && !cellProductOptions.value.some((o) => o.id === row.inputId)) {
|
||||
cellProductOptions.value = [{ id: row.inputId, label: row.inputName || row.inputCode || '' }, ...cellProductOptions.value]
|
||||
if (
|
||||
row.inputType === 'cell_product' &&
|
||||
row.inputId &&
|
||||
!cellProductOptions.value.some((o) => o.id === row.inputId)
|
||||
) {
|
||||
cellProductOptions.value = [
|
||||
{ id: row.inputId, label: row.inputName || row.inputCode || '' },
|
||||
...cellProductOptions.value
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -440,7 +618,9 @@ export function useTaskStepEditor() {
|
||||
await loadTaskDetail()
|
||||
if (newStep?.id) selectedStepId.value = newStep.id
|
||||
ElMessage.success('步骤添加成功')
|
||||
} catch { /* ignore */ } finally {
|
||||
} catch {
|
||||
/* ignore */
|
||||
} finally {
|
||||
stepListLoading.value = false
|
||||
}
|
||||
}
|
||||
@@ -449,8 +629,13 @@ export function useTaskStepEditor() {
|
||||
if (!taskId.value) return
|
||||
stepListLoading.value = true
|
||||
try {
|
||||
await fetchReorderTaskSteps({ taskId: taskId.value, stepIds: stepList.value.map((s) => s.id) })
|
||||
} catch { /* ignore */ } finally {
|
||||
await fetchReorderTaskSteps({
|
||||
taskId: taskId.value,
|
||||
stepIds: stepList.value.map((s) => s.id)
|
||||
})
|
||||
} catch {
|
||||
/* ignore */
|
||||
} finally {
|
||||
stepListLoading.value = false
|
||||
}
|
||||
}
|
||||
@@ -469,7 +654,9 @@ export function useTaskStepEditor() {
|
||||
await loadTaskDetail()
|
||||
if (ns?.id) selectedStepId.value = ns.id
|
||||
ElMessage.success('步骤已复制')
|
||||
} catch (e: any) { ElMessage.error(e?.message || '复制失败') } finally {
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || '复制失败')
|
||||
} finally {
|
||||
stepListLoading.value = false
|
||||
}
|
||||
}
|
||||
@@ -482,7 +669,9 @@ export function useTaskStepEditor() {
|
||||
if (selectedStepId.value === step.id)
|
||||
selectedStepId.value = stepList.value.length > 0 ? stepList.value[0].id : ''
|
||||
ElMessage.success('步骤已删除')
|
||||
} catch { /* ignore */ } finally {
|
||||
} catch {
|
||||
/* ignore */
|
||||
} finally {
|
||||
stepListLoading.value = false
|
||||
}
|
||||
}
|
||||
@@ -498,7 +687,9 @@ export function useTaskStepEditor() {
|
||||
estimated_duration: selectedStep.value.estimatedDuration,
|
||||
sop_ids: selectedStep.value.sopIds
|
||||
})
|
||||
} catch { /* ignore */ }
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
function onSopIdsChange(val: string[]) {
|
||||
if (!selectedStep.value) return
|
||||
@@ -509,84 +700,135 @@ export function useTaskStepEditor() {
|
||||
if (!selectedStep.value) return
|
||||
if (!selectedStep.value.reagents) selectedStep.value.reagents = []
|
||||
selectedStep.value.reagents.push({
|
||||
reagentId: '', reagentCode: '', reagentName: '', spec: '',
|
||||
standardUsage: '', unit: '', isKeyMaterial: false, remark: ''
|
||||
reagentId: '',
|
||||
reagentCode: '',
|
||||
reagentName: '',
|
||||
spec: '',
|
||||
standardUsage: '',
|
||||
unit: '',
|
||||
isKeyMaterial: false,
|
||||
remark: ''
|
||||
})
|
||||
}
|
||||
function removeReagentRow(i: number) { selectedStep.value?.reagents?.splice(i, 1) }
|
||||
function removeReagentRow(i: number) {
|
||||
selectedStep.value?.reagents?.splice(i, 1)
|
||||
}
|
||||
async function saveReagents() {
|
||||
if (!selectedStep.value || configSuppressSave.value) return
|
||||
try {
|
||||
const items = (selectedStep.value.reagents || []).map((r: Record<string, any>) => ({
|
||||
material_code: r.reagentCode, material_name: r.reagentName, spec: r.spec,
|
||||
standard_usage: r.standardUsage, actual_usage: r.actualUsage || '',
|
||||
material_code: r.reagentCode,
|
||||
material_name: r.reagentName,
|
||||
spec: r.spec,
|
||||
standard_usage: r.standardUsage,
|
||||
actual_usage: r.actualUsage || '',
|
||||
unit: r.unit,
|
||||
is_key_material: toApiFlag(r.isKeyMaterial), remark: r.remark || ''
|
||||
is_key_material: toApiFlag(r.isKeyMaterial),
|
||||
remark: r.remark || ''
|
||||
}))
|
||||
await fetchReplaceStepReagents(taskId.value, selectedStep.value.id, items)
|
||||
} catch { /* ignore */ }
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
function addConsumableRow() {
|
||||
if (!selectedStep.value) return
|
||||
if (!selectedStep.value.consumables) selectedStep.value.consumables = []
|
||||
selectedStep.value.consumables.push({
|
||||
consumableId: '', consumableCode: '', consumableName: '', specModel: '',
|
||||
standardUsage: '', unit: '', isRequired: false, remark: ''
|
||||
consumableId: '',
|
||||
consumableCode: '',
|
||||
consumableName: '',
|
||||
specModel: '',
|
||||
standardUsage: '',
|
||||
unit: '',
|
||||
isRequired: false,
|
||||
remark: ''
|
||||
})
|
||||
}
|
||||
function removeConsumableRow(i: number) { selectedStep.value?.consumables?.splice(i, 1) }
|
||||
function removeConsumableRow(i: number) {
|
||||
selectedStep.value?.consumables?.splice(i, 1)
|
||||
}
|
||||
async function saveConsumables() {
|
||||
if (!selectedStep.value || configSuppressSave.value) return
|
||||
try {
|
||||
const items = (selectedStep.value.consumables || []).map((c: Record<string, any>) => ({
|
||||
material_code: c.consumableCode, material_name: c.consumableName,
|
||||
material_code: c.consumableCode,
|
||||
material_name: c.consumableName,
|
||||
spec_model: c.specModel,
|
||||
standard_usage: c.standardUsage, actual_usage: c.actualUsage || '',
|
||||
standard_usage: c.standardUsage,
|
||||
actual_usage: c.actualUsage || '',
|
||||
unit: c.unit,
|
||||
is_required: toApiFlag(c.isRequired), remark: c.remark || ''
|
||||
is_required: toApiFlag(c.isRequired),
|
||||
remark: c.remark || ''
|
||||
}))
|
||||
await fetchReplaceStepConsumables(taskId.value, selectedStep.value.id, items)
|
||||
} catch { /* ignore */ }
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
function addOutputRow() {
|
||||
if (!selectedStep.value) return
|
||||
if (!selectedStep.value.outputs) selectedStep.value.outputs = []
|
||||
selectedStep.value.outputs.push({ outputTypeId: '', outputName: '', outputBarcode: '', remark: '' })
|
||||
selectedStep.value.outputs.push({
|
||||
outputTypeId: '',
|
||||
outputName: '',
|
||||
outputBarcode: '',
|
||||
remark: ''
|
||||
})
|
||||
}
|
||||
function removeOutputRow(i: number) {
|
||||
selectedStep.value?.outputs?.splice(i, 1)
|
||||
}
|
||||
function removeOutputRow(i: number) { selectedStep.value?.outputs?.splice(i, 1) }
|
||||
async function saveOutputs() {
|
||||
if (!selectedStep.value || configSuppressSave.value) return
|
||||
try {
|
||||
const items = (selectedStep.value.outputs || []).map((o: Record<string, any>) => ({
|
||||
output_type_id: o.outputTypeId, output_name: o.outputName,
|
||||
output_barcode: o.outputBarcode, remark: o.remark || ''
|
||||
output_type_id: o.outputTypeId,
|
||||
output_name: o.outputName,
|
||||
output_barcode: o.outputBarcode,
|
||||
remark: o.remark || ''
|
||||
}))
|
||||
await fetchReplaceStepOutputs(taskId.value, selectedStep.value.id, items)
|
||||
} catch { /* ignore */ }
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
function addDataCollectionRow() {
|
||||
if (!selectedStep.value) return
|
||||
if (!selectedStep.value.dataCollections) selectedStep.value.dataCollections = []
|
||||
selectedStep.value.dataCollections.push({ collectionName: '', unit: '', orderNum: 0, remark: '' })
|
||||
selectedStep.value.dataCollections.push({
|
||||
collectionName: '',
|
||||
unit: '',
|
||||
orderNum: 0,
|
||||
remark: ''
|
||||
})
|
||||
}
|
||||
function removeDataCollectionRow(i: number) {
|
||||
selectedStep.value?.dataCollections?.splice(i, 1)
|
||||
}
|
||||
function removeDataCollectionRow(i: number) { selectedStep.value?.dataCollections?.splice(i, 1) }
|
||||
async function saveDataCollections() {
|
||||
if (!selectedStep.value || configSuppressSave.value) return
|
||||
try {
|
||||
const items = (selectedStep.value.dataCollections || []).map((d: Record<string, any>) => ({
|
||||
collection_name: d.collectionName, unit: d.unit,
|
||||
collection_value: d.collectionValue || '', remark: d.remark || ''
|
||||
collection_name: d.collectionName,
|
||||
unit: d.unit,
|
||||
collection_value: d.collectionValue || '',
|
||||
remark: d.remark || ''
|
||||
}))
|
||||
await fetchReplaceStepDataCollection(taskId.value, selectedStep.value.id, items)
|
||||
} catch { /* ignore */ }
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
const manualSaveLoading = ref(false)
|
||||
const stepActionLoading = ref(false)
|
||||
const taskActionLoading = ref(false)
|
||||
async function handleManualSave() {
|
||||
if (!selectedStep.value) return
|
||||
if (!selectedStep.value || manualSaveLoading.value) return
|
||||
manualSaveLoading.value = true
|
||||
try {
|
||||
await Promise.all([
|
||||
@@ -605,34 +847,105 @@ export function useTaskStepEditor() {
|
||||
}
|
||||
|
||||
async function handleStartStep(step: Record<string, any>) {
|
||||
if (!taskId.value || !step.id) return
|
||||
await fetchUpdateTaskStep(taskId.value, step.id, { step_status: 'jxz' })
|
||||
ElMessage.success('步骤已开始执行')
|
||||
await loadTaskDetail()
|
||||
if (!taskId.value || !step.id || stepActionLoading.value) return
|
||||
stepActionLoading.value = true
|
||||
try {
|
||||
await fetchUpdateTaskStep(taskId.value, step.id, { step_status: 'jxz' })
|
||||
ElMessage.success('步骤已开始执行')
|
||||
await loadTaskDetail()
|
||||
} catch {
|
||||
/* global error handling */
|
||||
} finally {
|
||||
stepActionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCompleteStep(step: Record<string, any>) {
|
||||
if (!taskId.value || !step.id) return
|
||||
await ElMessageBox.confirm('确定完成该步骤吗?', '提示', { type: 'warning' })
|
||||
await fetchUpdateTaskStep(taskId.value, step.id, { step_status: 'ywc' })
|
||||
ElMessage.success('步骤已完成')
|
||||
await loadTaskDetail()
|
||||
if (!taskId.value || !step.id || stepActionLoading.value) return
|
||||
try {
|
||||
await ElMessageBox.confirm('确定完成该步骤吗?', '提示', { type: 'warning' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
stepActionLoading.value = true
|
||||
try {
|
||||
await fetchUpdateTaskStep(taskId.value, step.id, { step_status: 'ywc' })
|
||||
ElMessage.success('步骤已完成')
|
||||
await loadTaskDetail()
|
||||
} catch {
|
||||
/* global error handling */
|
||||
} finally {
|
||||
stepActionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleStartTask() {
|
||||
if (!taskDetail.value?.id) return
|
||||
await ElMessageBox.confirm('确定开始执行该任务吗?', '提示', { type: 'warning' })
|
||||
await fetchUpdateProductionTaskStatus(taskDetail.value.id, { taskStatus: 'jxz' })
|
||||
ElMessage.success('任务已开始执行')
|
||||
await loadTaskDetail(taskDetail.value.id)
|
||||
if (!taskDetail.value?.id || taskActionLoading.value) return
|
||||
try {
|
||||
await ElMessageBox.confirm('确定开始执行该任务吗?', '提示', { type: 'warning' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
taskActionLoading.value = true
|
||||
try {
|
||||
await fetchUpdateProductionTaskStatus(taskDetail.value.id, { taskStatus: 'jxz' })
|
||||
ElMessage.success('任务已开始执行')
|
||||
await loadTaskDetail(taskDetail.value.id)
|
||||
} catch {
|
||||
/* global error handling */
|
||||
} finally {
|
||||
taskActionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function validateTaskIoRequired(): string | null {
|
||||
if (!taskInputs.value.length) {
|
||||
return '请先填写原材料(样本/细胞产品),至少添加一项并保存'
|
||||
}
|
||||
if (!taskOutputs.value.length) {
|
||||
return '请先填写产出(细胞产品),至少添加一项并保存'
|
||||
}
|
||||
const invalidInput = taskInputs.value.find(
|
||||
(row) =>
|
||||
!row.inputType ||
|
||||
(!(row.inputId || '').trim() &&
|
||||
!(row.inputCode || '').trim() &&
|
||||
!(row.inputName || '').trim())
|
||||
)
|
||||
if (invalidInput) {
|
||||
return '原材料存在未填完整的行,请补全类型与编码/名称后保存'
|
||||
}
|
||||
const invalidOutput = taskOutputs.value.find(
|
||||
(row) => !(row.outputName || '').trim() || !(row.outputBarcode || '').trim()
|
||||
)
|
||||
if (invalidOutput) {
|
||||
return '产出存在未填完整的行,请补全输出名称与条码后保存'
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
async function handleCompleteTask() {
|
||||
if (!taskDetail.value?.id) return
|
||||
await ElMessageBox.confirm('确定完成该任务吗?完成后不可恢复。', '提示', { type: 'warning' })
|
||||
await fetchUpdateProductionTaskStatus(taskDetail.value.id, { taskStatus: 'ywc' })
|
||||
ElMessage.success('任务已完成')
|
||||
await loadTaskDetail(taskDetail.value.id)
|
||||
if (!taskDetail.value?.id || taskActionLoading.value) return
|
||||
const ioErr = validateTaskIoRequired()
|
||||
if (ioErr) {
|
||||
await ElMessageBox.alert(ioErr, '无法完成任务', { type: 'warning' })
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm('确定完成该任务吗?完成后不可恢复。', '提示', { type: 'warning' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
taskActionLoading.value = true
|
||||
try {
|
||||
await fetchUpdateProductionTaskStatus(taskDetail.value.id, { taskStatus: 'ywc' })
|
||||
ElMessage.success('任务已完成')
|
||||
await loadTaskDetail(taskDetail.value.id)
|
||||
} catch {
|
||||
/* global error handling */
|
||||
} finally {
|
||||
taskActionLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function addTaskInputRow() {
|
||||
@@ -646,27 +959,68 @@ export function useTaskStepEditor() {
|
||||
remark: ''
|
||||
})
|
||||
}
|
||||
function removeTaskInputRow(i: number) { taskInputs.value.splice(i, 1) }
|
||||
function removeTaskInputRow(i: number) {
|
||||
taskInputs.value.splice(i, 1)
|
||||
}
|
||||
async function saveTaskInputs() {
|
||||
if (!taskDetail.value?.id) return
|
||||
if (!taskInputs.value.length) {
|
||||
ElMessage.warning('请至少添加一项原材料(样本/细胞产品)')
|
||||
return
|
||||
}
|
||||
const invalid = taskInputs.value.find(
|
||||
(row) =>
|
||||
!row.inputType ||
|
||||
(!(row.inputId || '').trim() &&
|
||||
!(row.inputCode || '').trim() &&
|
||||
!(row.inputName || '').trim())
|
||||
)
|
||||
if (invalid) {
|
||||
ElMessage.warning('请补全每行原材料的类型与编码/名称')
|
||||
return
|
||||
}
|
||||
inputSaving.value = true
|
||||
try {
|
||||
await fetchSetTaskInputs(taskDetail.value.id, taskInputs.value)
|
||||
ElMessage.success('输入已保存')
|
||||
} catch { /* global error handling */ } finally { inputSaving.value = false }
|
||||
await loadTaskDetail()
|
||||
} catch {
|
||||
/* global error handling */
|
||||
} finally {
|
||||
inputSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function addTaskOutputRow() {
|
||||
taskOutputs.value.push({ cellProductId: '', storageSlotId: '', outputName: '', outputBarcode: '', quantity: 0, unit: '', remark: '' })
|
||||
taskOutputs.value.push({
|
||||
cellProductId: '',
|
||||
storageSlotId: '',
|
||||
outputName: '',
|
||||
outputBarcode: '',
|
||||
quantity: 0,
|
||||
unit: '',
|
||||
remark: ''
|
||||
})
|
||||
}
|
||||
function removeTaskOutputRow(i: number) {
|
||||
taskOutputs.value.splice(i, 1)
|
||||
}
|
||||
function removeTaskOutputRow(i: number) { taskOutputs.value.splice(i, 1) }
|
||||
async function saveTaskOutputs() {
|
||||
if (!taskDetail.value?.id) return
|
||||
if (!taskOutputs.value.length) {
|
||||
ElMessage.warning('请至少添加一项产出(细胞产品)')
|
||||
return
|
||||
}
|
||||
outputSaving.value = true
|
||||
try {
|
||||
await fetchSetTaskOutputs(taskDetail.value.id, taskOutputs.value)
|
||||
ElMessage.success('输出已保存')
|
||||
} catch { /* global error handling */ } finally { outputSaving.value = false }
|
||||
ElMessage.success('产出已保存')
|
||||
await loadTaskDetail()
|
||||
} catch {
|
||||
/* global error handling */
|
||||
} finally {
|
||||
outputSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function taskStatusTag(status: string) {
|
||||
@@ -693,43 +1047,100 @@ export function useTaskStepEditor() {
|
||||
activeSubTab.value = 'reagent'
|
||||
processSearchKeyword.value = ''
|
||||
configSuppressSave.value = true
|
||||
stepList.value = []
|
||||
stepListLoading.value = true
|
||||
try {
|
||||
await loadConfigReferenceData()
|
||||
await loadTaskDetail()
|
||||
} finally {
|
||||
stepListLoading.value = false
|
||||
await nextTick()
|
||||
configSuppressSave.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
taskId, stepList, selectedStepId, selectedStep, stepListLoading,
|
||||
activeSubTab, processSearchKeyword, configSuppressSave,
|
||||
mode, taskDetail, taskInputs, taskOutputs, inputSaving, outputSaving,
|
||||
selectedVirtualStep, processLibraryExpanded,
|
||||
virtualInputStatus, virtualOutputStatus, completedStepCount,
|
||||
processLibrary, filteredProcessLibrary, expandedCategories,
|
||||
categoryOptions, sopOptions, outputTypeOptions,
|
||||
itemUnitOptions, itemUnitOptionsLoading,
|
||||
reagentOptions, reagentOptionsLoading,
|
||||
consumableOptions, consumableOptionsLoading,
|
||||
cellProductOptions, cellProductOptionsLoading,
|
||||
taskId,
|
||||
stepList,
|
||||
selectedStepId,
|
||||
selectedStep,
|
||||
stepListLoading,
|
||||
activeSubTab,
|
||||
processSearchKeyword,
|
||||
configSuppressSave,
|
||||
mode,
|
||||
taskDetail,
|
||||
taskInputs,
|
||||
taskOutputs,
|
||||
inputSaving,
|
||||
outputSaving,
|
||||
selectedVirtualStep,
|
||||
processLibraryExpanded,
|
||||
virtualInputStatus,
|
||||
virtualOutputStatus,
|
||||
completedStepCount,
|
||||
processLibrary,
|
||||
filteredProcessLibrary,
|
||||
expandedCategories,
|
||||
categoryOptions,
|
||||
sopOptions,
|
||||
outputTypeOptions,
|
||||
itemUnitOptions,
|
||||
itemUnitOptionsLoading,
|
||||
reagentOptions,
|
||||
reagentOptionsLoading,
|
||||
consumableOptions,
|
||||
consumableOptionsLoading,
|
||||
cellProductOptions,
|
||||
cellProductOptionsLoading,
|
||||
productTypeOptions,
|
||||
specOptions,
|
||||
storageSlotOptions,
|
||||
sopIdsArr,
|
||||
onDragStart, onDropToCenter, onStepDragEnd, selectStep, selectVirtualStep,
|
||||
handleCopyStep, handleDeleteStep,
|
||||
handleStartStep, handleCompleteStep,
|
||||
handleStartTask, handleCompleteTask,
|
||||
addTaskInputRow, removeTaskInputRow, saveTaskInputs,
|
||||
addTaskOutputRow, removeTaskOutputRow, saveTaskOutputs,
|
||||
taskStatusTag, stepStatusTag,
|
||||
saveStepBasic, onSopIdsChange,
|
||||
searchItemUnitOptions, searchReagentOptions, searchConsumableOptions, searchCellProductOptions,
|
||||
onReagentRowSelect, onConsumableRowSelect, onCellProductInputSelect,
|
||||
addReagentRow, removeReagentRow, saveReagents,
|
||||
addConsumableRow, removeConsumableRow, saveConsumables,
|
||||
addOutputRow, removeOutputRow, saveOutputs,
|
||||
addDataCollectionRow, removeDataCollectionRow, saveDataCollections,
|
||||
manualSaveLoading, handleManualSave,
|
||||
onDragStart,
|
||||
onDropToCenter,
|
||||
onStepDragEnd,
|
||||
selectStep,
|
||||
selectVirtualStep,
|
||||
handleCopyStep,
|
||||
handleDeleteStep,
|
||||
handleStartStep,
|
||||
handleCompleteStep,
|
||||
handleStartTask,
|
||||
handleCompleteTask,
|
||||
addTaskInputRow,
|
||||
removeTaskInputRow,
|
||||
saveTaskInputs,
|
||||
addTaskOutputRow,
|
||||
removeTaskOutputRow,
|
||||
saveTaskOutputs,
|
||||
taskStatusTag,
|
||||
stepStatusTag,
|
||||
saveStepBasic,
|
||||
onSopIdsChange,
|
||||
searchItemUnitOptions,
|
||||
searchReagentOptions,
|
||||
searchConsumableOptions,
|
||||
searchCellProductOptions,
|
||||
onReagentRowSelect,
|
||||
onConsumableRowSelect,
|
||||
onCellProductInputSelect,
|
||||
addReagentRow,
|
||||
removeReagentRow,
|
||||
saveReagents,
|
||||
addConsumableRow,
|
||||
removeConsumableRow,
|
||||
saveConsumables,
|
||||
addOutputRow,
|
||||
removeOutputRow,
|
||||
saveOutputs,
|
||||
addDataCollectionRow,
|
||||
removeDataCollectionRow,
|
||||
saveDataCollections,
|
||||
manualSaveLoading,
|
||||
stepActionLoading,
|
||||
taskActionLoading,
|
||||
handleManualSave,
|
||||
initEditor
|
||||
}
|
||||
}
|
||||
@@ -138,9 +138,14 @@
|
||||
<SystemViewDialog
|
||||
v-model:visible="viewVisible"
|
||||
title="工单详情"
|
||||
width="1120px"
|
||||
:grid-columns="4"
|
||||
compact
|
||||
:loading="viewLoading"
|
||||
:icon="Document"
|
||||
:meta="detail?.workOrderNo || ''"
|
||||
:sections="viewSections"
|
||||
modal-class="qc-work-order-view-dialog-mask"
|
||||
>
|
||||
<template #reception-extra>
|
||||
<div class="view-reception-attachments">
|
||||
@@ -156,6 +161,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-for="task in viewTasksWithAttachments" #[taskAttachmentSlotName(task.id)]>
|
||||
<div :key="task.id" class="qc-work-order-task-attachments">
|
||||
<ProtocolAttachmentReadonly
|
||||
:files="task.attachmentFiles || []"
|
||||
@preview="handleTaskAttachmentPreview"
|
||||
@download="handleTaskAttachmentDownload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</SystemViewDialog>
|
||||
</section>
|
||||
</div>
|
||||
@@ -174,15 +188,19 @@
|
||||
type QcWorkOrder,
|
||||
type QcWorkOrderDetail
|
||||
} from '@/api/qc-work-order'
|
||||
import { fetchQcTaskResults, type QcTaskResultItem } from '@/api/qc-task-result'
|
||||
import type { QcTask } from '@/api/qc-task'
|
||||
import {
|
||||
workOrderStatusLabel,
|
||||
receptionSourceLabel,
|
||||
acceptConclusionLabel,
|
||||
transportTypeLabel
|
||||
transportTypeLabel,
|
||||
taskStatusLabel,
|
||||
qcTaskConclusionLabel
|
||||
} from '@/utils/qc-labels'
|
||||
import ProtocolAttachmentReadonly from '@/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue'
|
||||
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
|
||||
import type { ViewSection } from '@/components/business/system-view-dialog/index.vue'
|
||||
import type { ViewField, ViewSection } from '@/components/business/system-view-dialog/index.vue'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
|
||||
defineOptions({ name: 'QualityInspectionQcWorkOrder' })
|
||||
@@ -217,9 +235,20 @@
|
||||
})
|
||||
|
||||
const viewVisible = ref(false)
|
||||
const viewLoading = ref(false)
|
||||
const detail = ref<QcWorkOrderDetail | null>(null)
|
||||
type QcWorkOrderTaskView = QcTask & { results?: QcTaskResultItem[] }
|
||||
const viewTasks = ref<QcWorkOrderTaskView[]>([])
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
const viewTasksWithAttachments = computed(() =>
|
||||
viewTasks.value.filter((t) => (t.attachmentFiles?.length || 0) > 0)
|
||||
)
|
||||
|
||||
function taskAttachmentSlotName(taskId: string) {
|
||||
return `task-att-${taskId}`
|
||||
}
|
||||
|
||||
const viewReceptionAttachmentFiles = computed(() => {
|
||||
const r = detail.value?.reception
|
||||
if (!r) return []
|
||||
@@ -255,6 +284,125 @@
|
||||
downloadAttachmentFile(file, { fullFileUrl: receptionAttachmentFullUrl })
|
||||
}
|
||||
|
||||
function taskAttachmentFullUrl(url: string) {
|
||||
const u = String(url || '').trim()
|
||||
if (!u) return ''
|
||||
if (/^https?:\/\//i.test(u)) return u
|
||||
return minioBaseUrl ? `${minioBaseUrl}/${u.replace(/^\//, '')}` : u
|
||||
}
|
||||
|
||||
function handleTaskAttachmentPreview(file: Record<string, unknown>) {
|
||||
const url = taskAttachmentFullUrl(String(file.fileUrl || ''))
|
||||
if (url) window.open(url, '_blank')
|
||||
}
|
||||
|
||||
function handleTaskAttachmentDownload(file: Record<string, unknown>) {
|
||||
downloadAttachmentFile(file, { fullFileUrl: taskAttachmentFullUrl })
|
||||
}
|
||||
|
||||
function conclusionTagType(conclusion?: string) {
|
||||
if (conclusion === 'tg') return 'success' as const
|
||||
if (conclusion === 'btg') return 'danger' as const
|
||||
return 'info' as const
|
||||
}
|
||||
|
||||
function taskStatusTagType(status?: string) {
|
||||
if (status === 'ywc') return 'success' as const
|
||||
if (status === 'jxz') return 'warning' as const
|
||||
return 'info' as const
|
||||
}
|
||||
|
||||
function formatConfiguredLabItems(d: QcWorkOrderDetail) {
|
||||
return (
|
||||
d.labItems?.map((li) => `${li.itemName || ''}(${li.itemCode || ''})`).join('、') || '无'
|
||||
)
|
||||
}
|
||||
|
||||
function formatConfiguredThirdItems(d: QcWorkOrderDetail) {
|
||||
return (
|
||||
d.thirdPartyItems
|
||||
?.map((li) => `${li.companyName || ''} — ${li.itemName || ''}(¥${li.feeAmount ?? '—'})`)
|
||||
.join('、') || '无'
|
||||
)
|
||||
}
|
||||
|
||||
function buildTaskTypeSections(
|
||||
tasks: QcWorkOrderTaskView[],
|
||||
taskType: 'sysz' | 'dsfjc',
|
||||
sectionTitle: string
|
||||
): ViewSection[] {
|
||||
const filtered = tasks.filter((t) => t.taskType === taskType)
|
||||
if (!filtered.length) return []
|
||||
|
||||
return filtered.map((task, index) => {
|
||||
const title =
|
||||
filtered.length > 1 ? `${sectionTitle}(${index + 1})` : sectionTitle
|
||||
const fields: ViewField[] = [
|
||||
{ label: '检测项目', value: task.itemName || '—' },
|
||||
{ label: '项目编码', value: task.itemCode || '—' },
|
||||
{
|
||||
label: '任务状态',
|
||||
value: taskStatusLabel(task.taskStatus || ''),
|
||||
tagType: taskStatusTagType(task.taskStatus)
|
||||
},
|
||||
{
|
||||
label: '检测结论',
|
||||
value: task.conclusionLabel || qcTaskConclusionLabel(task.conclusion || '') || '—',
|
||||
tagType: conclusionTagType(task.conclusion)
|
||||
},
|
||||
{ label: '任务号', value: task.taskNo || '—' },
|
||||
{ label: '完成时间', value: task.completedTime || '—' }
|
||||
]
|
||||
if (taskType === 'dsfjc') {
|
||||
fields.splice(2, 0, { label: '第三方公司', value: task.companyName || '—' })
|
||||
if (task.feeAmount != null) {
|
||||
fields.push({ label: '费用(元)', value: String(task.feeAmount) })
|
||||
}
|
||||
}
|
||||
if (task.results?.length) {
|
||||
for (const item of task.results) {
|
||||
fields.push({
|
||||
label: item.paramName + (item.paramUnit ? `(${item.paramUnit})` : ''),
|
||||
value: item.paramValue || '—'
|
||||
})
|
||||
}
|
||||
} else if (taskType === 'sysz') {
|
||||
fields.push({ label: '检测参数', value: '—' })
|
||||
}
|
||||
fields.push({ label: '备注', value: task.remark || '—', span: true })
|
||||
|
||||
const section: ViewSection = { title, fields }
|
||||
if (task.attachmentFiles?.length) {
|
||||
section.slot = taskAttachmentSlotName(task.id)
|
||||
section.slotTitle = taskType === 'dsfjc' ? '检测材料' : '检测附件'
|
||||
section.slotTitleStyle = 'field'
|
||||
} else if (taskType === 'dsfjc') {
|
||||
fields.push({ label: '检测材料', value: '—' })
|
||||
} else {
|
||||
fields.push({ label: '检测附件', value: '—' })
|
||||
}
|
||||
return section
|
||||
})
|
||||
}
|
||||
|
||||
function buildConfiguredOnlySection(
|
||||
title: string,
|
||||
configuredLabel: string,
|
||||
configuredValue: string,
|
||||
attachmentLabel: string
|
||||
): ViewSection {
|
||||
return {
|
||||
title,
|
||||
fields: [
|
||||
{ label: configuredLabel, value: configuredValue, span: true },
|
||||
{ label: '检测结论', value: '—' },
|
||||
{ label: '检测参数', value: '—' },
|
||||
{ label: '备注', value: '—' },
|
||||
{ label: attachmentLabel, value: '—' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function formatDetectMethods(row: QcWorkOrder) {
|
||||
const parts: string[] = []
|
||||
if (row.hasLab) parts.push('实验室')
|
||||
@@ -342,28 +490,23 @@
|
||||
{ label: '备注', value: r.remark || '—', span: true }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '实验室项目',
|
||||
fields: [
|
||||
{
|
||||
label: '检测项目',
|
||||
value: d.labItems?.map((li) => li.itemName + '(' + li.itemCode + ')').join('、') || '无',
|
||||
span: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '第三方项目',
|
||||
fields: [
|
||||
{
|
||||
label: '检测项目',
|
||||
value:
|
||||
d.thirdPartyItems?.map((li) => li.companyName + ' — ' + li.itemName + '(¥' + li.feeAmount + ')').join('、') ||
|
||||
'无',
|
||||
span: true
|
||||
}
|
||||
]
|
||||
}
|
||||
...(viewTasks.value.some((t) => t.taskType === 'sysz')
|
||||
? buildTaskTypeSections(viewTasks.value, 'sysz', '实验室项目')
|
||||
: d.hasLab
|
||||
? [buildConfiguredOnlySection('实验室项目', '已配置项目', formatConfiguredLabItems(d), '检测附件')]
|
||||
: []),
|
||||
...(viewTasks.value.some((t) => t.taskType === 'dsfjc')
|
||||
? buildTaskTypeSections(viewTasks.value, 'dsfjc', '第三方项目')
|
||||
: d.hasThirdParty
|
||||
? [
|
||||
buildConfiguredOnlySection(
|
||||
'第三方项目',
|
||||
'已配置项目',
|
||||
formatConfiguredThirdItems(d),
|
||||
'检测材料'
|
||||
)
|
||||
]
|
||||
: [])
|
||||
]
|
||||
})
|
||||
|
||||
@@ -433,8 +576,30 @@
|
||||
}
|
||||
|
||||
async function handleView(row: QcWorkOrder) {
|
||||
detail.value = await fetchQcWorkOrderDetail(row.id)
|
||||
viewVisible.value = true
|
||||
viewLoading.value = true
|
||||
viewTasks.value = []
|
||||
try {
|
||||
const d = await fetchQcWorkOrderDetail(row.id)
|
||||
detail.value = d
|
||||
const rawTasks = (d.tasks || []) as QcWorkOrderTaskView[]
|
||||
const enriched = await Promise.all(
|
||||
rawTasks.map(async (task) => {
|
||||
if (task.taskType !== 'sysz' || task.taskStatus !== 'ywc') {
|
||||
return { ...task, results: [] as QcTaskResultItem[] }
|
||||
}
|
||||
try {
|
||||
const results = await fetchQcTaskResults(task.id)
|
||||
return { ...task, results: results || [] }
|
||||
} catch {
|
||||
return { ...task, results: [] as QcTaskResultItem[] }
|
||||
}
|
||||
})
|
||||
)
|
||||
viewTasks.value = enriched
|
||||
} finally {
|
||||
viewLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => loadData())
|
||||
@@ -475,6 +640,10 @@
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
}
|
||||
|
||||
.qc-work-order-task-attachments {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.view-reception-attachments {
|
||||
margin-top: 4px;
|
||||
|
||||
|
||||
Reference in new issue
Block a user