提交
This commit is contained in:
1 parent
48dcf991dc
commit
be2226e4ca
100 files changed
+61340
-40975
No files matched your search
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,11 +1,11 @@
|
||||
# Graph Report - src (2026-10-16)
|
||||
# Graph Report - src (2026-10-17)
|
||||
|
||||
## Corpus Check
|
||||
- Large corpus: 1036 files · ~687,058 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
- Large corpus: 1036 files · ~687,735 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
|
||||
## Summary
|
||||
- 3846 nodes · 5638 edges · 67 communities detected
|
||||
- Extraction: 76% EXTRACTED · 24% INFERRED · 0% AMBIGUOUS · INFERRED: 1352 edges (avg confidence: 0.8)
|
||||
- 3834 nodes · 5593 edges · 66 communities detected
|
||||
- Extraction: 76% EXTRACTED · 24% INFERRED · 0% AMBIGUOUS · INFERRED: 1336 edges (avg confidence: 0.8)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
@@ -52,35 +52,34 @@
|
||||
- [[_COMMUNITY_Community 40|Community 40]]
|
||||
- [[_COMMUNITY_Community 41|Community 41]]
|
||||
- [[_COMMUNITY_Community 42|Community 42]]
|
||||
- [[_COMMUNITY_Community 44|Community 44]]
|
||||
- [[_COMMUNITY_Community 43|Community 43]]
|
||||
- [[_COMMUNITY_Community 45|Community 45]]
|
||||
- [[_COMMUNITY_Community 46|Community 46]]
|
||||
- [[_COMMUNITY_Community 47|Community 47]]
|
||||
- [[_COMMUNITY_Community 49|Community 49]]
|
||||
- [[_COMMUNITY_Community 48|Community 48]]
|
||||
- [[_COMMUNITY_Community 51|Community 51]]
|
||||
- [[_COMMUNITY_Community 53|Community 53]]
|
||||
- [[_COMMUNITY_Community 52|Community 52]]
|
||||
- [[_COMMUNITY_Community 54|Community 54]]
|
||||
- [[_COMMUNITY_Community 55|Community 55]]
|
||||
- [[_COMMUNITY_Community 56|Community 56]]
|
||||
- [[_COMMUNITY_Community 57|Community 57]]
|
||||
- [[_COMMUNITY_Community 58|Community 58]]
|
||||
- [[_COMMUNITY_Community 59|Community 59]]
|
||||
- [[_COMMUNITY_Community 60|Community 60]]
|
||||
- [[_COMMUNITY_Community 62|Community 62]]
|
||||
- [[_COMMUNITY_Community 61|Community 61]]
|
||||
- [[_COMMUNITY_Community 64|Community 64]]
|
||||
- [[_COMMUNITY_Community 65|Community 65]]
|
||||
- [[_COMMUNITY_Community 66|Community 66]]
|
||||
- [[_COMMUNITY_Community 67|Community 67]]
|
||||
- [[_COMMUNITY_Community 68|Community 68]]
|
||||
- [[_COMMUNITY_Community 69|Community 69]]
|
||||
- [[_COMMUNITY_Community 73|Community 73]]
|
||||
- [[_COMMUNITY_Community 76|Community 76]]
|
||||
- [[_COMMUNITY_Community 77|Community 77]]
|
||||
- [[_COMMUNITY_Community 94|Community 94]]
|
||||
- [[_COMMUNITY_Community 72|Community 72]]
|
||||
- [[_COMMUNITY_Community 75|Community 75]]
|
||||
- [[_COMMUNITY_Community 78|Community 78]]
|
||||
- [[_COMMUNITY_Community 93|Community 93]]
|
||||
- [[_COMMUNITY_Community 96|Community 96]]
|
||||
- [[_COMMUNITY_Community 97|Community 97]]
|
||||
- [[_COMMUNITY_Community 98|Community 98]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `get()` - 343 edges
|
||||
2. `push()` - 129 edges
|
||||
3. `Number()` - 103 edges
|
||||
1. `get()` - 345 edges
|
||||
2. `push()` - 128 edges
|
||||
3. `Number()` - 102 edges
|
||||
4. `emit()` - 35 edges
|
||||
5. `WebSocketClient` - 26 edges
|
||||
6. `set()` - 23 edges
|
||||
@@ -97,336 +96,332 @@
|
||||
- `isStorageSlotAvailable()` --calls--> `Number()` [INFERRED]
|
||||
utils/storage-slot-options.ts → views/cell-storage/payment-info/PaymentBySampleTab.vue
|
||||
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
|
||||
views/operations/exhibition-appointment/index.vue → utils/fullscreen-loading.ts
|
||||
views/system/tenant/index.vue → utils/fullscreen-loading.ts
|
||||
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
|
||||
views/basic-info/cell-sample-type/index.vue → utils/fullscreen-loading.ts
|
||||
views/system/unit/index.vue → utils/fullscreen-loading.ts
|
||||
|
||||
## Communities
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.0
|
||||
Nodes (213): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchChannelAccount() (+205 more)
|
||||
Nodes (214): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchChannelAccount() (+206 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.01
|
||||
Nodes (168): fetchUploadAttachment(), fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), handleReportUpload(), handleReportUpload(), walk() (+160 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.02
|
||||
Nodes (131): handleFocus(), handleInput(), if(), Number(), applyPreferentialContractForV1Mode(), createEmptyTransport(), datetimeToYmd(), defaultInstallmentStartDateYmd() (+123 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.02
|
||||
Nodes (89): fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchDispatchProductionTaskSteps(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchProductionTaskEditorInit(), fetchProductionTaskList() (+81 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.02
|
||||
Nodes (85): fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionProjects(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchCreateItemUnit(), fetchDeleteItemUnit(), fetchItemUnitList() (+77 more)
|
||||
Nodes (92): fetchMyProductionTasks(), fetchMyQcTaskList(), fetchQcTaskDetail(), fetchQcTaskList(), buildTemporaryViewSections(), buildInsertedSiblingCard(), fetchMineTasksForWorkOrders(), resolveShowProtocolPaymentAtGroup() (+84 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.02
|
||||
Nodes (89): handleBlur(), handleFocus(), handleInput(), fetchConfirmPresignTransfer(), fetchPresignOrderDetail(), fetchPresignOrderList(), fetchPresignTransferReceiptConfirmedList(), fetchPresignTransferReceiptConfirmList() (+81 more)
|
||||
Nodes (81): fetchBatchDeleteOaReceiveAccounts(), fetchBatchMarkOaCcTasksRead(), fetchCreateOaReceiveAccount(), fetchDeleteOaReceiveAccount(), fetchMarkOaCcTaskRead(), fetchOaAppDraft(), fetchOaAppList(), fetchOaBankReceipts() (+73 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.02
|
||||
Nodes (89): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupDetail(), fetchPilotCheckupList(), markPilotCheckupChecked() (+81 more)
|
||||
Nodes (89): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchPilotCheckupDetail(), fetchPilotCheckupList(), fetchUpdatePostBoundRoles() (+81 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.02
|
||||
Nodes (73): fetchBatchDeleteOaReceiveAccounts(), fetchBatchMarkOaCcTasksRead(), fetchCreateOaReceiveAccount(), fetchDeleteOaReceiveAccount(), fetchMarkOaCcTaskRead(), fetchOaAppDraft(), fetchOaBankReceipts(), fetchOaCcUnreadCount() (+65 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (80): fetchUploadAttachment(), FireworkSystem, handleReportUpload(), handleReportUpload(), walk(), async(), addDispatchItem(), createDispatchItem() (+72 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.02
|
||||
Nodes (65): collectIds(), buildInboundBasicSection(), buildInboundCellStorageViewSections(), buildInboundResearchDonationViewSections(), buildReceptionSection(), buildTransportSection(), resolveInboundProtocolStatus(), buildBaseFields() (+57 more)
|
||||
Nodes (84): fetchCellSampleTypeList(), fetchCheckupInstitutionInvoices(), fetchCheckupInstitutionProjects(), fetchCreateCellSampleType(), fetchCreateCheckupInstitution(), fetchCreateCheckupInstitutionInvoice(), fetchCreateCheckupInstitutionProject(), fetchCreateExhibitionAppointment() (+76 more)
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.03
|
||||
Nodes (33): fetchGetMenuList(), ComponentLoader, IframeRouteManager, MenuProcessor, RouteRegistry, RouteTransformer, useAppMode(), useAuth() (+25 more)
|
||||
Nodes (34): fetchGetMenuList(), ComponentLoader, IframeRouteManager, MenuProcessor, RoutePermissionValidator, RouteRegistry, RouteTransformer, useAppMode() (+26 more)
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.03
|
||||
Nodes (57): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), RoutePermissionValidator, useCeremony(), useTheme(), if(), nodeSummary() (+49 more)
|
||||
Nodes (57): handleBlur(), handleFocus(), handleInput(), fetchCreatePaymentTransfer(), fetchDeletePaymentTransfer(), fetchPaymentChaseList(), fetchPaymentTransferList(), fetchPaymentTransferReceiptConfirmedList() (+49 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.04
|
||||
Nodes (64): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchCreateRegionStaff(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchDeleteRegionStaff(), fetchHospitalList() (+56 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (62): fetchOaRelatedSummaries(), useSettingsHandlers(), useSettingsPanel(), useSettingsState(), useCeremony(), useTheme(), if(), loadAllAppointmentsForExport() (+54 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.04
|
||||
Nodes (70): fetchDeptTree(), fetchUserList(), fetchOaFlowPreview(), fetchOaTemplateDetail(), openForDraft(), remoteSearchHandoverUsers(), applySchema(), flowNeedsInitiatorSelect() (+62 more)
|
||||
Nodes (71): fetchDeptTree(), fetchUserList(), fetchOaFlowPreview(), fetchOaTemplateDetail(), branchConditionLines(), remoteSearchHandoverUsers(), collectConditionReferencedFields(), collectFormConditionReferencedFields() (+63 more)
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.03
|
||||
Nodes (52): fetchDictDataOptions(), fetchAttachmentDownloadUrl(), fetchUpdateSampleFreight(), qcAttachmentFullUrl(), qcDownloadAttachment(), qcPreviewAttachment(), handleDownloadAttachment(), attachmentFullUrl() (+44 more)
|
||||
Nodes (54): fetchDictDataOptions(), fetchAttachmentDownloadUrl(), fetchUpdateSampleFreight(), formatProtocolAmount(), handleAttachmentPreview(), resolveFileUrl(), showStepContent(), visibleFields() (+46 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.03
|
||||
Nodes (30): fetchCollectionProtocolList(), fetchProductionWorkOrderQcInfo(), fetchSampleInsurance(), fetchSampleStorageCert(), fetchUpdateSampleStorageCert(), dash(), fmtMoney(), auditorDisplay() (+22 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (58): fetchOaInstanceDetail(), fetchOaTasks(), buildFilterParams(), currentPrinterName(), handleReset(), handleSearch(), load(), openDetail() (+50 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.04
|
||||
Nodes (51): fetchCreateQcInspectionItemTemplate(), fetchDeleteQcInspectionItemTemplate(), fetchQcInspectionItemTemplateDetail(), fetchQcInspectionItemTemplateList(), fetchQcInspectionItemTemplatesActive(), fetchUpdateQcInspectionItemTemplate(), fetchQcLabTestItemList(), fetchQcLabTestItemsActive() (+43 more)
|
||||
Nodes (45): buildBaseFields(), buildInboundBasicSection(), buildInboundCellStorageProductionViewSections(), buildInboundResearchDonationProductionViewSections(), buildOutboundBasicExperimentProductionViewSections(), buildOutboundClinicalProductionViewSections(), buildOutboundDetail(), buildOutboundPilotProductionViewSections() (+37 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.04
|
||||
Nodes (40): fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions(), fetchMaterialList(), fetchMaterialOptions(), fetchUpdateMaterialBatch(), fetchMaterialUsageList(), fetchMyMaterialUsageList() (+32 more)
|
||||
Nodes (51): fetchCreateItemUnit(), fetchCreateMenu(), fetchCreateTenant(), fetchDeleteItemUnit(), fetchDeleteMenu(), fetchDeleteTenant(), fetchItemUnitList(), fetchMenuTree() (+43 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.05
|
||||
Nodes (56): fetchOaRelatedSummaries(), buildFormRows(), buildOaPrintFilename(), buildOaPrintModel(), canvasToPdf(), ensurePlainObject(), exportOaApprovalPdf(), extractUrls() (+48 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (57): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchCreateRegionStaff(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchDeleteRegionStaff(), fetchHospitalList() (+49 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.05
|
||||
Nodes (34): fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed(), fetchClinicalRefundPending(), fetchOrderAdjusts(), fetchOrderDetail() (+26 more)
|
||||
Nodes (53): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), fetchUpdateProfile(), fetchUploadProfileAvatar(), fetchWxQrStatus(), fetchUserInfo() (+45 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.05
|
||||
Nodes (46): completePilotApplication(), deletePilotApplication(), fetchCreateExhibitionAppointment(), fetchDeleteExhibitionAppointment(), fetchExhibitionAppointmentList(), fetchPilotApplicationList(), fetchUpdateExhibitionAppointment(), startPilotApplication() (+38 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (40): fetchDeleteMaterialBatch(), fetchMaterialBatchList(), fetchMaterialBatchOptions(), fetchMaterialList(), fetchMaterialOptions(), fetchUpdateMaterialBatch(), fetchMaterialUsageList(), fetchMyMaterialUsageList() (+32 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.05
|
||||
Nodes (36): fetchAddTaskStep(), fetchCopyTaskStep(), fetchDeleteTaskStep(), fetchDispatchProductionTaskSteps(), fetchForwardProductionTask(), fetchProductionTaskDetail(), fetchProductionTaskEditorInit(), fetchReassignProductionTask() (+28 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (18): fetchProductionWorkOrderQcInfo(), fetchSampleInsurance(), fetchSampleStorageCert(), fetchUpdateSampleStorageCert(), dash(), fmtMoney(), auditorDisplay(), dash() (+10 more)
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.06
|
||||
Nodes (25): fetchQcMaterialBatchDetail(), fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), financeAttachmentsToUrlString() (+17 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (34): fetchClinicalPaymentDetail(), fetchClinicalPayments(), fetchClinicalReceiptConfirmed(), fetchClinicalReceiptPending(), fetchClinicalRefundConfirmed(), fetchClinicalRefundPending(), fetchOrderAdjusts(), fetchOrderDetail() (+26 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.06
|
||||
Nodes (25): fetchUpdatePostBoundRoles(), fetchMineQcWorkOrderTabUnfinishedCounts(), fetchOutboundQcDetail(), fetchQcWorkOrderDetail(), fetchQcWorkOrderList(), fetchQcWorkOrdersBySample(), fetchQcWorkOrderTabUnfinishedCounts(), reload() (+17 more)
|
||||
Nodes (40): buildInboundBasicSection(), buildInboundCellStorageViewSections(), buildInboundResearchDonationViewSections(), buildReceptionSection(), buildTransportSection(), resolveInboundProtocolStatus(), appendTaskResultSections(), batchTasksForPanelSlot() (+32 more)
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.06
|
||||
Nodes (26): fetchProductionTaskList(), createEmptySnapshots(), normalizeConfiguredReagents(), normalizeConsumables(), normalizeDataCollections(), normalizeReagents(), normalizeStepOutputs(), serializeAttachments() (+18 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (39): fetchCreateQcInspectionItemTemplate(), fetchDeleteQcInspectionItemTemplate(), fetchQcInspectionItemTemplateDetail(), fetchQcInspectionItemTemplateList(), fetchQcInspectionItemTemplatesActive(), fetchUpdateQcInspectionItemTemplate(), fetchQcLabTestItemList(), fetchQcLabTestItemsActive() (+31 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.1
|
||||
Nodes (2): WebSocketClient, TableCache
|
||||
Cohesion: 0.06
|
||||
Nodes (25): fetchQcMaterialBatchDetail(), fetchQcMaterialBatchList(), fetchQcMaterialBatchOptions(), fetchQcMaterialList(), fetchQcMaterialOptions(), fetchUpdateQcMaterialBatch(), fetchMarkQcMaterialUsageFullyUsed(), financeAttachmentsToUrlString() (+17 more)
|
||||
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.09
|
||||
Nodes (31): base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes(), sha256Hex() (+23 more)
|
||||
Cohesion: 0.1
|
||||
Nodes (2): WebSocketClient, TableCache
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.06
|
||||
Nodes (20): fetchEthnicList(), fetchSampleFreightDetail(), fetchSampleFreightList(), fetchSampleFreightSampleOptions(), applyDonationBundleToForm(), createEmptyTransport(), ensureEthnicOptionInList(), handleOpenForAdd() (+12 more)
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.08
|
||||
Nodes (30): fetchCreateTenant(), fetchDeleteTenant(), fetchTenantList(), fetchUpdateTenant(), fetchCreateEnvDetectItem(), fetchDeleteEnvDetectItem(), fetchEnvDetectItemDetail(), fetchEnvDetectItemList() (+22 more)
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.09
|
||||
Nodes (8): getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager, validateStorageData()
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.08
|
||||
Nodes (20): createEmptySnapshots(), normalizeConfiguredReagents(), normalizeConsumables(), normalizeDataCollections(), normalizeReagents(), normalizeStepOutputs(), serializeAttachments(), serializeEquipmentIds() (+12 more)
|
||||
|
||||
### Community 28 - "Community 28"
|
||||
Cohesion: 0.08
|
||||
Nodes (22): fetchMineProductionWorkOrderTabStepCounts(), fetchMineProductionWorkOrderTabStepCountsDeduped(), fetchProductionWorkOrderCellProductOptions(), fetchProductionWorkOrderDetail(), fetchProductionWorkOrderList(), fetchProductionWorkOrderQcDispatchOptions(), fetchProductionWorkOrderTabStepCounts(), fetchProductionWorkOrderTabStepCountsDeduped() (+14 more)
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.09
|
||||
Nodes (22): fetchOaAppList(), fetchOaGroupList(), fetchPublishOaApp(), loadData(), loadGroups(), resetForm(), onSuccess(), goBack() (+14 more)
|
||||
Cohesion: 0.11
|
||||
Nodes (25): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), clearTableSelection(), handleBatchComplete(), handleBatchStart() (+17 more)
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
Cohesion: 0.1
|
||||
Nodes (22): fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), fetchUpdateProfile(), fetchUploadProfileAvatar(), fetchWxQrStatus(), fetchUserInfo() (+14 more)
|
||||
Cohesion: 0.09
|
||||
Nodes (12): fetchMineQcWorkOrderTabUnfinishedCounts(), fetchOutboundQcDetail(), fetchQcWorkOrderDetail(), fetchQcWorkOrderList(), fetchQcWorkOrdersBySample(), fetchQcWorkOrderTabUnfinishedCounts(), reload(), reloadTabCounts() (+4 more)
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.13
|
||||
Nodes (19): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), postorder(), preorder(), applyTreeExpand(), handleAdd() (+11 more)
|
||||
Cohesion: 0.11
|
||||
Nodes (12): fetchCollectionProtocolList(), handleBatchDelete(), handleDelete(), handleDropdownCommand(), handleEdit(), handleReset(), handleSearch(), loadData() (+4 more)
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.1
|
||||
Nodes (12): fetchOutboundReleaseMaterial(), fetchOutboundReleaseReviewDetail(), fetchOutboundReleaseReviewList(), fetchReleaseReviewList(), handleReset(), handleSearch(), handleTabChange(), handleToggleSelect() (+4 more)
|
||||
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.13
|
||||
Nodes (19): fetchCellSampleTypeList(), fetchCreateCellSampleType(), fetchDeleteCellSampleType(), fetchUpdateCellSampleType(), handleAdd(), handleBatchDelete(), handleDelete(), handleDropdownCommand() (+11 more)
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.13
|
||||
Nodes (8): chipTitle(), confirmPicker(), memberOptionLabel(), removeUser(), resolveUser(), signBadge(), signBadgeText(), updateSelect()
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.2
|
||||
Nodes (14): applyFlagToRule(), applySchema(), countRules(), getActiveRule(), getDesigner(), getSetupState(), onMessage(), readFlag() (+6 more)
|
||||
|
||||
### Community 37 - "Community 37"
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.16
|
||||
Nodes (14): fetchCreateEnvDetectItem(), fetchDeleteEnvDetectItem(), fetchEnvDetectItemDetail(), fetchEnvDetectItemList(), fetchEnvDetectItemsActive(), fetchUpdateEnvDetectItem(), handleAdd(), handleBatchDelete() (+6 more)
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 0.12
|
||||
Nodes (6): setupOaConfirmationKit(), ensureOaKitMenu(), setupOaLeaveKit(), setupOaOvertimeKit(), setupOaResignationKit(), setupOaTripKit()
|
||||
|
||||
### Community 38 - "Community 38"
|
||||
### Community 37 - "Community 37"
|
||||
Cohesion: 0.16
|
||||
Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
### Community 38 - "Community 38"
|
||||
Cohesion: 0.27
|
||||
Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
Cohesion: 0.13
|
||||
Nodes (4): fetchPresignOrderDetail(), fetchPresignOrderList(), fetchPresignTransferReceiptConfirmedList(), fetchPresignTransferReceiptConfirmList()
|
||||
|
||||
### Community 40 - "Community 40"
|
||||
Cohesion: 0.25
|
||||
Nodes (12): buildFlowItems(), isCcProgressItem(), peopleDisplayNames(), progressNodeStatusText(), progressPersonLinesFromItem(), statusText(), displayName(), formatProgressPersonLines() (+4 more)
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
Cohesion: 0.33
|
||||
Nodes (13): buildProtocolPaymentFlowSteps(), dash(), financeReceiptTime(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit() (+5 more)
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
### Community 42 - "Community 42"
|
||||
Cohesion: 0.19
|
||||
Nodes (6): fetchServiceOrderTxnList(), handleReset(), handleSearch(), handleTabChange(), loadData(), switchTab()
|
||||
|
||||
### Community 42 - "Community 42"
|
||||
### Community 43 - "Community 43"
|
||||
Cohesion: 0.24
|
||||
Nodes (10): buildPrintDocumentHtml(), cleanupPrintIframe(), embedSopImagesAsDataUrls(), getSopA4ContentHeightPx(), getSopA4EditorHeightPx(), normalizeSopCheckboxes(), prepareSopHtmlForPrint(), printSopHtml() (+2 more)
|
||||
|
||||
### Community 44 - "Community 44"
|
||||
### Community 45 - "Community 45"
|
||||
Cohesion: 0.26
|
||||
Nodes (8): fetchSampleStorageCertQc(), saveSampleStorageCertQc(), applyDetail(), handleSave(), loadDetail(), resetFormKeys(), resolveStorageCertQcTypeKey(), supportsStorageCertQc()
|
||||
|
||||
### Community 45 - "Community 45"
|
||||
Cohesion: 0.42
|
||||
Nodes (9): buildPaymentFlowSteps(), dash(), formatAmount(), hasFinanceReceipt(), hasFinanceRefund(), hasReceiptSubmit(), hasRefundSubmit(), receiptSubmitTime() (+1 more)
|
||||
|
||||
### Community 46 - "Community 46"
|
||||
Cohesion: 0.25
|
||||
Nodes (2): handleTouchEnd(), setTransition()
|
||||
|
||||
### Community 47 - "Community 47"
|
||||
### Community 48 - "Community 48"
|
||||
Cohesion: 0.28
|
||||
Nodes (1): FireworkSystem
|
||||
|
||||
### Community 49 - "Community 49"
|
||||
Cohesion: 0.31
|
||||
Nodes (4): annotateForkEdges(), chainApiToElements(), walkAncestorBranch(), walkAncestorParents()
|
||||
Nodes (5): fetchMyProductionMaterialConsumptions(), displayConsumptionQty(), handleReset(), handleSearch(), loadData()
|
||||
|
||||
### Community 51 - "Community 51"
|
||||
Cohesion: 0.25
|
||||
Nodes (3): companyProgressTagType(), companyProgressText(), companyGroupProgress()
|
||||
|
||||
### Community 53 - "Community 53"
|
||||
Cohesion: 0.39
|
||||
Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
|
||||
|
||||
### Community 54 - "Community 54"
|
||||
### Community 52 - "Community 52"
|
||||
Cohesion: 0.36
|
||||
Nodes (1): TableHeightCalculator
|
||||
|
||||
### Community 56 - "Community 56"
|
||||
### Community 54 - "Community 54"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): logApiCryptoBootInfo(), maskSecret()
|
||||
|
||||
### Community 57 - "Community 57"
|
||||
### Community 55 - "Community 55"
|
||||
Cohesion: 0.29
|
||||
Nodes (1): fetchQcMonthTemplateList()
|
||||
|
||||
### Community 58 - "Community 58"
|
||||
### Community 56 - "Community 56"
|
||||
Cohesion: 0.29
|
||||
Nodes (2): fetchStorageReleaseReviewDetail(), fetchStorageReleaseReviewList()
|
||||
|
||||
### Community 59 - "Community 59"
|
||||
### Community 57 - "Community 57"
|
||||
Cohesion: 0.4
|
||||
Nodes (2): isUnfinishedQcWorkOrder(), resolveQcWorkOrderUnfinishedBadgeCount()
|
||||
|
||||
### Community 60 - "Community 60"
|
||||
### Community 58 - "Community 58"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): parseEquipmentIds(), syncEquipmentNamesFromOptions()
|
||||
|
||||
### Community 62 - "Community 62"
|
||||
### Community 60 - "Community 60"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): focusInput(), handleKeydown()
|
||||
|
||||
### Community 65 - "Community 65"
|
||||
### Community 61 - "Community 61"
|
||||
Cohesion: 0.4
|
||||
Nodes (1): fetchQcMaterialAmountAuthUsers()
|
||||
|
||||
### Community 64 - "Community 64"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): isCurrentPending(), isNotReached()
|
||||
|
||||
### Community 66 - "Community 66"
|
||||
### Community 65 - "Community 65"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): if(), logSidebarMenu()
|
||||
|
||||
### Community 68 - "Community 68"
|
||||
### Community 67 - "Community 67"
|
||||
Cohesion: 0.5
|
||||
Nodes (1): fetchStorageEntrySmsList()
|
||||
|
||||
### Community 69 - "Community 69"
|
||||
### Community 68 - "Community 68"
|
||||
Cohesion: 0.5
|
||||
Nodes (1): fetchQcSmsList()
|
||||
|
||||
### Community 73 - "Community 73"
|
||||
### Community 72 - "Community 72"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): getSettingDefaults(), resetToDefaults()
|
||||
|
||||
### Community 76 - "Community 76"
|
||||
### Community 75 - "Community 75"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): copyTextToClipboard(), copyTextWithFallback()
|
||||
|
||||
### Community 77 - "Community 77"
|
||||
### Community 78 - "Community 78"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): getSourceProtocolAttachment(), normalizeAttachmentFiles()
|
||||
|
||||
### Community 94 - "Community 94"
|
||||
### Community 93 - "Community 93"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): emptyTabCounts(), useProductionWorkOrderTabStepCounts()
|
||||
|
||||
### Community 97 - "Community 97"
|
||||
### Community 96 - "Community 96"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): initialProgress(), useQcWorkOrderAttachmentExport()
|
||||
|
||||
### Community 98 - "Community 98"
|
||||
### Community 97 - "Community 97"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): emptyTabCounts(), useQcWorkOrderTabUnfinishedCounts()
|
||||
|
||||
## Knowledge Gaps
|
||||
- **Thin community `Community 23`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
|
||||
- **Thin community `Community 24`** (42 nodes): `WebSocketClient`, `.calculateReconnectDelay()`, `.clearAllTimers()`, `.clearConnectionTimers()`, `.clearTimer()`, `.close()`, `.closeCurrentSocketForReconnect()`, `.connect()`, `.connectionStatusText()`, `.constructor()`, `.destroyInstance()`, `.flushMessageQueue()`, `.getInstance()`, `.getReadyStateText()`, `.handleClose()`, `.handleConnectionTimeout()`, `.handleError()`, `.handleMessage()`, `.handleOpen()`, `.init()`, `.isWebSocketConnected()`, `.reconnect()`, `.resetHeartbeat()`, `.send()`, `.startHeartbeat()`, `.startPing()`, `TableCache`, `.cleanupExpired()`, `.clear()`, `.clearByTags()`, `.clearCurrentSearch()`, `.clearPagination()`, `.constructor()`, `.evictLRU()`, `.generateKey()`, `.generateTags()`, `.get()`, `.getStats()`, `.log()`, `.set()`, `index.ts`, `tableCache.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 46`** (9 nodes): `cleanupEventListeners()`, `handleTouchEnd()`, `handleTouchMove()`, `handleTouchStart()`, `handleWheelScroll()`, `if()`, `setTransition()`, `setupEventListeners()`, `index.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 47`** (9 nodes): `FireworkSystem`, `.constructor()`, `.createFirework()`, `.createParticle()`, `.getAvailableParticle()`, `.initializePool()`, `.preloadAllImages()`, `.preloadImage()`, `index.vue`
|
||||
- **Thin community `Community 52`** (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 54`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
|
||||
- **Thin community `Community 54`** (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 56`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
|
||||
- **Thin community `Community 55`** (7 nodes): `fetchConfirmQcMonthImport()`, `fetchCreateQcMonthTemplate()`, `fetchDeleteQcMonthTemplate()`, `fetchPreviewQcMonthImport()`, `fetchQcMonthTemplateList()`, `fetchUpdateQcMonthTemplate()`, `qc-material-month-template.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 57`** (7 nodes): `fetchConfirmQcMonthImport()`, `fetchCreateQcMonthTemplate()`, `fetchDeleteQcMonthTemplate()`, `fetchPreviewQcMonthImport()`, `fetchQcMonthTemplateList()`, `fetchUpdateQcMonthTemplate()`, `qc-material-month-template.ts`
|
||||
- **Thin community `Community 56`** (7 nodes): `batchReviewStorageRelease()`, `batchStoreSample()`, `fetchStorageReleaseReviewDetail()`, `fetchStorageReleaseReviewList()`, `storeSample()`, `submitStorageReleaseReview()`, `storage-release-review.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 58`** (7 nodes): `batchReviewStorageRelease()`, `batchStoreSample()`, `fetchStorageReleaseReviewDetail()`, `fetchStorageReleaseReviewList()`, `storeSample()`, `submitStorageReleaseReview()`, `storage-release-review.ts`
|
||||
- **Thin community `Community 57`** (6 nodes): `formatQcWorkOrderUnfinishedBadgeCount()`, `isUnfinishedQcWorkOrder()`, `qcWorkOrderUnfinishedBadgeTitle()`, `resolveQcWorkOrderUnfinishedBadgeCount()`, `sumQcWorkOrderGroupUnfinishedBadgeCount()`, `qc-work-order-unfinished-badge.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 59`** (6 nodes): `formatQcWorkOrderUnfinishedBadgeCount()`, `isUnfinishedQcWorkOrder()`, `qcWorkOrderUnfinishedBadgeTitle()`, `resolveQcWorkOrderUnfinishedBadgeCount()`, `sumQcWorkOrderGroupUnfinishedBadgeCount()`, `qc-work-order-unfinished-badge.ts`
|
||||
- **Thin community `Community 58`** (5 nodes): `equipmentOptionLabel()`, `joinEquipmentIds()`, `parseEquipmentIds()`, `syncEquipmentNamesFromOptions()`, `equipment-select.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 60`** (5 nodes): `equipmentOptionLabel()`, `joinEquipmentIds()`, `parseEquipmentIds()`, `syncEquipmentNamesFromOptions()`, `equipment-select.ts`
|
||||
- **Thin community `Community 60`** (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 62`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
|
||||
- **Thin community `Community 61`** (5 nodes): `fetchBatchRevokeQcMaterialAmountAuth()`, `fetchGrantQcMaterialAmountAuth()`, `fetchQcMaterialAmountAuthUsers()`, `fetchRevokeQcMaterialAmountAuth()`, `qc-material-amount-auth.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 65`** (4 nodes): `isCurrentPending()`, `isNotReached()`, `mergeProgressWithComments()`, `oa-progress-merge-comments.ts`
|
||||
- **Thin community `Community 64`** (4 nodes): `isCurrentPending()`, `isNotReached()`, `mergeProgressWithComments()`, `oa-progress-merge-comments.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 66`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
|
||||
- **Thin community `Community 65`** (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 68`** (4 nodes): `fetchGenerateStorageEntrySms()`, `fetchStorageEntrySmsList()`, `fetchToggleStorageEntrySmsNotified()`, `storage-entry-sms-notification.ts`
|
||||
- **Thin community `Community 67`** (4 nodes): `fetchGenerateStorageEntrySms()`, `fetchStorageEntrySmsList()`, `fetchToggleStorageEntrySmsNotified()`, `storage-entry-sms-notification.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 69`** (4 nodes): `fetchGenerateQcSms()`, `fetchQcSmsList()`, `fetchToggleQcSmsNotified()`, `qc-sms-notification.ts`
|
||||
- **Thin community `Community 68`** (4 nodes): `fetchGenerateQcSms()`, `fetchQcSmsList()`, `fetchToggleQcSmsNotified()`, `qc-sms-notification.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 73`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
- **Thin community `Community 72`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 76`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
|
||||
- **Thin community `Community 75`** (3 nodes): `copyTextToClipboard()`, `copyTextWithFallback()`, `copy-text.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 77`** (3 nodes): `getSourceProtocolAttachment()`, `normalizeAttachmentFiles()`, `protocol-source-attachment.ts`
|
||||
- **Thin community `Community 78`** (3 nodes): `getSourceProtocolAttachment()`, `normalizeAttachmentFiles()`, `protocol-source-attachment.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 94`** (3 nodes): `emptyTabCounts()`, `useProductionWorkOrderTabStepCounts()`, `useProductionWorkOrderTabStepCounts.ts`
|
||||
- **Thin community `Community 93`** (3 nodes): `emptyTabCounts()`, `useProductionWorkOrderTabStepCounts()`, `useProductionWorkOrderTabStepCounts.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 97`** (3 nodes): `initialProgress()`, `useQcWorkOrderAttachmentExport()`, `useQcWorkOrderAttachmentExport.ts`
|
||||
- **Thin community `Community 96`** (3 nodes): `initialProgress()`, `useQcWorkOrderAttachmentExport()`, `useQcWorkOrderAttachmentExport.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 98`** (3 nodes): `emptyTabCounts()`, `useQcWorkOrderTabUnfinishedCounts()`, `useQcWorkOrderTabUnfinishedCounts.ts`
|
||||
- **Thin community `Community 97`** (3 nodes): `emptyTabCounts()`, `useQcWorkOrderTabUnfinishedCounts()`, `useQcWorkOrderTabUnfinishedCounts.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 `push()` connect `Community 1` to `Community 0`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 9`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 18`, `Community 19`, `Community 21`, `Community 22`, `Community 23`, `Community 24`, `Community 28`, `Community 29`, `Community 35`, `Community 47`?**
|
||||
_High betweenness centrality (0.278) - this node is a cross-community bridge._
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 21`, `Community 22`, `Community 25`, `Community 26`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 33`, `Community 41`, `Community 44`, `Community 49`, `Community 57`, `Community 58`, `Community 68`, `Community 69`?**
|
||||
- **Why does `push()` connect `Community 6` to `Community 0`, `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 7`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 17`, `Community 18`, `Community 19`, `Community 21`, `Community 22`, `Community 24`, `Community 27`, `Community 28`, `Community 30`, `Community 40`?**
|
||||
_High betweenness centrality (0.292) - this node is a cross-community bridge._
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 20`, `Community 21`, `Community 22`, `Community 23`, `Community 25`, `Community 28`, `Community 29`, `Community 30`, `Community 31`, `Community 32`, `Community 35`, `Community 39`, `Community 42`, `Community 45`, `Community 48`, `Community 55`, `Community 56`, `Community 61`, `Community 67`, `Community 68`?**
|
||||
_High betweenness centrality (0.272) - this node is a cross-community bridge._
|
||||
- **Why does `Number()` connect `Community 2` to `Community 0`, `Community 1`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 9`, `Community 11`, `Community 12`, `Community 13`, `Community 15`, `Community 16`, `Community 20`, `Community 22`, `Community 24`, `Community 25`, `Community 39`, `Community 40`, `Community 45`?**
|
||||
_High betweenness centrality (0.153) - this node is a cross-community bridge._
|
||||
- **Are the 342 inferred relationships involving `get()` (e.g. with `resolveStorageSlotRangeFill()` and `buildNodeCodePathFromFlat()`) actually correct?**
|
||||
_`get()` has 342 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 128 inferred relationships involving `push()` (e.g. with `refreshQcMenuBadges()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
|
||||
_`push()` has 128 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 98 inferred relationships involving `Number()` (e.g. with `slotOrderNum()` and `isSlotAvailable()`) actually correct?**
|
||||
_`Number()` has 98 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Why does `Number()` connect `Community 1` to `Community 0`, `Community 3`, `Community 4`, `Community 6`, `Community 7`, `Community 9`, `Community 10`, `Community 11`, `Community 12`, `Community 13`, `Community 14`, `Community 17`, `Community 18`, `Community 19`, `Community 21`, `Community 23`, `Community 25`, `Community 27`, `Community 38`, `Community 41`, `Community 48`?**
|
||||
_High betweenness centrality (0.151) - this node is a cross-community bridge._
|
||||
- **Are the 344 inferred relationships involving `get()` (e.g. with `resolveStorageSlotRangeFill()` and `buildNodeCodePathFromFlat()`) actually correct?**
|
||||
_`get()` has 344 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 127 inferred relationships involving `push()` (e.g. with `refreshQcMenuBadges()` and `collectLeafFacilityNodesInTreeOrder()`) actually correct?**
|
||||
_`push()` has 127 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 97 inferred relationships involving `Number()` (e.g. with `slotOrderNum()` and `isSlotAvailable()`) actually correct?**
|
||||
_`Number()` has 97 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 34 inferred relationships involving `emit()` (e.g. with `updateSamples()` and `notifyChanged()`) actually correct?**
|
||||
_`emit()` has 34 INFERRED edges - model-reasoned connections that need verification._
|
||||
@@ -57,12 +57,12 @@
|
||||
</div>
|
||||
<div id="legend"></div>
|
||||
</div>
|
||||
<div id="stats">3846 nodes · 5638 edges · 404 communities</div>
|
||||
<div id="stats">3834 nodes · 5593 edges · 404 communities</div>
|
||||
</div>
|
||||
<script>
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 56, "community_name": "Community 56", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 56, "community_name": "Community 56", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 56, "community_name": "Community 56", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 56, "community_name": "Community 56", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 56, "community_name": "Community 56", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 56, "community_name": "Community 56", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 145, "community_name": "Community 145", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#B07AA1", "border": "#B07AA1", "highlight": {"background": "#ffffff", "border": "#B07AA1"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 146, "community_name": "Community 146", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 34, "community_name": "Community 34", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 34, "community_name": "Community 34", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 34, "community_name": "Community 34", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 34, "community_name": "Community 34", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 34, "community_name": "Community 34", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective",Line 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": 571}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 251}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 186}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 136}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 135}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 131}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 129}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 126}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 94}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 89}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 87}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 84}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 83}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 80}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 73}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 68}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 67}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 61}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 57}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 56}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 46}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 43}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 43}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 42}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 39}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 38}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 38}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 37}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 35}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 33}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 31}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 24}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 24}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 23}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 22}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 20}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 19}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 17}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 17}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 16}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 14}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 14}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 13}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 13}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 12}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 10}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 9}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 9}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 9}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 9}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 9}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 9}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 9}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 8}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 8}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 8}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 7}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 7}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 7}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 6}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 5}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 5}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 5}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 4}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 4}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 4}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 4}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 4}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 4}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 4}, {"cid": 70, "color": "#4E79A7", "label": "CommuniLine truncated
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 54, "community_name": "Community 54", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 54, "community_name": "Community 54", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 54, "community_name": "Community 54", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 54, "community_name": "Community 54", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 54, "community_name": "Community 54", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 54, "community_name": "Community 54", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#59A14F", "border": "#59A14F", "highlight": {"background": "#ffffff", "border": "#59A14F"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 144, "community_name": "Community 144", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 145, "community_name": "Community 145", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 33, "community_name": "Community 33", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 33, "community_name": "Community 33", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 33, "community_name": "Community 33", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 33, "community_name": "Community 33", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#76B7B2", "border": "#76B7B2", "highlight": {"background": "#ffffff", "border": "#76B7B2"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 33, "community_name": "Community 33", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective",Line 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": 568}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 186}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 155}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 143}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 132}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 129}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 126}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 123}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 102}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 88}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 87}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 86}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 86}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 85}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 78}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 77}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 73}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 70}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 68}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 68}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 61}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 58}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 57}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 46}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 42}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 38}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 37}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 35}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 35}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 30}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 25}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 24}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 24}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 22}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 19}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 19}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 17}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 17}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 16}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 15}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 14}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 14}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 14}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 13}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 13}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 12}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 9}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 9}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 9}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 9}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 9}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 8}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 8}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 8}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 7}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 7}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 7}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 6}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 5}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 5}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 5}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 5}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 4}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 4}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 4}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 4}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 4}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 4}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 4}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 4}, {"cid": 70, "color": "#4E79A7", "label": "CommunLine 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": 1781272556.3597305,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1781272556.3601494,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1781272556.3606477,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1792141056.4307427,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1792139044.4306676,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1792207835.5380793,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1792206847.1791198,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1787887312.735774,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1787887320.8486114,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/composables/useAttachmentActions.ts": 1788162977.6691713,
|
||||
@@ -87,7 +87,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sample-freight-flatten.ts": 1791468382.3859549,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-labels.ts": 1782893910.9937537,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/oa-approval-print-flow.ts": 1786601489.1530275,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1792028747.9199126,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1792208113.061491,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/production-work-order-card-meta.ts": 1788234947.3617282,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fetch-paged-list.ts": 1781272556.3903377,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-audit-flatten.ts": 1791868355.944149,
|
||||
@@ -319,14 +319,14 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/media/art-video-player/index.vue": 1782893851.5118508,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/media/art-cutter-img/index.vue": 1782893851.4950457,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/TinyMceEditor/index.vue": 1787643214.0412707,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-transport-form-section/index.vue": 1791785224.3434331,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-transport-form-section/index.vue": 1792145332.1622937,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-attachment-mini-readonly/index.vue": 1785145511.689019,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/cell-product-lineage-qc-dialog/index.vue": 1785290453.1924367,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-freight-pick-table/index.vue": 1791540667.8527935,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-finance-confirm-fields/index.vue": 1785145511.6893718,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-finance-confirm-fields/finance-attachments.ts": 1781272556.6256514,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue": 1787107329.207847,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/clinical-payment-confirm-timeline/index.vue": 1791162469.9343283,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/clinical-payment-confirm-timeline/index.vue": 1792143951.1833928,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/outbound-material-readonly-panel/index.vue": 1782893852.2706735,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-reception-dual-readonly-panel/index.vue": 1791471812.3709838,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/dialog-form-body/index.vue": 1782893852.1647537,
|
||||
@@ -335,7 +335,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/special-sample-display/index.vue": 1783925058.170451,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/order-operate-local-dialog/index.vue": 1782893852.4089837,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/product-view-dialog/index.vue": 1790217249.4347427,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-payment-detail-dialog/index.vue": 1791623760.681438,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-payment-detail-dialog/index.vue": 1792202737.3385131,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-production-info-cards/index.vue": 1791471821.0584927,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-handle-panel/index.vue": 1791860633.8460462,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-storage-release-info/index.vue": 1791471822.4833055,
|
||||
@@ -431,7 +431,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material-usage.ts": 1782894693.658203,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/pre-transit-sample.ts": 1782204427.8344586,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-third-party-company.ts": 1781415389.1483624,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/collection-protocol.ts": 1792075243.0424178,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/collection-protocol.ts": 1792199724.7526388,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/chinaRegion.ts": 1781272556.6606348,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-entry-sms-notification.ts": 1781272556.660898,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/business.ts": 1781750847.0646322,
|
||||
@@ -447,7 +447,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production-waste-output.ts": 1787023802.604376,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/oaApproval.ts": 1792141011.8097997,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/material-stock-check.ts": 1781427280.4992144,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/research-donation.ts": 1782893816.502791,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/research-donation.ts": 1792199726.4842353,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task.ts": 1783327864.9737554,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-item-param.ts": 1782446790.5508618,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/env-detect-item.ts": 1781272556.668322,
|
||||
@@ -464,19 +464,19 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/protocol-sample-payment.ts": 1792075236.7229176,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-work-order.ts": 1791862690.2468123,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/auth/login/index.vue": 1789715240.9777095,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/presign-order/index.vue": 1792078851.1747634,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/presign-order/PresignOrderFormDialog.vue": 1792079639.101142,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/presign-order/index.vue": 1792202745.7217324,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/presign-order/PresignOrderFormDialog.vue": 1792143951.183733,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/refund-confirm/index.vue": 1782893854.3402505,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-freight/index.vue": 1792029084.1036322,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-freight/FreightConfigDialog.vue": 1792029084.6662757,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-freight/FreightEditDialog.vue": 1791424830.3864272,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/my-material-usage/index.vue": 1791296731.262585,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/index.vue": 1792078851.1662643,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/index.vue": 1792202737.8532119,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolStatusChangeDialog.vue": 1782894806.0188534,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolRefundRecordDialog.vue": 1792078851.1693885,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue": 1792078851.1700473,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolRefundRecordDialog.vue": 1792202738.902946,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue": 1792202724.60167,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue": 1791162227.1000674,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue": 1792079326.0241637,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue": 1792143951.1766114,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/SamplePaymentPanel.vue": 1792078851.1718132,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue": 1782893854.3675368,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-info/index.vue": 1791187848.6336563,
|
||||
@@ -484,23 +484,23 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-info/UnifiedSampleInfoPage.vue": 1791868912.5231571,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/sample-reception-info/index.vue": 1791200162.920515,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-info/index.vue": 1791504621.1780708,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-info/PaymentBySampleTab.vue": 1792079135.457411,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-info/PaymentExcelList.vue": 1792078851.1755798,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-info/PaymentBySampleTab.vue": 1792202727.257655,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-info/PaymentExcelList.vue": 1792202728.178532,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/storage-cert/index.vue": 1791199760.6008983,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-chase/index.vue": 1792078851.1759324,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-chase/index.vue": 1792202729.212173,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/index.vue": 1791346519.2317798,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/DonationProtocolTab.vue": 1791868429.5362544,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/UnifiedProtocolPage.vue": 1791429008.46071,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/ProtocolAuditTab.vue": 1791769656.733718,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/PaymentInfoTab.vue": 1792079135.4569967,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/ProtocolAuditTab.vue": 1792202738.3284647,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/PaymentInfoTab.vue": 1792202725.2582252,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/material/index.vue": 1791328376.3211129,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/material/MaterialUsageTab.vue": 1783429524.4350624,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/material/MaterialStockCheckTab.vue": 1791328376.3238451,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/fullmoon-todo/index.vue": 1791199762.446884,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/storage-entry-info/index.vue": 1791200163.9343994,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-confirm/index.vue": 1792078851.1768854,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-confirm/RefundConfirmTab.vue": 1792079135.4566004,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-confirm/InvoiceRequestTab.vue": 1792079135.4561322,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-confirm/index.vue": 1792208118.0859683,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-confirm/RefundConfirmTab.vue": 1792208127.1538856,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-confirm/InvoiceRequestTab.vue": 1792208128.4761107,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/material-stock-log/index.vue": 1782893853.8275082,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/outside/Iframe.vue": 1782893853.8888595,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/demo/index.vue": 1791296731.28513,
|
||||
@@ -509,10 +509,10 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/operations/exhibition-appointment/index.vue": 1791328331.1522717,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/audit/index.vue": 1791346520.1101646,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/sample-info/index.vue": 1791187849.9149377,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/index.vue": 1791437070.4771616,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/index.vue": 1792143951.182839,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationAuditDialog.vue": 1782893853.885547,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationViewDialog.vue": 1791775350.7223852,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue": 1791775341.5097153,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationViewDialog.vue": 1792199835.0448852,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue": 1792143951.1814156,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationStatusChangeDialog.vue": 1782894808.1240149,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/dashboard/console/index.vue": 1791855531.6714394,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/system/tenant/index.vue": 1791328376.332648,
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { test } from 'node:test'
|
||||
import ts from 'typescript'
|
||||
|
||||
const base = new URL('../src/views/cell-storage/', import.meta.url)
|
||||
const presign = 'presign-order/PresignOrderFormDialog.vue'
|
||||
const panel = 'protocol/SamplePaymentPanel.vue'
|
||||
const protocol = 'protocol/ProtocolFormDialog.vue'
|
||||
function source(file: string) {
|
||||
const text = readFileSync(new URL(file, base), 'utf8').match(
|
||||
/<script setup lang="ts">([\s\S]*?)<\/script>/
|
||||
)![1]
|
||||
return ts.createSourceFile(file + '.ts', text, ts.ScriptTarget.Latest, true)
|
||||
}
|
||||
function functions(file: string, names: string[], env: Record<string, any>) {
|
||||
env = {
|
||||
isEdit: { value: false },
|
||||
editDetailLoaded: { value: true },
|
||||
loading: { value: false },
|
||||
formReady: { value: true },
|
||||
...env
|
||||
}
|
||||
const ast = source(file)
|
||||
const text = ast.statements
|
||||
.filter((n) => ts.isFunctionDeclaration(n) && names.includes(n.name!.text))
|
||||
.map((n) => n.getText(ast))
|
||||
.join('\n')
|
||||
const js = ts.transpile(text, { target: ts.ScriptTarget.ES2022 })
|
||||
return new Function(
|
||||
...Object.keys(env),
|
||||
`${js}; return { ${names.filter((n) => text.includes('function ' + n + '(')).join(',')} }`
|
||||
)(...Object.values(env))
|
||||
}
|
||||
function expression(file: string, target: string, env: Record<string, any>) {
|
||||
const ast = source(file)
|
||||
let result = ''
|
||||
function visit(node: ts.Node) {
|
||||
if (ts.isBinaryExpression(node) && node.left.getText(ast) === target)
|
||||
result = node.right.getText(ast)
|
||||
if (ts.isVariableDeclaration(node) && node.name.getText(ast) === target && node.initializer)
|
||||
result = node.initializer.getText(ast)
|
||||
node.forEachChild(visit)
|
||||
}
|
||||
visit(ast)
|
||||
assert.ok(result, `missing ${target}`)
|
||||
const js = ts.transpile(`const result = ${result}`, { target: ts.ScriptTarget.ES2022 })
|
||||
return new Function(...Object.keys(env), `${js}; return result`)(...Object.values(env))
|
||||
}
|
||||
function presignEnv(transfers: any[] = []) {
|
||||
const writes: any[] = []
|
||||
const warnings: string[] = []
|
||||
const env: Record<string, any> = {
|
||||
form: {
|
||||
motherName: '测试',
|
||||
sampleTypeIds: ['sample'],
|
||||
transfers,
|
||||
orderDate: '',
|
||||
expectedDeliveryDate: '',
|
||||
regionId: '',
|
||||
hospitalId: '',
|
||||
sampleCollectTime: '',
|
||||
collectorUserId: '',
|
||||
regionManagerUserId: '',
|
||||
payMode: 'dj',
|
||||
contractAmount: null,
|
||||
orderStatus: 'active',
|
||||
remark: ''
|
||||
},
|
||||
props: { orderId: 'order-1' },
|
||||
saving: { value: false },
|
||||
ElMessage: { warning: (v: string) => warnings.push(v), success: () => {} },
|
||||
fetchUpdatePresignOrder: async (_: string, body: any) =>
|
||||
writes.push(JSON.parse(JSON.stringify(body))),
|
||||
fetchUpdatePresignTransfer: async (_: string, body: any) =>
|
||||
writes.push(JSON.parse(JSON.stringify(body))),
|
||||
fetchCreatePresignTransfer: async (_: string, body: any) => {
|
||||
writes.push(body)
|
||||
return { id: 'new' }
|
||||
},
|
||||
fetchSubmitPresignTransferFinance: async () => {},
|
||||
submitDfjTransfersOnSave: async () => [],
|
||||
emit: () => {}
|
||||
}
|
||||
return { env, writes, warnings }
|
||||
}
|
||||
|
||||
test('预签约清空可选字段后,更新请求仍携带空值', async () => {
|
||||
const { env, writes } = presignEnv()
|
||||
await functions(presign, ['handleSave', 'attachmentsToUrls'], env).handleSave()
|
||||
for (const key of [
|
||||
'expectedDeliveryDate',
|
||||
'regionId',
|
||||
'hospitalId',
|
||||
'sampleCollectTime',
|
||||
'collectorUserId',
|
||||
'regionManagerUserId',
|
||||
'remark'
|
||||
])
|
||||
assert.equal(writes[0][key], '', key)
|
||||
assert.equal(writes[0].contractAmount, null)
|
||||
})
|
||||
test('预签约保存转账清空凭证/日期/备注后请求携带空值', async () => {
|
||||
const { env, writes } = presignEnv([
|
||||
{ id: 't', payStatus: 'dfj', attachments: [], transferTime: '', remark: '' }
|
||||
])
|
||||
await functions(presign, ['handleSave', 'attachmentsToUrls'], env).handleSave()
|
||||
assert.equal(writes[1].transferAttachments, '')
|
||||
assert.equal(writes[1].transferTime, '')
|
||||
assert.equal(writes[1].remark, '')
|
||||
})
|
||||
test('预签约提交财务前同步清空凭证', async () => {
|
||||
const { env, writes } = presignEnv()
|
||||
await functions(presign, ['submitTransfer', 'attachmentsToUrls'], env).submitTransfer({
|
||||
id: 't',
|
||||
payStatus: 'dfj',
|
||||
attachments: []
|
||||
})
|
||||
assert.equal(writes[0].transferAttachments, '')
|
||||
})
|
||||
test('预签约凭证上传时禁止保存订单', async () => {
|
||||
const { env, writes, warnings } = presignEnv([
|
||||
{ payStatus: 'dfj', attachments: [{ _uploading: true }] }
|
||||
])
|
||||
await functions(presign, ['handleSave', 'attachmentsToUrls'], env).handleSave()
|
||||
assert.equal(writes.length, 0)
|
||||
assert.equal(warnings.length, 1)
|
||||
})
|
||||
test('预签约凭证上传时禁止提交财务', async () => {
|
||||
const { env, writes, warnings } = presignEnv()
|
||||
await functions(presign, ['submitTransfer', 'attachmentsToUrls'], env).submitTransfer({
|
||||
id: 't',
|
||||
payStatus: 'dfj',
|
||||
attachments: [{ _uploading: true }]
|
||||
})
|
||||
assert.equal(writes.length, 0)
|
||||
assert.equal(warnings.length, 1)
|
||||
})
|
||||
for (const attachments of [[], [{ fileUrl: 'new.pdf' }], undefined]) {
|
||||
test(`协议整体保存使用当前附件数组(${JSON.stringify(attachments)})`, () => {
|
||||
const payments = expression(protocol, 'payload.samplePayments', {
|
||||
formData: {
|
||||
samplePayments: [
|
||||
{ schedules: [{ transfers: [{ transferAttachments: 'old.pdf', attachments }] }] }
|
||||
]
|
||||
},
|
||||
toIntOrUndefined: () => undefined,
|
||||
toFloatOrUndefined: () => undefined,
|
||||
parseInstallmentRatios: () => []
|
||||
})
|
||||
assert.equal(
|
||||
payments[0].schedules[0].transfers[0].transferAttachments,
|
||||
attachments ? attachments.map((f) => f.fileUrl).join(',') : 'old.pdf'
|
||||
)
|
||||
})
|
||||
}
|
||||
test('样本缴费转账行保存删除全部凭证', async () => {
|
||||
let written: any
|
||||
const tr = {
|
||||
id: 't',
|
||||
transferType: 'bank',
|
||||
transferAmount: 1,
|
||||
attachments: [],
|
||||
transferAttachments: 'old.pdf'
|
||||
}
|
||||
const env = {
|
||||
props: { protocolId: 'p', modelValue: [{ schedules: [{ id: 's', transfers: [tr] }] }] },
|
||||
savingKey: { value: '' },
|
||||
isTransferLocked: () => false,
|
||||
emitChange: () => {},
|
||||
ElMessage: { warning: () => {}, success: () => {} },
|
||||
fetchUpdatePaymentTransfer: async (_: string, body: any) => {
|
||||
written = JSON.parse(JSON.stringify(body))
|
||||
return { transferAttachments: '', attachments: [] }
|
||||
}
|
||||
}
|
||||
await functions(
|
||||
panel,
|
||||
[
|
||||
'saveTransferRow',
|
||||
'getTransfer',
|
||||
'validateTransferRow',
|
||||
'transferRowKey',
|
||||
'attachmentsToUrls',
|
||||
'ensureTransferAttachments',
|
||||
'applySavedTransfer'
|
||||
],
|
||||
env
|
||||
).saveTransferRow(0, 0, 0)
|
||||
assert.equal(written.transferAttachments, '')
|
||||
assert.equal(tr.transferAttachments, '')
|
||||
})
|
||||
for (const file of [presign, panel]) {
|
||||
for (const fail of [false, true]) {
|
||||
test(`${file} 上传${fail ? '失败' : '成功'}时删除前一个附件不应错位`, async () => {
|
||||
let resolve!: (value: any) => void
|
||||
let reject!: (error: Error) => void
|
||||
const pending = new Promise((yes, no) => {
|
||||
resolve = yes
|
||||
reject = no
|
||||
})
|
||||
const tr = { attachments: [{ id: 'old', fileUrl: 'old.pdf' }] }
|
||||
const env = {
|
||||
form: { transfers: [tr] },
|
||||
props: { modelValue: [{ schedules: [{ transfers: [tr] }] }] },
|
||||
emitChange: () => {},
|
||||
fetchUploadAttachment: () => pending,
|
||||
ElMessage: { warning: () => {}, error: () => {} }
|
||||
}
|
||||
const fns = functions(
|
||||
file,
|
||||
['handleTransferAttachmentChange', 'getTransfer', 'ensureTransferAttachments'],
|
||||
env
|
||||
)
|
||||
const upload = { raw: new File(['x'], 'new.pdf') }
|
||||
const saving =
|
||||
file === presign
|
||||
? fns.handleTransferAttachmentChange(0, upload)
|
||||
: fns.handleTransferAttachmentChange(0, 0, 0, upload)
|
||||
tr.attachments.splice(0, 1)
|
||||
if (fail) reject(new Error('upload failed'))
|
||||
else resolve({ id: 'new', fileUrl: 'new.pdf' })
|
||||
await saving
|
||||
assert.equal(tr.attachments.length, fail ? 0 : 1)
|
||||
if (!fail) assert.equal(tr.attachments[0].fileUrl, 'new.pdf')
|
||||
})
|
||||
}
|
||||
}
|
||||
for (const handler of ['uploadProtocolAttachmentFile', 'handleDetailAttachmentChange']) {
|
||||
for (const fail of [false, true]) {
|
||||
test(`协议 ${handler} 上传${fail ? '失败' : '成功'}不受前项删除影响`, async () => {
|
||||
let resolve!: (value: any) => void
|
||||
let reject!: (error: Error) => void
|
||||
const pending = new Promise((yes, no) => {
|
||||
resolve = yes
|
||||
reject = no
|
||||
})
|
||||
const list = [{ id: 'old', fileUrl: 'old.pdf' }]
|
||||
const env = {
|
||||
formData: { paymentDetails: [{ attachments: list }] },
|
||||
fetchUploadAttachment: () => pending,
|
||||
nextTick: (cb: () => void) => cb(),
|
||||
ElMessage: { error: () => {} }
|
||||
}
|
||||
const fns = functions(protocol, [handler], env)
|
||||
const upload = { raw: new File(['x'], 'new.pdf') }
|
||||
const saving =
|
||||
handler === 'uploadProtocolAttachmentFile'
|
||||
? fns[handler](upload, list, 'protocol', {})
|
||||
: fns[handler](0, upload)
|
||||
list.splice(0, 1)
|
||||
if (fail) reject(new Error('upload failed'))
|
||||
else resolve({ id: 'new', fileUrl: 'new.pdf' })
|
||||
await saving
|
||||
assert.equal(list.length, fail ? 0 : 1)
|
||||
if (!fail) assert.equal(list[0].fileUrl, 'new.pdf')
|
||||
})
|
||||
}
|
||||
}
|
||||
for (const field of [
|
||||
'attachments',
|
||||
'consentAttachments',
|
||||
'healthSurveyAttachments',
|
||||
'paymentDetails',
|
||||
'samplePayments'
|
||||
]) {
|
||||
test(`协议 ${field} 上传中禁止整体保存`, async () => {
|
||||
let validated = false
|
||||
const warnings: string[] = []
|
||||
const file = { _uploading: true }
|
||||
const formData: any = {
|
||||
attachments: [],
|
||||
consentAttachments: [],
|
||||
healthSurveyAttachments: [],
|
||||
paymentDetails: [],
|
||||
samplePayments: []
|
||||
}
|
||||
formData[field] =
|
||||
field === 'paymentDetails'
|
||||
? [{ attachments: [file] }]
|
||||
: field === 'samplePayments'
|
||||
? [{ schedules: [{ transfers: [{ attachments: [file] }] }] }]
|
||||
: [file]
|
||||
const env = {
|
||||
formData,
|
||||
formRef: {
|
||||
value: {
|
||||
validate: async () => {
|
||||
validated = true
|
||||
throw new Error('validation')
|
||||
}
|
||||
}
|
||||
},
|
||||
submitLoading: { value: false },
|
||||
ElMessage: { warning: (v: string) => warnings.push(v) }
|
||||
}
|
||||
await functions(protocol, ['handleSubmit'], env).handleSubmit()
|
||||
assert.equal(validated, false)
|
||||
assert.equal(warnings.length, 1)
|
||||
})
|
||||
}
|
||||
|
||||
test('正式协议清空主单附件、客户和采集字段后提交空值', () => {
|
||||
const formData = new Proxy(
|
||||
{
|
||||
attachments: [],
|
||||
consentAttachments: [],
|
||||
healthSurveyAttachments: [],
|
||||
samples: [],
|
||||
protocolAttachments: 'old.pdf',
|
||||
consentFormUrls: 'old-consent.pdf',
|
||||
healthSurveyFormUrls: 'old-health.pdf'
|
||||
},
|
||||
{ get: (o, key) => (key in o ? o[key as keyof typeof o] : '') }
|
||||
)
|
||||
const payload = JSON.parse(
|
||||
JSON.stringify(
|
||||
expression(protocol, 'payload', {
|
||||
formData,
|
||||
protocolAttachmentUrls: '',
|
||||
consentAttachmentUrls: '',
|
||||
healthSurveyAttachmentUrls: ''
|
||||
})
|
||||
)
|
||||
)
|
||||
for (const key of ['protocolAttachments', 'consentFormUrls', 'healthSurveyFormUrls', 'remark'])
|
||||
assert.equal(payload[key], '', key)
|
||||
for (const key of ['motherPhone', 'motherEmail', 'fatherPhone', 'newbornIdCard'])
|
||||
assert.equal(payload.partyCustomer[key], '', key)
|
||||
for (const key of ['hospitalId', 'medicalStaff', 'sampleCollectTime'])
|
||||
assert.equal(payload.collectionInfo[key], '', key)
|
||||
})
|
||||
|
||||
test('协议详情加载失败后禁止保存未加载的表单', async () => {
|
||||
let validated = false
|
||||
const warnings: string[] = []
|
||||
const env = {
|
||||
isEdit: { value: true },
|
||||
editDetailLoaded: { value: false },
|
||||
formData: {
|
||||
attachments: [],
|
||||
consentAttachments: [],
|
||||
healthSurveyAttachments: [],
|
||||
paymentDetails: [],
|
||||
samplePayments: []
|
||||
},
|
||||
formRef: {
|
||||
value: {
|
||||
validate: async () => {
|
||||
validated = true
|
||||
throw new Error('validation')
|
||||
}
|
||||
}
|
||||
},
|
||||
submitLoading: { value: false },
|
||||
ElMessage: { warning: (v: string) => warnings.push(v) }
|
||||
}
|
||||
await functions(protocol, ['handleSubmit'], env).handleSubmit()
|
||||
assert.equal(validated, false)
|
||||
assert.equal(warnings.length, 1)
|
||||
})
|
||||
test('协议详情读取失败保持未就绪并提示重试', async () => {
|
||||
const messages: string[] = []
|
||||
const ready = { value: true }
|
||||
const env = {
|
||||
props: { listRow: { id: 'p' }, editId: 'p' },
|
||||
isEdit: { value: false },
|
||||
dialogTitle: { value: '' },
|
||||
editingId: { value: '' },
|
||||
editDetailLoaded: ready,
|
||||
formData: {},
|
||||
resetForm: () => {},
|
||||
dialogFormLoading: { value: false },
|
||||
loadEthnicOptions: async () => {},
|
||||
fetchCollectionProtocolDetail: async () => {
|
||||
throw new Error('network')
|
||||
},
|
||||
ElMessage: { error: (v: string) => messages.push(v) },
|
||||
emit: () => {}
|
||||
}
|
||||
await functions(protocol, ['handleOpenForEdit'], env).handleOpenForEdit()
|
||||
assert.equal(ready.value, false)
|
||||
assert.equal(messages.length, 1)
|
||||
})
|
||||
|
||||
test('编辑后新增协议不沿用上一条协议ID', () => {
|
||||
const editingId = { value: 'previous-protocol' }
|
||||
const env = {
|
||||
editingId,
|
||||
isEdit: { value: true },
|
||||
dialogTitle: { value: '' },
|
||||
resetForm: () => {},
|
||||
formData: {},
|
||||
DEFAULT_MOTHER_ETHNIC: '',
|
||||
loadEthnicOptions: () => {},
|
||||
userStore: { getUserInfo: {} }
|
||||
}
|
||||
functions(protocol, ['handleOpenForAdd'], env).handleOpenForAdd()
|
||||
assert.equal(editingId.value, null)
|
||||
})
|
||||
|
||||
function presignOpenWatcher(env: Record<string, any>) {
|
||||
const ast = source(presign)
|
||||
const statement = ast.statements.find(
|
||||
(n) =>
|
||||
ts.isExpressionStatement(n) &&
|
||||
ts.isCallExpression(n.expression) &&
|
||||
n.expression.expression.getText(ast) === 'watch'
|
||||
)!
|
||||
let callback: (open: boolean) => Promise<void>
|
||||
new Function(
|
||||
...Object.keys(env),
|
||||
'watch',
|
||||
ts.transpile(statement.getText(ast), { target: ts.ScriptTarget.ES2022 })
|
||||
)(...Object.values(env), (_: unknown, cb: typeof callback) => {
|
||||
callback = cb
|
||||
})
|
||||
return callback!
|
||||
}
|
||||
|
||||
test('预签约从字典加载开始即处于加载中,全部初始化后才就绪', async () => {
|
||||
let resolve!: () => void
|
||||
const pending = new Promise<void>((yes) => {
|
||||
resolve = yes
|
||||
})
|
||||
const loading = { value: false }
|
||||
const formReady = { value: true }
|
||||
const events: string[] = []
|
||||
const open = presignOpenWatcher({
|
||||
props: { orderId: 'p' },
|
||||
loading,
|
||||
formReady,
|
||||
resetForm: () => {},
|
||||
loadLookups: () => pending,
|
||||
loadDetail: async () => {
|
||||
events.push('detail')
|
||||
},
|
||||
ElMessage: { error: () => {} },
|
||||
emit: () => {}
|
||||
})
|
||||
const initialization = open(true)
|
||||
const during = { loading: loading.value, ready: formReady.value }
|
||||
resolve()
|
||||
await initialization
|
||||
assert.deepEqual(during, { loading: true, ready: false })
|
||||
assert.deepEqual(events, ['detail'])
|
||||
assert.equal(loading.value, false)
|
||||
assert.equal(formReady.value, true)
|
||||
})
|
||||
for (const stage of ['lookups', 'detail']) {
|
||||
test(`预签约${stage}加载失败保持未就绪并提示重试`, async () => {
|
||||
const loading = { value: false }
|
||||
const formReady = { value: true }
|
||||
const errors: string[] = []
|
||||
const open = presignOpenWatcher({
|
||||
props: { orderId: 'p' },
|
||||
loading,
|
||||
formReady,
|
||||
resetForm: () => {},
|
||||
loadLookups: async () => {
|
||||
if (stage === 'lookups') throw new Error('network')
|
||||
},
|
||||
loadDetail: async () => {
|
||||
if (stage === 'detail') throw new Error('network')
|
||||
},
|
||||
ElMessage: { error: (v: string) => errors.push(v) },
|
||||
emit: () => {}
|
||||
})
|
||||
await open(true).catch(() => {})
|
||||
assert.equal(formReady.value, false)
|
||||
assert.equal(loading.value, false)
|
||||
assert.equal(errors.length, 1)
|
||||
})
|
||||
}
|
||||
for (const state of [
|
||||
{ loading: true, ready: false },
|
||||
{ loading: false, ready: false }
|
||||
]) {
|
||||
test(`预签约未初始化成功不得保存:${JSON.stringify(state)}`, async () => {
|
||||
const { env, writes, warnings } = presignEnv()
|
||||
env.loading = { value: state.loading }
|
||||
env.formReady = { value: state.ready }
|
||||
await functions(presign, ['handleSave', 'attachmentsToUrls'], env).handleSave()
|
||||
assert.equal(writes.length, 0)
|
||||
assert.equal(warnings.length, 1)
|
||||
})
|
||||
}
|
||||
@@ -43,6 +43,12 @@ export function fetchCollectionProtocolDetail(id: string) {
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchCollectionProtocolStatusChanges(protocolId: string) {
|
||||
return request.get<{ list: Array<Record<string, unknown>> }>({
|
||||
url: `/web/admin/collection-protocols/${protocolId}/status-changes`
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchCollectionProtocolAuditList(params?: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
|
||||
@@ -110,6 +110,12 @@ export function fetchSubmitDonationAudit(
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchDonationStatusChanges(donationId: string) {
|
||||
return request.get<{ list: Array<Record<string, unknown>> }>({
|
||||
url: `/web/admin/research-donations/${donationId}/status-changes`
|
||||
})
|
||||
}
|
||||
|
||||
// 状态变更
|
||||
export function fetchDonationStatusChange(
|
||||
donationId: string,
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
<div v-loading="loading" class="sample-pay-detail">
|
||||
<template v-if="detail">
|
||||
<div class="detail-summary form-grid-4">
|
||||
<div class="detail-field">
|
||||
<label>协议号</label>
|
||||
<span>{{ detail.protocolNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-field">
|
||||
<label>客户</label>
|
||||
<span>
|
||||
|
||||
@@ -72,7 +72,13 @@
|
||||
>
|
||||
<template #default="{ row }">{{ fmtMoney(row.price) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="存储年限" min-width="96" align="center" header-align="center">
|
||||
<el-table-column
|
||||
v-if="!hideCollectDetailFields"
|
||||
label="存储年限"
|
||||
min-width="96"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">{{
|
||||
row.storageYears != null ? row.storageYears + '年' : '—'
|
||||
}}</template>
|
||||
@@ -413,8 +419,9 @@
|
||||
siteOptions?: Api.Site.SiteItem[]
|
||||
expressCompanyOptions?: Api.ExpressCompany.ExpressCompanyItem[]
|
||||
showPrice?: boolean
|
||||
/** 科研捐赠等:列表与样本弹窗均不展示存储年限及相关采集明细 */
|
||||
hideCollectDetailFields?: boolean
|
||||
/** 添加/编辑样本弹窗内隐藏采集量、单位(不影响列表列与存储年限) */
|
||||
/** 添加/编辑样本弹窗内隐藏采集量、单位(不影响列表列) */
|
||||
hideDialogCollectAmountUnit?: boolean
|
||||
autoGenerateSampleCode?: boolean
|
||||
reserveSampleCode?: () => Promise<string>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- OA 审批详情 · 评论输入(列表已并入审批进度时间线) -->
|
||||
<template>
|
||||
<section v-if="instanceStatus === 'running'" class="oa-comment-panel">
|
||||
<section class="oa-comment-panel">
|
||||
<div class="oa-comment-panel__composer">
|
||||
<div class="oa-comment-panel__editor">
|
||||
<el-input
|
||||
|
||||
@@ -147,6 +147,14 @@ export const payStatus: StatusRegistry = {
|
||||
export const payStatusLabel = createLabelGetter(payStatus)
|
||||
export const payStatusTagType = createTagTypeGetter(payStatus)
|
||||
|
||||
/** 采集协议开票申请状态(biz_protocol_invoice_request.status) */
|
||||
export const invoiceRequestStatus: StatusRegistry = {
|
||||
dkp: { label: '待开票', tagType: 'warning' },
|
||||
ykp: { label: '已开票', tagType: 'success' }
|
||||
}
|
||||
export const invoiceRequestStatusLabel = createLabelGetter(invoiceRequestStatus)
|
||||
export const invoiceRequestStatusTagType = createTagTypeGetter(invoiceRequestStatus)
|
||||
|
||||
export const auditStatus: StatusRegistry = {
|
||||
shtg: { label: '审核通过', tagType: 'success' },
|
||||
shth: { label: '审核退回', tagType: 'danger' }
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="协议号/客户/样本/电话"
|
||||
placeholder="客户/样本/电话"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
@@ -69,13 +69,6 @@
|
||||
align="center"
|
||||
:index="(i) => (pagination.page - 1) * pagination.pageSize + i + 1"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="protocolNo"
|
||||
label="协议号"
|
||||
min-width="140"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="客户" min-width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.motherName || '—' }}</div>
|
||||
@@ -323,12 +316,12 @@
|
||||
|
||||
function formatAmount(v: unknown) {
|
||||
if (v == null || v === '') return '—'
|
||||
return formatMoneyDisplay(v)
|
||||
return formatMoneyDisplay(v as string | number)
|
||||
}
|
||||
|
||||
function formatAmountYuan(v: unknown) {
|
||||
if (v == null || v === '') return '—'
|
||||
return `${formatMoneyDisplay(v)} 元`
|
||||
return `${formatMoneyDisplay(v as string | number)} 元`
|
||||
}
|
||||
|
||||
function hasPaidAmount(row: Record<string, any>) {
|
||||
@@ -414,7 +407,8 @@
|
||||
const scheduleId = String(row.scheduleId || '')
|
||||
const samplePaymentId = String(row.samplePaymentId || '')
|
||||
if (!scheduleId || !samplePaymentId) return
|
||||
periodDialogTitle.value = `${row.motherName || ''} · ${row.protocolNo || ''} · 第 ${row.scheduleNo || '—'} 期转账记录`
|
||||
const titleParts = [row.motherName, row.sampleCode].filter(Boolean)
|
||||
periodDialogTitle.value = `${titleParts.join(' · ')}${titleParts.length ? ' · ' : ''}第 ${row.scheduleNo || '—'} 期转账记录`
|
||||
periodDialogVisible.value = true
|
||||
periodLoading.value = true
|
||||
periodTransfers.value = []
|
||||
|
||||
@@ -55,10 +55,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="pending-card__grid">
|
||||
<div class="pending-card__field">
|
||||
<label>协议号</label>
|
||||
<span>{{ row.protocolNo || '-' }}</span>
|
||||
</div>
|
||||
<div class="pending-card__field">
|
||||
<label>包含期数</label>
|
||||
<span>{{ row.installmentSummary || '-' }}</span>
|
||||
@@ -70,7 +66,12 @@
|
||||
</div>
|
||||
<div class="pending-card__foot">
|
||||
<div class="pending-card__meta">
|
||||
<el-tag type="warning" size="small">待开票</el-tag>
|
||||
<el-tag
|
||||
:type="invoiceRequestStatusTagType(String(row.status || 'dkp'))"
|
||||
size="small"
|
||||
>
|
||||
{{ invoiceRequestStatusLabel(String(row.status || 'dkp')) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<el-button class="btn-op-item primary" @click="openProcess(row)">
|
||||
<el-icon><Check /></el-icon>处理开票
|
||||
@@ -199,8 +200,11 @@
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 'ykp' ? 'success' : 'warning'" size="small">
|
||||
{{ row.status === 'ykp' ? '已开票' : '待开票' }}
|
||||
<el-tag
|
||||
:type="invoiceRequestStatusTagType(String(row.status || ''))"
|
||||
size="small"
|
||||
>
|
||||
{{ invoiceRequestStatusLabel(String(row.status || '')) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -254,9 +258,6 @@
|
||||
<el-form-item label="客户姓名">
|
||||
<span class="view-field-text">{{ processRow.motherName || '—' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="协议号">
|
||||
<span class="view-field-text">{{ processRow.protocolNo || '—' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号">
|
||||
<span class="view-field-text">{{ maskIdCard(processRow.motherIdCard) || '—' }}</span>
|
||||
</el-form-item>
|
||||
@@ -371,9 +372,6 @@
|
||||
<el-form-item label="客户姓名">
|
||||
<span class="view-field-text">{{ viewRow.motherName || '—' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="协议号">
|
||||
<span class="view-field-text">{{ viewRow.protocolNo || '—' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号">
|
||||
<span class="view-field-text">{{ maskIdCard(viewRow.motherIdCard) || '—' }}</span>
|
||||
</el-form-item>
|
||||
@@ -394,8 +392,11 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<span class="view-field-tag">
|
||||
<el-tag :type="viewRow.status === 'ykp' ? 'success' : 'warning'" size="small">
|
||||
{{ viewRow.status === 'ykp' ? '已开票' : '待开票' }}
|
||||
<el-tag
|
||||
:type="invoiceRequestStatusTagType(String(viewRow.status || ''))"
|
||||
size="small"
|
||||
>
|
||||
{{ invoiceRequestStatusLabel(String(viewRow.status || '')) }}
|
||||
</el-tag>
|
||||
</span>
|
||||
</el-form-item>
|
||||
@@ -507,6 +508,10 @@
|
||||
import { maskIdCard } from '@/utils/mask-sensitive'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
import { transferTypeLabel, transferTypeTagType, loadTransferTypeDict } from '@/utils/protocol-payment-labels'
|
||||
import {
|
||||
invoiceRequestStatusLabel,
|
||||
invoiceRequestStatusTagType
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
|
||||
defineOptions({ name: 'CellStorageInvoiceRequestTab' })
|
||||
|
||||
@@ -93,7 +93,12 @@
|
||||
</div>
|
||||
<div class="pending-card__foot">
|
||||
<div class="pending-card__meta">
|
||||
<el-tag type="warning" size="small">退费确认中</el-tag>
|
||||
<el-tag
|
||||
:type="payStatusTagType(String(row.payStatus || 'tfqrz'))"
|
||||
size="small"
|
||||
>
|
||||
{{ payStatusLabel(String(row.payStatus || 'tfqrz')) }}
|
||||
</el-tag>
|
||||
<span>提交 {{ formatDisplayOrDash(row.createTime) }}</span>
|
||||
</div>
|
||||
<el-button class="btn-op-item primary" @click="openConfirm(row)">
|
||||
@@ -332,8 +337,13 @@
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default>
|
||||
<el-tag type="success" size="small">已退费</el-tag>
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="payStatusTagType(String(row.payStatus || 'ytf'))"
|
||||
size="small"
|
||||
>
|
||||
{{ payStatusLabel(String(row.payStatus || 'ytf')) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -485,6 +495,8 @@
|
||||
import {
|
||||
formatPaymentDueDate,
|
||||
payModeLabel,
|
||||
payStatusLabel,
|
||||
payStatusTagType,
|
||||
protocolStatusLabel,
|
||||
protocolStatusDisplayLabel,
|
||||
protocolStatusTagType,
|
||||
|
||||
@@ -55,9 +55,7 @@
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="pendingSearchForm.keyword"
|
||||
:placeholder="
|
||||
isPresignTab ? '协议号/客户姓名' : '协议号/样本编码/客户姓名/身份证号'
|
||||
"
|
||||
:placeholder="isPresignTab ? '客户姓名' : '样本编码/客户姓名/身份证号'"
|
||||
clearable
|
||||
@keyup.enter="handlePendingSearch"
|
||||
/>
|
||||
@@ -107,15 +105,6 @@
|
||||
(index) => (pendingPagination.page - 1) * pendingPagination.pageSize + index + 1
|
||||
"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="protocolNo"
|
||||
label="协议号"
|
||||
min-width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">{{ row.protocolNo || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="sampleCode"
|
||||
:label="isPresignTab ? '样本类型' : '样本编码'"
|
||||
@@ -267,8 +256,13 @@
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default>
|
||||
<el-tag type="warning" size="small">付款确认中</el-tag>
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="payStatusTagType(String(row.payStatus || 'jfqrz'))"
|
||||
size="small"
|
||||
>
|
||||
{{ payStatusLabel(String(row.payStatus || 'jfqrz')) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -334,9 +328,7 @@
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="confirmedSearchForm.keyword"
|
||||
:placeholder="
|
||||
isPresignTab ? '协议号/客户姓名' : '协议号/样本编码/客户姓名/身份证号'
|
||||
"
|
||||
:placeholder="isPresignTab ? '客户姓名' : '样本编码/客户姓名/身份证号'"
|
||||
clearable
|
||||
@keyup.enter="handleConfirmedSearch"
|
||||
/>
|
||||
@@ -386,15 +378,6 @@
|
||||
(index) => (confirmedPagination.page - 1) * confirmedPagination.pageSize + index + 1
|
||||
"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="protocolNo"
|
||||
label="协议号"
|
||||
min-width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">{{ row.protocolNo || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="sampleCode"
|
||||
:label="isPresignTab ? '样本类型' : '样本编码'"
|
||||
@@ -546,8 +529,13 @@
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default>
|
||||
<el-tag type="success" size="small">付款完成</el-tag>
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="payStatusTagType(String(row.payStatus || 'yjf'))"
|
||||
size="small"
|
||||
>
|
||||
{{ payStatusLabel(String(row.payStatus || 'yjf')) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -715,6 +703,8 @@
|
||||
import {
|
||||
formatPaymentDueDate,
|
||||
payModeLabel,
|
||||
payStatusLabel,
|
||||
payStatusTagType,
|
||||
protocolStatusDisplayLabel,
|
||||
protocolStatusTagType,
|
||||
loadTransferTypeDict,
|
||||
@@ -745,7 +735,7 @@
|
||||
|
||||
function formatPaymentAmount(v: unknown): string {
|
||||
if (v == null || v === '') return '-'
|
||||
return '¥' + formatMoneyDisplay(v)
|
||||
return '¥' + formatMoneyDisplay(v as string | number)
|
||||
}
|
||||
|
||||
function isPresignRow(row: Record<string, unknown> | null | undefined) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="协议号/客户/样本编码/电话"
|
||||
placeholder="客户/样本编码/电话"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
@@ -80,14 +80,6 @@
|
||||
header-align="center"
|
||||
:index="(i) => (pagination.page - 1) * pagination.pageSize + i + 1"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="protocolNo"
|
||||
label="协议号"
|
||||
min-width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="客户" min-width="140" align="center" header-align="center">
|
||||
<template #default="{ row }">
|
||||
<div class="customer-cell">
|
||||
@@ -220,10 +212,6 @@
|
||||
<div v-loading="detailLoading" class="sample-pay-detail">
|
||||
<template v-if="detail">
|
||||
<div class="detail-summary form-grid-2">
|
||||
<div class="detail-field">
|
||||
<label>协议号</label>
|
||||
<span>{{ detail.protocolNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="detail-field">
|
||||
<label>客户</label>
|
||||
<span>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="协议号/客户/样本编码/电话"
|
||||
placeholder="客户/样本编码/电话"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
@@ -363,12 +363,12 @@
|
||||
|
||||
function formatAmount(v: unknown) {
|
||||
if (v == null || v === '') return '—'
|
||||
return formatMoneyDisplay(v)
|
||||
return formatMoneyDisplay(v as string | number)
|
||||
}
|
||||
|
||||
function formatAmountYuan(v: unknown) {
|
||||
if (v == null || v === '') return '—'
|
||||
return `${formatMoneyDisplay(v)} 元`
|
||||
return `${formatMoneyDisplay(v as string | number)} 元`
|
||||
}
|
||||
|
||||
function transferAttachmentFiles(tr: Record<string, any>) {
|
||||
@@ -491,7 +491,8 @@
|
||||
async function openPeriodDialog(row: Record<string, any>, idx: number) {
|
||||
const cell = periodRaw(row, idx)
|
||||
if (!cell?.scheduleId) return
|
||||
periodDialogTitle.value = `${row.motherName || ''} · ${row.protocolNo || ''} · 第 ${idx + 1} 期转账记录`
|
||||
const titleParts = [row.motherName, row.sampleCode].filter(Boolean)
|
||||
periodDialogTitle.value = `${titleParts.join(' · ')}${titleParts.length ? ' · ' : ''}第 ${idx + 1} 期转账记录`
|
||||
periodDialogVisible.value = true
|
||||
periodLoading.value = true
|
||||
periodTransfers.value = []
|
||||
|
||||
@@ -15,7 +15,13 @@
|
||||
@closed="resetForm"
|
||||
>
|
||||
<DialogFormBody v-loading="loading">
|
||||
<el-form ref="formRef" :model="form" label-position="top" scroll-to-error>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:disabled="loading || !formReady"
|
||||
label-position="top"
|
||||
scroll-to-error
|
||||
>
|
||||
<div class="form-grid-3">
|
||||
<el-form-item label="业务日期">
|
||||
<el-date-picker
|
||||
@@ -371,7 +377,7 @@
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
:loading="saving"
|
||||
:disabled="isConverted"
|
||||
:disabled="isConverted || loading || !formReady"
|
||||
@click="handleSave"
|
||||
>
|
||||
<el-icon><Check /></el-icon>
|
||||
@@ -430,6 +436,7 @@
|
||||
|
||||
const formRef = ref()
|
||||
const loading = ref(false)
|
||||
const formReady = ref(false)
|
||||
const saving = ref(false)
|
||||
const transferTypeOptions = ref<Api.Dict.DictDataOption[]>([])
|
||||
const sampleTypeOptions = ref<Array<Record<string, any>>>([])
|
||||
@@ -457,7 +464,7 @@
|
||||
nickName: String(
|
||||
currentUser?.realName || currentUser?.userName || currentUser?.username || ''
|
||||
),
|
||||
phoneNo: String(currentUser?.phone || currentUser?.phoneNo || ''),
|
||||
phoneNo: String(currentUser?.phone || ''),
|
||||
deptId: '',
|
||||
userNo: '',
|
||||
email: '',
|
||||
@@ -572,13 +579,14 @@
|
||||
fileUrl: '',
|
||||
_uploading: true
|
||||
})
|
||||
const idx = row.attachments.length - 1
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('folder', 'presign-transfer')
|
||||
fd.append('file', file)
|
||||
const res: any = await fetchUploadAttachment(fd)
|
||||
const data = res?.data || res
|
||||
const idx = row.attachments.findIndex((f: TransferAttachment) => f.id === tempId)
|
||||
if (idx < 0) return
|
||||
row.attachments[idx] = {
|
||||
id: data?.id || tempId,
|
||||
fileName: data?.fileName || file.name,
|
||||
@@ -587,7 +595,8 @@
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error('文件上传失败')
|
||||
row.attachments.splice(idx, 1)
|
||||
const idx = row.attachments.findIndex((f: TransferAttachment) => f.id === tempId)
|
||||
if (idx >= 0) row.attachments.splice(idx, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,6 +795,10 @@
|
||||
}
|
||||
|
||||
async function submitTransfer(tr: Record<string, any>) {
|
||||
if ((tr.attachments || []).some((f: TransferAttachment) => f._uploading)) {
|
||||
ElMessage.warning('凭证正在上传,请稍后再提交')
|
||||
return
|
||||
}
|
||||
if (!props.orderId || !tr.id) {
|
||||
ElMessage.warning('请先保存订单后再提交财务确认')
|
||||
return
|
||||
@@ -794,9 +807,9 @@
|
||||
await fetchUpdatePresignTransfer(String(tr.id), {
|
||||
transferType: tr.transferType,
|
||||
transferAmount: tr.transferAmount,
|
||||
transferTime: tr.transferTime || undefined,
|
||||
transferAttachments: attachmentsToUrls(tr.attachments) || undefined,
|
||||
remark: tr.remark || undefined
|
||||
transferTime: tr.transferTime || '',
|
||||
transferAttachments: attachmentsToUrls(tr.attachments),
|
||||
remark: tr.remark || ''
|
||||
})
|
||||
}
|
||||
await fetchSubmitPresignTransferFinance(String(tr.id))
|
||||
@@ -817,6 +830,19 @@
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (saving.value) return
|
||||
if (loading.value || !formReady.value) {
|
||||
ElMessage.warning('订单尚未初始化成功,请稍候或重新打开后重试')
|
||||
return
|
||||
}
|
||||
if (
|
||||
form.transfers.some((tr) =>
|
||||
(tr.attachments || []).some((f: TransferAttachment) => f._uploading)
|
||||
)
|
||||
) {
|
||||
ElMessage.warning('凭证正在上传,请稍后再保存')
|
||||
return
|
||||
}
|
||||
if (!form.motherName.trim()) {
|
||||
ElMessage.warning('请填写母亲姓名')
|
||||
return
|
||||
@@ -830,17 +856,17 @@
|
||||
const payload: Record<string, unknown> = {
|
||||
orderDate: form.orderDate || undefined,
|
||||
motherName: form.motherName,
|
||||
expectedDeliveryDate: form.expectedDeliveryDate || undefined,
|
||||
regionId: form.regionId || undefined,
|
||||
hospitalId: form.hospitalId || undefined,
|
||||
sampleCollectTime: form.sampleCollectTime || undefined,
|
||||
collectorUserId: form.collectorUserId || undefined,
|
||||
regionManagerUserId: form.regionManagerUserId || undefined,
|
||||
expectedDeliveryDate: form.expectedDeliveryDate || '',
|
||||
regionId: form.regionId || '',
|
||||
hospitalId: form.hospitalId || '',
|
||||
sampleCollectTime: form.sampleCollectTime || '',
|
||||
collectorUserId: form.collectorUserId || '',
|
||||
regionManagerUserId: form.regionManagerUserId || '',
|
||||
payMode: form.payMode || undefined,
|
||||
contractAmount: form.contractAmount ?? undefined,
|
||||
contractAmount: form.contractAmount ?? null,
|
||||
orderStatus: form.orderStatus || 'active',
|
||||
sampleTypeIds: form.sampleTypeIds,
|
||||
remark: form.remark || undefined
|
||||
remark: form.remark || ''
|
||||
}
|
||||
let transfersForSubmit: Array<Record<string, any>> = []
|
||||
|
||||
@@ -851,18 +877,18 @@
|
||||
const created: any = await fetchCreatePresignTransfer(props.orderId, {
|
||||
transferType: tr.transferType,
|
||||
transferAmount: tr.transferAmount,
|
||||
transferTime: tr.transferTime || undefined,
|
||||
transferAttachments: attachmentsToUrls(tr.attachments) || undefined,
|
||||
remark: tr.remark || undefined
|
||||
transferTime: tr.transferTime || '',
|
||||
transferAttachments: attachmentsToUrls(tr.attachments),
|
||||
remark: tr.remark || ''
|
||||
})
|
||||
if (created?.id) tr.id = String(created.id)
|
||||
} else if (tr.id && tr.payStatus === 'dfj') {
|
||||
await fetchUpdatePresignTransfer(String(tr.id), {
|
||||
transferType: tr.transferType,
|
||||
transferAmount: tr.transferAmount,
|
||||
transferTime: tr.transferTime || undefined,
|
||||
transferAttachments: attachmentsToUrls(tr.attachments) || undefined,
|
||||
remark: tr.remark || undefined
|
||||
transferTime: tr.transferTime || '',
|
||||
transferAttachments: attachmentsToUrls(tr.attachments),
|
||||
remark: tr.remark || ''
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -873,9 +899,9 @@
|
||||
.map((t) => ({
|
||||
transferType: t.transferType,
|
||||
transferAmount: t.transferAmount,
|
||||
transferTime: t.transferTime || undefined,
|
||||
transferAttachments: attachmentsToUrls(t.attachments) || undefined,
|
||||
remark: t.remark || undefined
|
||||
transferTime: t.transferTime || '',
|
||||
transferAttachments: attachmentsToUrls(t.attachments),
|
||||
remark: t.remark || ''
|
||||
}))
|
||||
const createdOrder: any = await fetchCreatePresignOrder(payload)
|
||||
transfersForSubmit = Array.isArray(createdOrder?.transfers)
|
||||
@@ -901,9 +927,19 @@
|
||||
() => props.modelValue,
|
||||
async (open) => {
|
||||
if (!open) return
|
||||
loading.value = true
|
||||
formReady.value = false
|
||||
resetForm()
|
||||
await loadLookups()
|
||||
if (props.orderId) await loadDetail()
|
||||
try {
|
||||
await loadLookups()
|
||||
if (props.orderId) await loadDetail()
|
||||
formReady.value = true
|
||||
} catch {
|
||||
ElMessage.error('订单初始化失败,请重新打开后重试')
|
||||
emit('update:modelValue', false)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="母亲姓名/协议编号"
|
||||
placeholder="母亲姓名"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
@@ -81,15 +81,6 @@
|
||||
>
|
||||
<template #default="{ row }">{{ formatYmd(row.orderDate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="protocolNo"
|
||||
label="协议编号"
|
||||
min-width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">{{ row.protocolNo || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="motherName"
|
||||
label="母亲姓名"
|
||||
@@ -217,7 +208,7 @@
|
||||
|
||||
function fmtMoney(v: unknown) {
|
||||
if (v == null || v === '') return '-'
|
||||
return '¥' + formatMoneyDisplay(v)
|
||||
return '¥' + formatMoneyDisplay(v as string | number)
|
||||
}
|
||||
|
||||
function formatYmd(v: unknown) {
|
||||
@@ -276,9 +267,8 @@
|
||||
'转正式协议',
|
||||
{ type: 'warning' }
|
||||
)
|
||||
const res: any = await fetchConvertPresignOrder(String(row.id))
|
||||
const protocolNo = res?.protocolNo || res?.data?.protocolNo
|
||||
ElMessage.success(protocolNo ? `已生成正式协议 ${protocolNo}` : '已转正式协议')
|
||||
await fetchConvertPresignOrder(String(row.id))
|
||||
ElMessage.success('已转正式协议')
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
||||
@@ -65,16 +65,6 @@
|
||||
header-align="center"
|
||||
:index="(i) => (pagination.page - 1) * pagination.pageSize + i + 1"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="protocolNo"
|
||||
label="协议号"
|
||||
min-width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">{{ row.protocolNo || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户" min-width="150" align="center" header-align="center">
|
||||
<template #default="{ row }">
|
||||
<div>{{ row.motherName || '—' }}</div>
|
||||
|
||||
@@ -537,9 +537,7 @@
|
||||
}
|
||||
const row = rows[0]
|
||||
refundProtocolId.value = String(row.id || '')
|
||||
const name = String(row.motherName || '').trim()
|
||||
const no = String(row.protocolNo || '').trim()
|
||||
refundProtocolLabel.value = [name, no].filter(Boolean).join(' / ')
|
||||
refundProtocolLabel.value = String(row.motherName || '').trim()
|
||||
refundDialogVisible.value = true
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
<SampleTransportFormSection
|
||||
v-model:samples="formData.samples"
|
||||
sample-source="cell_storage"
|
||||
:protocol-id="formData.id || ''"
|
||||
:protocol-id="editingId || ''"
|
||||
hide-dialog-collect-amount-unit
|
||||
:sample-type-options="sampleTypeOptions"
|
||||
:sample-type-options-loading="sampleTypeLoading"
|
||||
@@ -1232,9 +1232,6 @@
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import {
|
||||
payModeLabel,
|
||||
newbornSexLabel,
|
||||
deliveryModeLabel,
|
||||
sampleStatusLabel,
|
||||
loadTransferTypeDict,
|
||||
getTransferTypeOptions,
|
||||
transferTypeLabel,
|
||||
@@ -1262,7 +1259,7 @@
|
||||
/** 金额只读展示:千分位 + ¥ 前缀 */
|
||||
function fmtMoney(v: unknown): string {
|
||||
if (v == null || v === '') return '—'
|
||||
return '¥' + formatMoneyDisplay(v)
|
||||
return '¥' + formatMoneyDisplay(v as string | number)
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
@@ -1469,6 +1466,7 @@
|
||||
|
||||
/* ====== 新增/编辑弹窗 ====== */
|
||||
const dialogFormLoading = ref(false)
|
||||
const editDetailLoaded = ref(false)
|
||||
const dialogTitle = ref('新增协议单')
|
||||
const isEdit = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
@@ -2078,6 +2076,7 @@
|
||||
|
||||
function handleOpenForAdd() {
|
||||
isEdit.value = false
|
||||
editingId.value = null
|
||||
dialogTitle.value = '新增协议单'
|
||||
resetForm()
|
||||
formData.protocolStatus = 'sxz'
|
||||
@@ -2278,6 +2277,7 @@
|
||||
if (!row || !props.editId) return
|
||||
|
||||
isEdit.value = true
|
||||
editDetailLoaded.value = false
|
||||
dialogTitle.value = '编辑协议单'
|
||||
editingId.value = props.editId
|
||||
resetForm()
|
||||
@@ -2296,11 +2296,11 @@
|
||||
try {
|
||||
const [, bundle] = await Promise.all([
|
||||
loadEthnicOptions(),
|
||||
fetchCollectionProtocolDetail(String(row.id)).catch(() => null)
|
||||
fetchCollectionProtocolDetail(String(row.id))
|
||||
])
|
||||
if (bundle) {
|
||||
applyProtocolBundleToForm(bundle as Record<string, any>, row)
|
||||
}
|
||||
if (!bundle) throw new Error('协议详情为空')
|
||||
applyProtocolBundleToForm(bundle as Record<string, any>, row)
|
||||
editDetailLoaded.value = true
|
||||
|
||||
const ciBundle = (bundle as any)?.collectionInfo as Record<string, any> | null
|
||||
const hydrateTasks: Promise<void>[] = [
|
||||
@@ -2345,7 +2345,10 @@
|
||||
hydrateSampleLookupOptions()
|
||||
}
|
||||
} catch {
|
||||
/* 保留列表行已填基础字段 */
|
||||
if (!editDetailLoaded.value) {
|
||||
ElMessage.error('协议详情加载失败,请重新打开后重试')
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
} finally {
|
||||
dialogFormLoading.value = false
|
||||
}
|
||||
@@ -2353,6 +2356,25 @@
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!formRef.value || submitLoading.value) return
|
||||
if (isEdit.value && !editDetailLoaded.value) {
|
||||
ElMessage.warning('协议详情尚未加载成功,请重新打开后重试')
|
||||
return
|
||||
}
|
||||
const uploadLists = [
|
||||
formData.attachments,
|
||||
formData.consentAttachments,
|
||||
formData.healthSurveyAttachments,
|
||||
...formData.paymentDetails.map((d) => d.attachments || []),
|
||||
...formData.samplePayments.flatMap((p) =>
|
||||
(p.schedules || []).flatMap((sch: any) =>
|
||||
(sch.transfers || []).map((t: any) => t.attachments || [])
|
||||
)
|
||||
)
|
||||
]
|
||||
if (uploadLists.some((list) => list.some((f: any) => f._uploading))) {
|
||||
ElMessage.warning('附件正在上传,请稍后再保存')
|
||||
return
|
||||
}
|
||||
submitLoading.value = true
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
@@ -2438,12 +2460,11 @@
|
||||
.join(',')
|
||||
const payload: Record<string, unknown> = {
|
||||
protocolStatus: formData.protocolStatus,
|
||||
registerDate: formData.registerDate || undefined,
|
||||
collectorUserId: formData.collectorUserId || undefined,
|
||||
protocolAttachments: protocolAttachmentUrls || formData.protocolAttachments || undefined,
|
||||
consentFormUrls: consentAttachmentUrls || formData.consentFormUrls || undefined,
|
||||
healthSurveyFormUrls:
|
||||
healthSurveyAttachmentUrls || formData.healthSurveyFormUrls || undefined,
|
||||
registerDate: formData.registerDate || '',
|
||||
collectorUserId: formData.collectorUserId || '',
|
||||
protocolAttachments: protocolAttachmentUrls,
|
||||
consentFormUrls: consentAttachmentUrls,
|
||||
healthSurveyFormUrls: healthSurveyAttachmentUrls,
|
||||
attachments: formData.attachments.map((a) => ({
|
||||
id: a.id,
|
||||
fileName: a.fileName,
|
||||
@@ -2468,39 +2489,39 @@
|
||||
fileSizeDisplay: a.fileSizeDisplay,
|
||||
fileType: a.fileType
|
||||
})),
|
||||
remark: formData.remark || undefined,
|
||||
remark: formData.remark || '',
|
||||
partyCustomer: {
|
||||
customerId: formData.customerId || undefined,
|
||||
fatherCustomerId: formData.fatherCustomerId || undefined,
|
||||
motherName: formData.motherName || undefined,
|
||||
motherPhone: formData.motherPhone || undefined,
|
||||
motherIdCard: formData.motherIdCard || undefined,
|
||||
motherBirthYm: formData.motherBirthYm || undefined,
|
||||
motherEthnic: formData.motherEthnic || undefined,
|
||||
motherEmail: formData.motherEmail || undefined,
|
||||
motherAddress: formData.motherAddress || undefined,
|
||||
fatherName: formData.fatherName || undefined,
|
||||
fatherPhone: formData.fatherPhone || undefined,
|
||||
fatherIdCard: formData.fatherIdCard || undefined,
|
||||
newbornName: formData.newbornName || undefined,
|
||||
newbornIdCard: formData.newbornIdCard || undefined,
|
||||
expectedDeliveryDate: formData.expectedDeliveryDate || undefined,
|
||||
newbornDeliveryTime: formData.newbornDeliveryTime || undefined,
|
||||
newbornSex: formData.newbornSex || undefined,
|
||||
deliveryMode: formData.deliveryMode || undefined,
|
||||
birthOrder: formData.birthOrder ?? undefined,
|
||||
newbornWeightGram: formData.newbornWeightGram ?? undefined,
|
||||
apgar1min: formData.apgar1min ?? undefined,
|
||||
apgar5min: formData.apgar5min ?? undefined
|
||||
customerId: formData.customerId || '',
|
||||
fatherCustomerId: formData.fatherCustomerId || '',
|
||||
motherName: formData.motherName || '',
|
||||
motherPhone: formData.motherPhone || '',
|
||||
motherIdCard: formData.motherIdCard || '',
|
||||
motherBirthYm: formData.motherBirthYm || '',
|
||||
motherEthnic: formData.motherEthnic || '',
|
||||
motherEmail: formData.motherEmail || '',
|
||||
motherAddress: formData.motherAddress || '',
|
||||
fatherName: formData.fatherName || '',
|
||||
fatherPhone: formData.fatherPhone || '',
|
||||
fatherIdCard: formData.fatherIdCard || '',
|
||||
newbornName: formData.newbornName || '',
|
||||
newbornIdCard: formData.newbornIdCard || '',
|
||||
expectedDeliveryDate: formData.expectedDeliveryDate || '',
|
||||
newbornDeliveryTime: formData.newbornDeliveryTime || '',
|
||||
newbornSex: formData.newbornSex || '',
|
||||
deliveryMode: formData.deliveryMode || '',
|
||||
birthOrder: formData.birthOrder ?? null,
|
||||
newbornWeightGram: formData.newbornWeightGram ?? null,
|
||||
apgar1min: formData.apgar1min ?? null,
|
||||
apgar5min: formData.apgar5min ?? null
|
||||
},
|
||||
collectionInfo: {
|
||||
regionId: formData.regionId || undefined,
|
||||
hospitalId: formData.hospitalId || undefined,
|
||||
medicalStaff: formData.medicalStaff || undefined,
|
||||
salesUserId: formData.salesUserId || undefined,
|
||||
regionManagerUserId: formData.regionManagerUserId || undefined,
|
||||
sampleCollectTime: formData.sampleCollectTime || undefined,
|
||||
maternalBloodCollectTime: formData.maternalBloodCollectTime || undefined
|
||||
regionId: formData.regionId || '',
|
||||
hospitalId: formData.hospitalId || '',
|
||||
medicalStaff: formData.medicalStaff || '',
|
||||
salesUserId: formData.salesUserId || '',
|
||||
regionManagerUserId: formData.regionManagerUserId || '',
|
||||
sampleCollectTime: formData.sampleCollectTime || '',
|
||||
maternalBloodCollectTime: formData.maternalBloodCollectTime || ''
|
||||
},
|
||||
samples: formData.samples.map(({ _rowKey, transport, freight, freightDraft, ...s }) => ({
|
||||
...s,
|
||||
@@ -2560,7 +2581,9 @@
|
||||
transferType: t.transferType,
|
||||
transferAmount: t.transferAmount,
|
||||
transferTime: t.transferTime,
|
||||
transferAttachments: fromFiles || t.transferAttachments || undefined,
|
||||
transferAttachments: Array.isArray(t.attachments)
|
||||
? fromFiles
|
||||
: t.transferAttachments || '',
|
||||
payStatus: t.payStatus || 'dfj',
|
||||
remark: t.remark
|
||||
}
|
||||
@@ -3376,13 +3399,15 @@
|
||||
const q = (query || '').trim()
|
||||
if (!q) {
|
||||
cb([])
|
||||
return
|
||||
return []
|
||||
}
|
||||
try {
|
||||
const res = await fetchCustomerList({ page: 1, pageSize: 20, keyword: q })
|
||||
cb(res.list || [])
|
||||
return res.list || []
|
||||
} catch {
|
||||
cb([])
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3524,13 +3549,13 @@
|
||||
fileType: file.type || '',
|
||||
_uploading: true
|
||||
})
|
||||
const idx = list.length - 1
|
||||
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('folder', folder)
|
||||
fd.append('file', file)
|
||||
const res = await fetchUploadAttachment(fd)
|
||||
const idx = list.findIndex((f) => f.id === tempId)
|
||||
if (idx < 0) return
|
||||
list[idx] = {
|
||||
...list[idx],
|
||||
id: res.id || tempId,
|
||||
@@ -3543,7 +3568,8 @@
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error('文件上传失败')
|
||||
list.splice(idx, 1)
|
||||
const idx = list.findIndex((f) => f.id === tempId)
|
||||
if (idx >= 0) list.splice(idx, 1)
|
||||
} finally {
|
||||
nextTick(() => uploadRef.value?.clearFiles())
|
||||
}
|
||||
@@ -3668,13 +3694,13 @@
|
||||
fileType: file.type || '',
|
||||
_uploading: true
|
||||
})
|
||||
const aIdx = detail.attachments.length - 1
|
||||
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('folder', 'protocol-payment')
|
||||
fd.append('file', file)
|
||||
const res = await fetchUploadAttachment(fd)
|
||||
const aIdx = detail.attachments.findIndex((f) => f.id === tempId)
|
||||
if (aIdx < 0) return
|
||||
detail.attachments[aIdx] = {
|
||||
...detail.attachments[aIdx],
|
||||
id: res.id || tempId,
|
||||
@@ -3687,7 +3713,8 @@
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error('文件上传失败')
|
||||
detail.attachments.splice(aIdx, 1)
|
||||
const aIdx = detail.attachments.findIndex((f) => f.id === tempId)
|
||||
if (aIdx >= 0) detail.attachments.splice(aIdx, 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
:title="dialogTitle"
|
||||
width="720px"
|
||||
width="960px"
|
||||
align-center
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@@ -27,10 +27,17 @@
|
||||
<template #default="{ row }">{{ fmtMoney(row.refundAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="refundTime" label="退费时间" min-width="160" />
|
||||
<el-table-column prop="refundRemark" label="备注" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="附件" min-width="100">
|
||||
<el-table-column prop="refundRemark" label="备注" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="附件" min-width="280">
|
||||
<template #default="{ row }">
|
||||
{{ (row.refundAttachmentFiles || []).length || (row.refundAttachments ? 1 : 0) }} 个
|
||||
<ProtocolAttachmentReadonly
|
||||
v-if="rowAttachmentFiles(row).length"
|
||||
:files="rowAttachmentFiles(row)"
|
||||
size="compact"
|
||||
@preview="handleListPreview"
|
||||
@download="handleDownloadAttachment"
|
||||
/>
|
||||
<span v-else class="muted-dash">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="140" fixed="right">
|
||||
@@ -52,7 +59,7 @@
|
||||
<el-dialog
|
||||
v-model="formVisible"
|
||||
:title="editingId ? '编辑退费登记' : '新增退费登记'"
|
||||
width="560px"
|
||||
width="720px"
|
||||
align-center
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
@@ -87,14 +94,15 @@
|
||||
<div class="attachment-upload">
|
||||
<div class="attachment-list">
|
||||
<el-upload
|
||||
ref="attachmentUploadRef"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:multiple="true"
|
||||
accept="image/*,.pdf,.doc,.docx,.xls,.xlsx,.zip,.rar"
|
||||
accept="image/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.zip,.rar"
|
||||
@change="onFileChange"
|
||||
>
|
||||
<div class="upload-card upload-card--sm">
|
||||
<el-icon :size="22" color="#047857"><Plus /></el-icon>
|
||||
<div class="upload-card">
|
||||
<el-icon :size="28" color="#047857"><Plus /></el-icon>
|
||||
<span class="upload-card-text">上传附件</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
@@ -102,17 +110,57 @@
|
||||
v-for="(file, idx) in form.attachments"
|
||||
:key="file.id || idx"
|
||||
class="attachment-item"
|
||||
:class="{ 'is-uploading': file._uploading }"
|
||||
>
|
||||
<div class="attachment-name" :title="file.fileName">{{ file.fileName }}</div>
|
||||
<el-icon
|
||||
class="action-btn action-btn--danger"
|
||||
@click="form.attachments.splice(idx, 1)"
|
||||
<div
|
||||
class="attachment-preview"
|
||||
@click="
|
||||
isImageFile(file) && file.fileUrl ? handlePreviewAttachment(file) : undefined
|
||||
"
|
||||
>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
<template v-if="isImageFile(file) && file.fileUrl">
|
||||
<img :src="fullFileUrl(file.fileUrl)" :alt="file.fileName" />
|
||||
</template>
|
||||
<template v-else-if="isImageFile(file) && !file.fileUrl">
|
||||
<el-icon :size="24" color="#94a3b8"><Loading /></el-icon>
|
||||
</template>
|
||||
<template v-else-if="isVideoFile(file)">
|
||||
<div class="attachment-preview__icon attachment-preview__icon--video">
|
||||
<el-icon :size="28"><VideoPlay /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="attachment-preview__icon attachment-preview__icon--doc">
|
||||
<el-icon :size="28"><Document /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="attachment-footer">
|
||||
<span class="attachment-name" :title="file.fileName">{{
|
||||
file.fileName
|
||||
}}</span>
|
||||
<div class="attachment-actions">
|
||||
<el-icon
|
||||
class="action-btn"
|
||||
title="下载"
|
||||
@click="handleDownloadAttachment(file)"
|
||||
>
|
||||
<Download />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="action-btn action-btn--danger"
|
||||
title="删除"
|
||||
@click="form.attachments.splice(idx, 1)"
|
||||
>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="upload-hint">支持图片与常见文档,单文件不超过 20MB</span>
|
||||
<span class="upload-hint"
|
||||
>支持拖拽或点击上传图片、视频、PDF、Word、Excel等文件,单文件不超过 20MB</span
|
||||
>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
@@ -128,14 +176,42 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="previewVisible"
|
||||
title="文件预览"
|
||||
width="800px"
|
||||
align-center
|
||||
append-to-body
|
||||
modal-class="protocol-dialog-mask"
|
||||
>
|
||||
<div v-if="previewType === 'image'" class="preview-container">
|
||||
<img :src="previewUrl" alt="预览" style="max-width: 100%" />
|
||||
</div>
|
||||
<div v-else class="preview-container">
|
||||
<video :src="previewUrl" controls style="max-width: 100%" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { Plus, Edit, Delete, Close, Check } from '@element-plus/icons-vue'
|
||||
import { computed, nextTick, reactive, ref } from 'vue'
|
||||
import {
|
||||
Plus,
|
||||
Edit,
|
||||
Delete,
|
||||
Close,
|
||||
Check,
|
||||
Download,
|
||||
Document,
|
||||
VideoPlay,
|
||||
Loading
|
||||
} from '@element-plus/icons-vue'
|
||||
import AmountInput from '@/components/common/amount-input/index.vue'
|
||||
import ProtocolAttachmentReadonly from './ProtocolAttachmentReadonly.vue'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import {
|
||||
fetchProtocolRefundRecords,
|
||||
@@ -147,7 +223,7 @@
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
protocolId?: string
|
||||
/** 展示在标题中的协议标识,如母亲姓名 / 协议号 */
|
||||
/** 展示在标题中的协议标识,如母亲姓名 */
|
||||
protocolLabel?: string
|
||||
}>()
|
||||
|
||||
@@ -155,26 +231,114 @@
|
||||
'update:modelValue': [boolean]
|
||||
}>()
|
||||
|
||||
type RefundAttachmentItem = {
|
||||
id?: string
|
||||
fileName: string
|
||||
fileUrl: string
|
||||
fileSize?: number
|
||||
fileSizeDisplay?: string
|
||||
fileType?: string
|
||||
_uploading?: boolean
|
||||
}
|
||||
|
||||
const records = ref<Array<Record<string, any>>>([])
|
||||
const loading = ref(false)
|
||||
const formVisible = ref(false)
|
||||
const editingId = ref('')
|
||||
const saving = ref(false)
|
||||
const attachmentUploadRef = ref()
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const previewType = ref<'image' | 'video'>('image')
|
||||
const form = reactive({
|
||||
refundAmount: null as number | null,
|
||||
refundTime: '',
|
||||
refundRemark: '',
|
||||
attachments: [] as Array<Record<string, any>>
|
||||
attachments: [] as RefundAttachmentItem[]
|
||||
})
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
const label = String(props.protocolLabel || '').trim()
|
||||
return label ? `退费登记 · ${label}` : '退费登记'
|
||||
})
|
||||
|
||||
function fullFileUrl(suffix: string) {
|
||||
if (!suffix) return ''
|
||||
if (suffix.startsWith('http://') || suffix.startsWith('https://') || suffix.startsWith('/')) {
|
||||
return suffix
|
||||
}
|
||||
return `${minioBaseUrl}/${suffix}`
|
||||
}
|
||||
|
||||
function isImageFile(file: RefundAttachmentItem) {
|
||||
const url = (file.fileUrl || '').toLowerCase()
|
||||
const name = (file.fileName || '').toLowerCase()
|
||||
return (
|
||||
/\.(jpg|jpeg|png|gif|bmp|webp|svg)(\?|$)/.test(url) ||
|
||||
/\.(jpg|jpeg|png|gif|bmp|webp|svg)$/.test(name) ||
|
||||
(file.fileType || '').startsWith('image/')
|
||||
)
|
||||
}
|
||||
|
||||
function isVideoFile(file: RefundAttachmentItem) {
|
||||
const url = (file.fileUrl || '').toLowerCase()
|
||||
const name = (file.fileName || '').toLowerCase()
|
||||
return (
|
||||
/\.(mp4|webm|ogg|mov|avi)(\?|$)/.test(url) ||
|
||||
/\.(mp4|webm|ogg|mov|avi)$/.test(name) ||
|
||||
(file.fileType || '').startsWith('video/')
|
||||
)
|
||||
}
|
||||
|
||||
function rowAttachmentFiles(row: Record<string, any>): RefundAttachmentItem[] {
|
||||
if (Array.isArray(row.refundAttachmentFiles) && row.refundAttachmentFiles.length) {
|
||||
return row.refundAttachmentFiles.map((f: Record<string, any>, index: number) => ({
|
||||
id: f.id || `refund-att-${index}`,
|
||||
fileName: f.fileName || '附件',
|
||||
fileUrl: f.fileUrl || '',
|
||||
fileSize: f.fileSize,
|
||||
fileType: f.fileType
|
||||
}))
|
||||
}
|
||||
const raw = String(row.refundAttachments || '')
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
return raw.map((url, index) => {
|
||||
const name = url.includes('/') ? url.split('/').pop() || `附件${index + 1}` : url
|
||||
return {
|
||||
id: `refund-url-${index}`,
|
||||
fileName: decodeURIComponent(name),
|
||||
fileUrl: url
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handlePreviewAttachment(file: RefundAttachmentItem) {
|
||||
if (file._uploading || !file.fileUrl) return
|
||||
previewUrl.value = fullFileUrl(file.fileUrl)
|
||||
previewType.value = isVideoFile(file) ? 'video' : 'image'
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
function handleListPreview(file: Record<string, any>) {
|
||||
handlePreviewAttachment({
|
||||
id: file.id,
|
||||
fileName: file.fileName || '附件',
|
||||
fileUrl: file.fileUrl || '',
|
||||
fileType: file.fileType
|
||||
})
|
||||
}
|
||||
|
||||
function handleDownloadAttachment(file: RefundAttachmentItem | Record<string, any>) {
|
||||
downloadAttachmentFile(file, { fullFileUrl })
|
||||
}
|
||||
|
||||
function fmtMoney(v: unknown) {
|
||||
if (v == null || v === '') return '—'
|
||||
return formatMoneyDisplay(v)
|
||||
return formatMoneyDisplay(v as string | number)
|
||||
}
|
||||
|
||||
async function loadRecords() {
|
||||
@@ -203,7 +367,13 @@
|
||||
form.refundTime = String(row.refundTime || '')
|
||||
form.refundRemark = String(row.refundRemark || '')
|
||||
form.attachments = Array.isArray(row.refundAttachmentFiles)
|
||||
? [...row.refundAttachmentFiles]
|
||||
? row.refundAttachmentFiles.map((f: Record<string, any>) => ({
|
||||
id: f.id,
|
||||
fileName: f.fileName || '附件',
|
||||
fileUrl: f.fileUrl || '',
|
||||
fileSize: f.fileSize,
|
||||
fileType: f.fileType
|
||||
}))
|
||||
: []
|
||||
formVisible.value = true
|
||||
}
|
||||
@@ -223,24 +393,47 @@
|
||||
}
|
||||
|
||||
async function onFileChange(uploadFile: any) {
|
||||
const raw = uploadFile?.raw as File | undefined
|
||||
const raw = (uploadFile?.raw || uploadFile) as File | undefined
|
||||
if (!raw) return
|
||||
if (raw.size > 20 * 1024 * 1024) {
|
||||
ElMessage.warning('单文件不超过 20MB')
|
||||
return
|
||||
}
|
||||
const fd = new FormData()
|
||||
fd.append('file', raw)
|
||||
fd.append('folder', 'protocol-refund-record')
|
||||
const res: any = await fetchUploadAttachment(fd)
|
||||
const data = res?.data || res
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
||||
form.attachments.push({
|
||||
id: data?.id,
|
||||
fileName: data?.fileName || raw.name,
|
||||
fileUrl: data?.fileUrl || data?.url,
|
||||
fileSize: data?.fileSize,
|
||||
fileType: data?.fileType
|
||||
id: tempId,
|
||||
fileName: raw.name,
|
||||
fileUrl: '',
|
||||
fileSize: raw.size,
|
||||
fileSizeDisplay: `${(raw.size / 1024).toFixed(1)} KB`,
|
||||
fileType: raw.type || '',
|
||||
_uploading: true
|
||||
})
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('file', raw)
|
||||
fd.append('folder', 'protocol-refund-record')
|
||||
const res: any = await fetchUploadAttachment(fd)
|
||||
const data = res?.data || res
|
||||
const idx = form.attachments.findIndex((f) => f.id === tempId)
|
||||
if (idx < 0) return
|
||||
form.attachments[idx] = {
|
||||
...form.attachments[idx],
|
||||
id: data?.id || tempId,
|
||||
fileName: raw.name || data?.fileName,
|
||||
fileUrl: data?.fileUrl || data?.url || '',
|
||||
fileSize: data?.fileSize || raw.size,
|
||||
fileSizeDisplay: data?.fileSizeDisplay || `${(raw.size / 1024).toFixed(1)} KB`,
|
||||
fileType: data?.fileType || raw.type || '',
|
||||
_uploading: false
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error('文件上传失败')
|
||||
const idx = form.attachments.findIndex((f) => f.id === tempId)
|
||||
if (idx >= 0) form.attachments.splice(idx, 1)
|
||||
} finally {
|
||||
nextTick(() => attachmentUploadRef.value?.clearFiles?.())
|
||||
}
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
@@ -249,6 +442,10 @@
|
||||
ElMessage.warning('请填写退费金额')
|
||||
return
|
||||
}
|
||||
if (form.attachments.some((a) => a._uploading)) {
|
||||
ElMessage.warning('附件正在上传,请稍后再保存')
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
const urls = form.attachments
|
||||
@@ -300,60 +497,175 @@
|
||||
color: var(--mint-text-muted, #94a3b8);
|
||||
}
|
||||
|
||||
.muted-dash {
|
||||
color: var(--mint-text-muted, #94a3b8);
|
||||
}
|
||||
|
||||
.attachment-upload {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.attachment-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.upload-card--sm {
|
||||
.upload-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 140px;
|
||||
height: 96px;
|
||||
cursor: pointer;
|
||||
border: 1px dashed #a7f3d0;
|
||||
background: #f0fdf4;
|
||||
border: 1px dashed #a8d4c4;
|
||||
border-radius: 8px;
|
||||
background: #ecfdf5;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
background 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #e8f4ee;
|
||||
border-color: #047857;
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-card-text {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
color: #047857;
|
||||
}
|
||||
|
||||
.attachment-item {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
min-width: 140px;
|
||||
max-width: 220px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.attachment-name {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.action-btn--danger {
|
||||
color: #ef4444;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
margin-top: 8px;
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
color: var(--mint-text-muted, #334155);
|
||||
}
|
||||
|
||||
.attachment-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 140px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
box-shadow 0.15s;
|
||||
|
||||
&:hover {
|
||||
border-color: #047857;
|
||||
box-shadow: 0 2px 8px rgba(4, 120, 87, 0.1);
|
||||
}
|
||||
|
||||
&.is-uploading {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 96px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
background: #f8fafc;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-preview__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 10px;
|
||||
|
||||
&--video {
|
||||
color: #7c3aed;
|
||||
background: #ede9fe;
|
||||
}
|
||||
|
||||
&--doc {
|
||||
color: #0284c7;
|
||||
background: #e0f2fe;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-footer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.attachment-name {
|
||||
display: -webkit-box;
|
||||
min-height: calc(11px * 1.4 * 2);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
color: #334155;
|
||||
word-break: break-all;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.attachment-actions {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
font-size: 12px;
|
||||
color: var(--mint-text-muted, #94a3b8);
|
||||
color: var(--mint-text-muted, #334155);
|
||||
cursor: pointer;
|
||||
background: #f1f5f9;
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
background 0.15s,
|
||||
color 0.15s;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background: #047857;
|
||||
}
|
||||
|
||||
&--danger:hover {
|
||||
color: #fff;
|
||||
background: #ef4444;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -52,6 +52,10 @@
|
||||
></div
|
||||
></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">母亲姓名</div
|
||||
><div class="data-value">{{ basicMotherNameDisplay }}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">登记日期</div
|
||||
><div class="data-value">{{ formatYmd(protocol?.registerDate) }}</div></div
|
||||
@@ -786,6 +790,23 @@
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="附件"
|
||||
min-width="280"
|
||||
align="left"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<ProtocolAttachmentReadonly
|
||||
v-if="refundRecordAttachments(row).length"
|
||||
:files="refundRecordAttachments(row)"
|
||||
size="compact"
|
||||
@preview="openPreview"
|
||||
@download="downloadFile"
|
||||
/>
|
||||
<span v-else class="muted-dash">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-else description="暂无退费登记" :image-size="40" />
|
||||
</template>
|
||||
@@ -1099,62 +1120,47 @@
|
||||
<!-- 状态变更记录(置底) -->
|
||||
<div id="view-section-status-change" class="section-card section-card--white">
|
||||
<div class="section-title section-title--banner"><h3>状态变更记录</h3></div>
|
||||
<el-empty v-if="!hasAnyStatusChange" description="暂无状态变更记录" :image-size="64" />
|
||||
<div v-else class="data-grid data-grid--4">
|
||||
<template v-if="protocol?.effectiveTime">
|
||||
<div class="data-item"
|
||||
><div class="data-label">协议生效时间</div
|
||||
><div class="data-value">{{ dash(protocol.effectiveTime) }}</div></div
|
||||
<div class="list-body list-wrap view-inner-table-wrap">
|
||||
<el-table
|
||||
v-loading="statusChangeLoading"
|
||||
class="list-table inner-table"
|
||||
:data="statusChangeRecords"
|
||||
stripe
|
||||
empty-text="暂无状态变更记录"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column label="变更类型" min-width="110" align="center" header-align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusChangeTagType(String(row.changeType || ''))" size="small">
|
||||
{{ statusChangeTypeLabel(String(row.changeType || '')) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变更时间" min-width="170" align="center" header-align="center">
|
||||
<template #default="{ row }">{{ dash(row.changeTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态流转" min-width="160" align="center" header-align="center">
|
||||
<template #default="{ row }">{{ statusChangeFlow(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="变更说明"
|
||||
min-width="200"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">协议生效描述</div
|
||||
><div class="data-value data-value--muted">{{
|
||||
dash(protocol.effectiveDesc)
|
||||
}}</div></div
|
||||
>
|
||||
</template>
|
||||
<template v-if="protocol?.expireTime">
|
||||
<div class="data-item"
|
||||
><div class="data-label">协议过期时间</div
|
||||
><div class="data-value">{{ dash(protocol.expireTime) }}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">协议过期描述</div
|
||||
><div class="data-value data-value--muted">{{
|
||||
dash(protocol.expireDesc)
|
||||
}}</div></div
|
||||
>
|
||||
</template>
|
||||
<template v-if="protocol?.voidTime">
|
||||
<div class="data-item"
|
||||
><div class="data-label">作废类型</div
|
||||
><div class="data-value">{{
|
||||
voidTypeLabel(String(protocol?.voidType || '')) || '—'
|
||||
}}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">协议作废时间</div
|
||||
><div class="data-value">{{ dash(protocol.voidTime) }}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">协议作废描述</div
|
||||
><div class="data-value data-value--muted">{{
|
||||
dash(protocol.voidDesc)
|
||||
}}</div></div
|
||||
>
|
||||
</template>
|
||||
<template v-if="protocol?.toResearchTime">
|
||||
<div class="data-item"
|
||||
><div class="data-label">协议转科研时间</div
|
||||
><div class="data-value">{{ dash(protocol.toResearchTime) }}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">协议转科研描述</div
|
||||
><div class="data-value data-value--muted">{{
|
||||
dash(protocol.toResearchDesc)
|
||||
}}</div></div
|
||||
>
|
||||
</template>
|
||||
<template #default="{ row }">{{ statusChangeDesc(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人" min-width="120" align="center" header-align="center">
|
||||
<template #default="{ row }">{{ dash(row.operatorUserName) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1208,6 +1214,7 @@
|
||||
import {
|
||||
fetchCollectionProtocolAuditList,
|
||||
fetchCollectionProtocolDetail,
|
||||
fetchCollectionProtocolStatusChanges,
|
||||
type CollectionProtocolRow
|
||||
} from '@/api/collection-protocol'
|
||||
import {
|
||||
@@ -1305,8 +1312,52 @@
|
||||
const detailLoading = ref(false)
|
||||
const lookupLoading = ref(false)
|
||||
const auditLoading = ref(false)
|
||||
const statusChangeLoading = ref(false)
|
||||
const bundle = ref<Record<string, any> | null>(null)
|
||||
const auditRecords = ref<Array<Record<string, unknown>>>([])
|
||||
const statusChangeRecords = ref<Array<Record<string, unknown>>>([])
|
||||
|
||||
const STATUS_CHANGE_TYPE_LABEL: Record<string, string> = {
|
||||
activate: '生效',
|
||||
expire: '过期',
|
||||
void: '作废',
|
||||
to_research: '转科研',
|
||||
to_storage: '转细胞存储'
|
||||
}
|
||||
const STATUS_CHANGE_TAG: Record<string, 'success' | 'warning' | 'danger' | 'primary' | 'info'> = {
|
||||
activate: 'success',
|
||||
expire: 'warning',
|
||||
void: 'danger',
|
||||
to_research: 'primary',
|
||||
to_storage: 'primary'
|
||||
}
|
||||
|
||||
function statusChangeTypeLabel(changeType: string) {
|
||||
return STATUS_CHANGE_TYPE_LABEL[changeType] || changeType || '—'
|
||||
}
|
||||
|
||||
function statusChangeTagType(changeType: string) {
|
||||
return STATUS_CHANGE_TAG[changeType] || 'info'
|
||||
}
|
||||
|
||||
function statusChangeFlow(row: Record<string, unknown>) {
|
||||
const from = String(row.fromStatus || '')
|
||||
const to = String(row.toStatus || '')
|
||||
const voidType = String(row.voidType || '')
|
||||
const toLabel = protocolStatusDisplayLabel(to, to === 'yzf' ? voidType : '')
|
||||
if (!from) return toLabel || '—'
|
||||
return `${protocolStatusDisplayLabel(from)} → ${toLabel}`
|
||||
}
|
||||
|
||||
function statusChangeDesc(row: Record<string, unknown>) {
|
||||
const desc = String(row.changeDesc || '').trim()
|
||||
if (String(row.changeType || '') === 'void') {
|
||||
const vt = voidTypeLabel(String(row.voidType || ''))
|
||||
if (vt && desc) return `作废类型:${vt};${desc}`
|
||||
if (vt) return `作废类型:${vt}`
|
||||
}
|
||||
return desc || '—'
|
||||
}
|
||||
const activeTab = ref('basic')
|
||||
|
||||
const sampleTypeOptions = ref<Array<{ id: string; typeName: string }>>([])
|
||||
@@ -1520,25 +1571,21 @@
|
||||
/** 金额只读展示:千分位 + ¥ 前缀,null 显示 — */
|
||||
function fmtMoney(v: unknown): string {
|
||||
if (v == null || v === '') return '—'
|
||||
return '¥' + formatMoneyDisplay(v)
|
||||
return '¥' + formatMoneyDisplay(v as string | number)
|
||||
}
|
||||
|
||||
const basicMotherNameDisplay = computed(() =>
|
||||
dash(
|
||||
partyCustomer.value?.motherName || props.listRow?.motherName || protocol.value?.motherName
|
||||
)
|
||||
)
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
const name =
|
||||
partyCustomer.value?.motherName || props.listRow?.motherName || protocol.value?.motherName
|
||||
return `查看 — ${formatProtocolDocSubject('cell_storage', { motherName: name })}`
|
||||
})
|
||||
|
||||
const hasAnyStatusChange = computed(() => {
|
||||
if (!protocol.value) return false
|
||||
return !!(
|
||||
protocol.value.effectiveTime ||
|
||||
protocol.value.expireTime ||
|
||||
protocol.value.voidTime ||
|
||||
protocol.value.toResearchTime
|
||||
)
|
||||
})
|
||||
|
||||
const collectorDisplay = computed(() => {
|
||||
const fromList = props.listRow?.collectorUserName
|
||||
if (fromList) return String(fromList)
|
||||
@@ -1892,6 +1939,13 @@
|
||||
return urlsToAttachmentFiles(String(detail.financeConfirmAttachments || ''))
|
||||
}
|
||||
|
||||
function refundRecordAttachments(row: Record<string, any>) {
|
||||
if (Array.isArray(row.refundAttachmentFiles) && row.refundAttachmentFiles.length) {
|
||||
return row.refundAttachmentFiles
|
||||
}
|
||||
return urlsToAttachmentFiles(String(row.refundAttachments || ''))
|
||||
}
|
||||
|
||||
function refundAttachmentFiles(detail: Record<string, any>) {
|
||||
if (Array.isArray(detail.refundAttachmentFiles)) {
|
||||
return detail.refundAttachmentFiles
|
||||
@@ -2007,6 +2061,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadStatusChangeRecords() {
|
||||
if (!props.protocolId) {
|
||||
statusChangeRecords.value = []
|
||||
return
|
||||
}
|
||||
statusChangeLoading.value = true
|
||||
try {
|
||||
const res = await fetchCollectionProtocolStatusChanges(props.protocolId)
|
||||
statusChangeRecords.value = res.list || []
|
||||
} catch {
|
||||
statusChangeRecords.value = []
|
||||
} finally {
|
||||
statusChangeLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAuditRecords() {
|
||||
if (!props.showAuditHistory || !props.protocolId) {
|
||||
auditRecords.value = []
|
||||
@@ -2033,9 +2103,11 @@
|
||||
lookupLoading.value = true
|
||||
bundle.value = null
|
||||
auditRecords.value = []
|
||||
statusChangeRecords.value = []
|
||||
|
||||
const baseLookupsPromise = loadBaseLookups().catch(() => {})
|
||||
const auditPromise = props.showAuditHistory ? loadAuditRecords() : null
|
||||
const statusChangePromise = loadStatusChangeRecords()
|
||||
|
||||
let data: Record<string, any> | null = null
|
||||
try {
|
||||
@@ -2052,6 +2124,7 @@
|
||||
|
||||
void finishLookups(data, baseLookupsPromise)
|
||||
void auditPromise
|
||||
void statusChangePromise
|
||||
if (props.highlightSampleCode) {
|
||||
nextTick(() => {
|
||||
document
|
||||
@@ -2074,10 +2147,12 @@
|
||||
function handleClosed() {
|
||||
bundle.value = null
|
||||
auditRecords.value = []
|
||||
statusChangeRecords.value = []
|
||||
activeTab.value = 'basic'
|
||||
detailLoading.value = false
|
||||
lookupLoading.value = false
|
||||
auditLoading.value = false
|
||||
statusChangeLoading.value = false
|
||||
}
|
||||
|
||||
watch(
|
||||
|
||||
@@ -444,7 +444,7 @@
|
||||
|
||||
function fmtMoney(v: unknown): string {
|
||||
if (v == null || v === '') return '—'
|
||||
return '¥' + formatMoneyDisplay(v)
|
||||
return '¥' + formatMoneyDisplay(v as string | number)
|
||||
}
|
||||
|
||||
function samplePayKey(pay: Record<string, any>, pIdx: number): string {
|
||||
@@ -896,10 +896,10 @@
|
||||
tr.payStatus = data.payStatus || tr.payStatus || 'dfj'
|
||||
tr.scheduleId = data.scheduleId || tr.scheduleId
|
||||
tr.samplePaymentId = data.samplePaymentId || tr.samplePaymentId
|
||||
if (Array.isArray(data.attachments) && data.attachments.length) {
|
||||
if (Array.isArray(data.attachments)) {
|
||||
tr.attachments = data.attachments.map((a: Record<string, any>) => ({ ...a }))
|
||||
}
|
||||
if (data.transferAttachments) tr.transferAttachments = data.transferAttachments
|
||||
if (data.transferAttachments != null) tr.transferAttachments = data.transferAttachments
|
||||
emitChange()
|
||||
}
|
||||
|
||||
@@ -922,8 +922,8 @@
|
||||
const payload = {
|
||||
transferType: tr.transferType,
|
||||
transferAmount: tr.transferAmount,
|
||||
transferTime: tr.transferTime || undefined,
|
||||
transferAttachments: attachmentsToUrls(tr) || undefined,
|
||||
transferTime: tr.transferTime || '',
|
||||
transferAttachments: attachmentsToUrls(tr),
|
||||
remark: tr.remark
|
||||
}
|
||||
if (tr.id) {
|
||||
@@ -965,13 +965,14 @@
|
||||
_uploading: true
|
||||
})
|
||||
emitChange()
|
||||
const idx = tr.attachments.length - 1
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('folder', 'protocol-transfer')
|
||||
fd.append('file', file)
|
||||
const res: any = await fetchUploadAttachment(fd)
|
||||
const data = res?.data || res
|
||||
const idx = tr.attachments.findIndex((f: TransferAttachment) => f.id === tempId)
|
||||
if (idx < 0) return
|
||||
tr.attachments[idx] = {
|
||||
id: data?.id || tempId,
|
||||
fileName: data?.fileName || file.name,
|
||||
@@ -981,7 +982,8 @@
|
||||
emitChange()
|
||||
} catch {
|
||||
ElMessage.error('文件上传失败')
|
||||
tr.attachments.splice(idx, 1)
|
||||
const idx = tr.attachments.findIndex((f: TransferAttachment) => f.id === tempId)
|
||||
if (idx >= 0) tr.attachments.splice(idx, 1)
|
||||
emitChange()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@
|
||||
}
|
||||
|
||||
function handleEdit(row: CollectionProtocolRow) {
|
||||
formDialogEditId.value = row.id
|
||||
formDialogEditId.value = String(row.id || '')
|
||||
formDialogRow.value = row
|
||||
formDialogVisible.value = true
|
||||
}
|
||||
@@ -420,9 +420,7 @@
|
||||
}
|
||||
const row = selectedRows.value[0]
|
||||
refundProtocolId.value = String(row.id || '')
|
||||
const name = String(row.motherName || '').trim()
|
||||
const no = String(row.protocolNo || '').trim()
|
||||
refundProtocolLabel.value = [name, no].filter(Boolean).join(' / ')
|
||||
refundProtocolLabel.value = String(row.motherName || '').trim()
|
||||
refundDialogVisible.value = true
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,9 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="审批人去重">
|
||||
<el-switch v-model="advanced.approverDedup" />
|
||||
<div class="oa-designer__hint">
|
||||
开启后,同一张审批单里某人已经同意过,后续节点再出现该人时系统自动通过,不再生成待办。或签整节点通过,会签仅完成本人;仅「已同意」会计入,拒绝不触发。默认关闭。
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
@@ -33,44 +33,6 @@
|
||||
|
||||
<template v-if="bundle">
|
||||
<div>
|
||||
<!-- KPI 摘要条 -->
|
||||
<div class="kpi-strip">
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-card__icon" aria-hidden="true">
|
||||
<el-icon><CircleCheck /></el-icon>
|
||||
</div>
|
||||
<div class="kpi-card__main">
|
||||
<div class="kpi-label">状态</div>
|
||||
<div class="kpi-value">
|
||||
<el-tag
|
||||
:type="donationStatusTagType(String(donation?.donationStatus || ''))"
|
||||
size="small"
|
||||
>
|
||||
{{ donationStatusLabel(String(donation?.donationStatus || '')) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-card__icon" aria-hidden="true">
|
||||
<el-icon><User /></el-icon>
|
||||
</div>
|
||||
<div class="kpi-card__main">
|
||||
<div class="kpi-label">母亲姓名</div>
|
||||
<div class="kpi-value">{{ dash(party?.motherName) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-card__icon" aria-hidden="true">
|
||||
<el-icon><Calendar /></el-icon>
|
||||
</div>
|
||||
<div class="kpi-card__main">
|
||||
<div class="kpi-label">捐赠日期</div>
|
||||
<div class="kpi-value">{{ formatYmd(donation?.donationDate) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<div id="view-section-basic" class="section-card section-card--white">
|
||||
<div class="section-title section-title--banner"><h3>基本信息</h3></div>
|
||||
@@ -85,6 +47,10 @@
|
||||
></div
|
||||
></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">母亲姓名</div
|
||||
><div class="data-value">{{ dash(party?.motherName) }}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">捐赠日期</div
|
||||
><div class="data-value">{{ formatYmd(donation?.donationDate) }}</div></div
|
||||
@@ -103,62 +69,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 状态变更记录(独立区域) -->
|
||||
<div id="view-section-status-change" class="section-card section-card--white">
|
||||
<div class="section-title"><h3>状态变更记录</h3></div>
|
||||
<el-empty v-if="!hasAnyStatusChange" description="暂无状态变更记录" :image-size="64" />
|
||||
<div v-else class="data-grid data-grid--4">
|
||||
<template v-if="donation?.effectiveTime">
|
||||
<div class="data-item"
|
||||
><div class="data-label">生效时间</div
|
||||
><div class="data-value">{{ dash(donation.effectiveTime) }}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">生效描述</div
|
||||
><div class="data-value data-value--muted">{{
|
||||
dash(donation.effectiveDesc)
|
||||
}}</div></div
|
||||
>
|
||||
</template>
|
||||
<template v-if="donation?.expireTime">
|
||||
<div class="data-item"
|
||||
><div class="data-label">过期时间</div
|
||||
><div class="data-value">{{ dash(donation.expireTime) }}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">过期描述</div
|
||||
><div class="data-value data-value--muted">{{
|
||||
dash(donation.expireDesc)
|
||||
}}</div></div
|
||||
>
|
||||
</template>
|
||||
<template v-if="donation?.voidTime">
|
||||
<div class="data-item"
|
||||
><div class="data-label">作废时间</div
|
||||
><div class="data-value">{{ dash(donation.voidTime) }}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">作废描述</div
|
||||
><div class="data-value data-value--muted">{{
|
||||
dash(donation.voidDesc)
|
||||
}}</div></div
|
||||
>
|
||||
</template>
|
||||
<template v-if="donation?.convertTime">
|
||||
<div class="data-item"
|
||||
><div class="data-label">转细胞存储时间</div
|
||||
><div class="data-value">{{ dash(donation.convertTime) }}</div></div
|
||||
>
|
||||
<div class="data-item"
|
||||
><div class="data-label">转细胞存储描述</div
|
||||
><div class="data-value data-value--muted">{{
|
||||
dash(donation.convertDesc)
|
||||
}}</div></div
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 采集信息 -->
|
||||
<div id="view-section-collection" class="section-card section-card--white">
|
||||
<div class="section-title section-title--banner"><h3>采集信息</h3></div>
|
||||
@@ -219,7 +129,7 @@
|
||||
</div>
|
||||
<div class="sample-card__body">
|
||||
<div class="sample-card__section">
|
||||
<div class="sample-card__section-label">样本信息</div>
|
||||
<div class="sample-card__section-label">样本及运输</div>
|
||||
<div class="data-grid data-grid--4">
|
||||
<div class="data-item">
|
||||
<div class="data-label">样本编码</div>
|
||||
@@ -232,26 +142,6 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">存储年限(年)</div>
|
||||
<div class="data-value">{{ dash(row.storageYears) }}</div>
|
||||
</div>
|
||||
<div v-if="row.remark" class="data-item data-item--full">
|
||||
<div class="data-label">备注</div>
|
||||
<div class="data-value data-value--muted">{{ dash(row.remark) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
row.freight?.id ||
|
||||
row.freight?.transportMode ||
|
||||
row.transport?.etaTime
|
||||
"
|
||||
class="sample-card__section sample-card__section--transport"
|
||||
>
|
||||
<div class="sample-card__section-label">运输与运费</div>
|
||||
<div class="data-grid data-grid--4">
|
||||
<div class="data-item">
|
||||
<div class="data-label">运输方式</div>
|
||||
<div class="data-value">{{
|
||||
@@ -274,6 +164,10 @@
|
||||
<div class="data-label">预计到达</div>
|
||||
<div class="data-value">{{ dash(row.transport?.etaTime) }}</div>
|
||||
</div>
|
||||
<div v-if="row.remark" class="data-item data-item--full">
|
||||
<div class="data-label">备注</div>
|
||||
<div class="data-value data-value--muted">{{ dash(row.remark) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sample-card__section sample-card__section--reception">
|
||||
@@ -530,6 +424,53 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 状态变更记录(附件信息下方) -->
|
||||
<div id="view-section-status-change" class="section-card section-card--white">
|
||||
<div class="section-title section-title--banner"><h3>状态变更记录</h3></div>
|
||||
<div class="list-body list-wrap view-inner-table-wrap">
|
||||
<el-table
|
||||
v-loading="statusChangeLoading"
|
||||
class="list-table inner-table"
|
||||
:data="statusChangeRecords"
|
||||
stripe
|
||||
empty-text="暂无状态变更记录"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column label="变更类型" min-width="110" align="center" header-align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusChangeTagType(String(row.changeType || ''))" size="small">
|
||||
{{ statusChangeTypeLabel(String(row.changeType || '')) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变更时间" min-width="170" align="center" header-align="center">
|
||||
<template #default="{ row }">{{ dash(row.changeTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态流转" min-width="160" align="center" header-align="center">
|
||||
<template #default="{ row }">{{ statusChangeFlow(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="变更说明"
|
||||
min-width="200"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">{{ statusChangeDesc(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人" min-width="120" align="center" header-align="center">
|
||||
<template #default="{ row }">{{ dash(row.operatorUserName) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -571,7 +512,7 @@
|
||||
import { formatDisplayOrDash } from '@/utils/datetime-display'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import { Calendar, CircleCheck, Close, User } from '@element-plus/icons-vue'
|
||||
import { Close } from '@element-plus/icons-vue'
|
||||
import ProtocolAttachmentReadonly from '@/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue'
|
||||
import SampleQcInfoGroupedPanel from '@/components/business/sample-qc-info-grouped-panel/index.vue'
|
||||
import SampleReceptionDualReadonlyPanel from '@/components/business/sample-reception-dual-readonly-panel/index.vue'
|
||||
@@ -579,6 +520,7 @@
|
||||
import SampleProductionInfoCards from '@/components/business/sample-production-info-cards/index.vue'
|
||||
import SampleStorageReleaseInfo from '@/components/business/sample-storage-release-info/index.vue'
|
||||
import {
|
||||
fetchDonationStatusChanges,
|
||||
fetchResearchDonationAuditList,
|
||||
fetchResearchDonationDetail,
|
||||
type ResearchDonationRow
|
||||
@@ -594,6 +536,7 @@
|
||||
} from '@/api/admin'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
import {
|
||||
donationStatusDisplayLabel,
|
||||
donationStatusLabel,
|
||||
donationStatusTagType,
|
||||
auditStatusLabel,
|
||||
@@ -606,6 +549,7 @@
|
||||
acceptConclusionLabel,
|
||||
acceptConclusionTagType
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { loadVoidTypeDict, voidTypeLabel } from '@/utils/void-type-dict'
|
||||
|
||||
defineOptions({ name: 'DonationViewDialog' })
|
||||
|
||||
@@ -627,7 +571,6 @@
|
||||
|
||||
const baseFormTabs = [
|
||||
{ key: 'basic', label: '基本信息' },
|
||||
{ key: 'status-change', label: '状态变更记录' },
|
||||
{ key: 'collection', label: '采集信息' },
|
||||
{ key: 'sample', label: '样本及运输信息' },
|
||||
{ key: 'party', label: '客户信息' }
|
||||
@@ -639,13 +582,58 @@
|
||||
tabs.push({ key: 'audit', label: '审核记录' })
|
||||
}
|
||||
tabs.push({ key: 'attachment', label: '附件信息' })
|
||||
tabs.push({ key: 'status-change', label: '状态变更记录' })
|
||||
return tabs
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const auditLoading = ref(false)
|
||||
const statusChangeLoading = ref(false)
|
||||
const bundle = ref<Record<string, any> | null>(null)
|
||||
const auditRecords = ref<Array<Record<string, unknown>>>([])
|
||||
const statusChangeRecords = ref<Array<Record<string, unknown>>>([])
|
||||
|
||||
const STATUS_CHANGE_TYPE_LABEL: Record<string, string> = {
|
||||
activate: '生效',
|
||||
expire: '过期',
|
||||
void: '作废',
|
||||
to_research: '转科研',
|
||||
to_storage: '转细胞存储'
|
||||
}
|
||||
const STATUS_CHANGE_TAG: Record<string, 'success' | 'warning' | 'danger' | 'primary' | 'info'> = {
|
||||
activate: 'success',
|
||||
expire: 'warning',
|
||||
void: 'danger',
|
||||
to_research: 'primary',
|
||||
to_storage: 'primary'
|
||||
}
|
||||
|
||||
function statusChangeTypeLabel(changeType: string) {
|
||||
return STATUS_CHANGE_TYPE_LABEL[changeType] || changeType || '—'
|
||||
}
|
||||
|
||||
function statusChangeTagType(changeType: string) {
|
||||
return STATUS_CHANGE_TAG[changeType] || 'info'
|
||||
}
|
||||
|
||||
function statusChangeFlow(row: Record<string, unknown>) {
|
||||
const from = String(row.fromStatus || '')
|
||||
const to = String(row.toStatus || '')
|
||||
const voidType = String(row.voidType || '')
|
||||
const toLabel = donationStatusDisplayLabel(to, to === 'yzf' ? voidType : '')
|
||||
if (!from) return toLabel || '—'
|
||||
return `${donationStatusDisplayLabel(from)} → ${toLabel}`
|
||||
}
|
||||
|
||||
function statusChangeDesc(row: Record<string, unknown>) {
|
||||
const desc = String(row.changeDesc || '').trim()
|
||||
if (String(row.changeType || '') === 'void') {
|
||||
const vt = voidTypeLabel(String(row.voidType || ''))
|
||||
if (vt && desc) return `作废类型:${vt};${desc}`
|
||||
if (vt) return `作废类型:${vt}`
|
||||
}
|
||||
return desc || '—'
|
||||
}
|
||||
const activeTab = ref('basic')
|
||||
|
||||
const sampleTypeOptions = ref<Array<{ id: string; typeName: string }>>([])
|
||||
@@ -707,16 +695,6 @@
|
||||
|
||||
const dialogTitle = computed(() => '查看捐赠协议单')
|
||||
|
||||
const hasAnyStatusChange = computed(() => {
|
||||
if (!donation.value) return false
|
||||
return !!(
|
||||
donation.value.effectiveTime ||
|
||||
donation.value.expireTime ||
|
||||
donation.value.voidTime ||
|
||||
donation.value.convertTime
|
||||
)
|
||||
})
|
||||
|
||||
const collectorDisplay = computed(() => {
|
||||
const fromList = props.listRow?.collectorUserName
|
||||
if (fromList) return String(fromList)
|
||||
@@ -954,6 +932,22 @@
|
||||
await Promise.all(tasks)
|
||||
}
|
||||
|
||||
async function loadStatusChangeRecords() {
|
||||
if (!props.donationId) {
|
||||
statusChangeRecords.value = []
|
||||
return
|
||||
}
|
||||
statusChangeLoading.value = true
|
||||
try {
|
||||
const res = await fetchDonationStatusChanges(props.donationId)
|
||||
statusChangeRecords.value = res.list || []
|
||||
} catch {
|
||||
statusChangeRecords.value = []
|
||||
} finally {
|
||||
statusChangeLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAuditRecords() {
|
||||
if (!props.showAuditHistory || !props.donationId) {
|
||||
auditRecords.value = []
|
||||
@@ -979,11 +973,13 @@
|
||||
loading.value = true
|
||||
bundle.value = null
|
||||
auditRecords.value = []
|
||||
statusChangeRecords.value = []
|
||||
void loadVoidTypeDict()
|
||||
try {
|
||||
const data = await fetchResearchDonationDetail(props.donationId)
|
||||
bundle.value = data as Record<string, any>
|
||||
await loadLookups(data as Record<string, any>)
|
||||
await loadAuditRecords()
|
||||
await Promise.all([loadAuditRecords(), loadStatusChangeRecords()])
|
||||
if (props.highlightSampleCode) {
|
||||
nextTick(() => {
|
||||
document
|
||||
@@ -1009,9 +1005,11 @@
|
||||
function handleClosed() {
|
||||
bundle.value = null
|
||||
auditRecords.value = []
|
||||
statusChangeRecords.value = []
|
||||
activeTab.value = 'basic'
|
||||
loading.value = false
|
||||
auditLoading.value = false
|
||||
statusChangeLoading.value = false
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -1044,75 +1042,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- KPI 摘要条(与渠道/申请单查看弹窗一致:浅绿渐变通栏) ---- */
|
||||
.kpi-strip {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0;
|
||||
margin-bottom: 24px;
|
||||
padding: 18px 20px;
|
||||
background: linear-gradient(180deg, #ecfdf5 0%, #fff 100%);
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.kpi-card {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 8px 16px;
|
||||
background: transparent;
|
||||
border-radius: 8px;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.kpi-card__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
border: none;
|
||||
color: #065f46;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.kpi-card__main {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.kpi-label {
|
||||
margin-bottom: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--mint-text-muted, #334155);
|
||||
}
|
||||
|
||||
.kpi-value {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #0f172a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
&--emphasis {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #047857;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Section Card ---- */
|
||||
.section-card {
|
||||
border-radius: 12px;
|
||||
@@ -1504,37 +1433,12 @@
|
||||
|
||||
/* ---- Responsive ---- */
|
||||
@media (max-width: 1200px) {
|
||||
.kpi-strip {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.kpi-strip .kpi-card {
|
||||
border-right: none !important;
|
||||
border-bottom: 1px solid rgba(168, 212, 196, 0.75);
|
||||
|
||||
&:nth-last-child(-n + 2) {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.client-sub-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.kpi-strip {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.kpi-strip .kpi-card:nth-last-child(-n + 2) {
|
||||
border-bottom: 1px solid rgba(168, 212, 196, 0.75);
|
||||
}
|
||||
|
||||
.kpi-strip .kpi-card:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.client-sub-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user