提交
This commit is contained in:
1 parent
ad8f8d696f
commit
41aa90aa05
115 files changed
+29626
-10210
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-05-26)
|
||||
# Graph Report - src (2026-05-27)
|
||||
|
||||
## Corpus Check
|
||||
- Large corpus: 632 files · ~390,279 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
- Large corpus: 641 files · ~402,707 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
|
||||
## Summary
|
||||
- 1816 nodes · 2360 edges · 37 communities detected
|
||||
- Extraction: 77% EXTRACTED · 23% INFERRED · 0% AMBIGUOUS · INFERRED: 538 edges (avg confidence: 0.8)
|
||||
- 1844 nodes · 2394 edges · 41 communities detected
|
||||
- Extraction: 77% EXTRACTED · 23% INFERRED · 0% AMBIGUOUS · INFERRED: 550 edges (avg confidence: 0.8)
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
@@ -33,39 +33,43 @@
|
||||
- [[_COMMUNITY_Community 21|Community 21]]
|
||||
- [[_COMMUNITY_Community 22|Community 22]]
|
||||
- [[_COMMUNITY_Community 23|Community 23]]
|
||||
- [[_COMMUNITY_Community 24|Community 24]]
|
||||
- [[_COMMUNITY_Community 25|Community 25]]
|
||||
- [[_COMMUNITY_Community 26|Community 26]]
|
||||
- [[_COMMUNITY_Community 27|Community 27]]
|
||||
- [[_COMMUNITY_Community 29|Community 29]]
|
||||
- [[_COMMUNITY_Community 30|Community 30]]
|
||||
- [[_COMMUNITY_Community 31|Community 31]]
|
||||
- [[_COMMUNITY_Community 32|Community 32]]
|
||||
- [[_COMMUNITY_Community 33|Community 33]]
|
||||
- [[_COMMUNITY_Community 34|Community 34]]
|
||||
- [[_COMMUNITY_Community 35|Community 35]]
|
||||
- [[_COMMUNITY_Community 36|Community 36]]
|
||||
- [[_COMMUNITY_Community 37|Community 37]]
|
||||
- [[_COMMUNITY_Community 38|Community 38]]
|
||||
- [[_COMMUNITY_Community 39|Community 39]]
|
||||
- [[_COMMUNITY_Community 41|Community 41]]
|
||||
- [[_COMMUNITY_Community 42|Community 42]]
|
||||
- [[_COMMUNITY_Community 43|Community 43]]
|
||||
- [[_COMMUNITY_Community 45|Community 45]]
|
||||
- [[_COMMUNITY_Community 47|Community 47]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `get()` - 193 edges
|
||||
2. `push()` - 33 edges
|
||||
1. `get()` - 196 edges
|
||||
2. `push()` - 34 edges
|
||||
3. `WebSocketClient` - 26 edges
|
||||
4. `withFullscreenLoading()` - 16 edges
|
||||
5. `MenuProcessor` - 16 edges
|
||||
6. `VersionManager` - 15 edges
|
||||
7. `emit()` - 15 edges
|
||||
4. `withFullscreenLoading()` - 17 edges
|
||||
5. `emit()` - 16 edges
|
||||
6. `MenuProcessor` - 16 edges
|
||||
7. `VersionManager` - 15 edges
|
||||
8. `handleDynamicRoutes()` - 15 edges
|
||||
9. `TableCache` - 14 edges
|
||||
10. `buildOutboundPilotContextSections()` - 12 edges
|
||||
|
||||
## Surprising Connections (you probably didn't know these)
|
||||
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
|
||||
views/system/site/index.vue → utils/fullscreen-loading.ts
|
||||
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
|
||||
views/basic-info/customer/index.vue → utils/fullscreen-loading.ts
|
||||
- `handleBatchDelete()` --calls--> `withFullscreenLoading()` [INFERRED]
|
||||
views/basic-info/collection-region/index.vue → utils/fullscreen-loading.ts
|
||||
- `handleAttachmentDownload()` --calls--> `downloadAttachmentFile()` [INFERRED]
|
||||
views/cell-storage/refund-confirm/index.vue → utils/download-attachment.ts
|
||||
- `handleAttachmentDownload()` --calls--> `downloadAttachmentFile()` [INFERRED]
|
||||
views/cell-storage/payment-confirm/index.vue → utils/download-attachment.ts
|
||||
- `subscribeTokenRefresh()` --calls--> `push()` [INFERRED]
|
||||
utils/http/index.ts → views/operations/storage-facility/index.vue
|
||||
- `handleUnauthorizedResponse()` --calls--> `fetchRefreshToken()` [INFERRED]
|
||||
@@ -75,190 +79,204 @@
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.01
|
||||
Nodes (143): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchCellSampleTypeList() (+135 more)
|
||||
Nodes (102): fetchAccessLogList(), fetchActiveRegions(), fetchCategoryList(), fetchCategoryParameters(), fetchCellProductPriceList(), fetchCellProductSpecList(), fetchCellProductTypeList(), fetchCellSampleTypeList() (+94 more)
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.01
|
||||
Nodes (10): fetchDeleteProjectSystemItem(), fetchDeptTree(), fetchGetRoleList(), fetchNotDeletedRegions(), fetchPostBoundUserIds(), fetchRegionList(), loadRoleList(), handleView() (+2 more)
|
||||
Cohesion: 0.02
|
||||
Nodes (99): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchCheckupInstitutionProjects(), fetchPilotCheckupDetail(), markPilotCheckupChecked() (+91 more)
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.02
|
||||
Nodes (71): fetchBatchGenerateProtocolNos(), fetchBatchGenerateSampleCodes(), fetchBatchIssueProtocolNos(), fetchBatchIssueSampleCodes(), fetchCollectionProtocolNoList(), fetchCreateCollectionProtocolNo(), fetchCreateMenu(), fetchCreateSampleCode() (+63 more)
|
||||
Nodes (66): fetchBatchGenerateProtocolNos(), fetchBatchGenerateSampleCodes(), fetchBatchIssueProtocolNos(), fetchBatchIssueSampleCodes(), fetchCollectionProtocolNoList(), fetchCreateCollectionProtocolNo(), fetchCreateSampleCode(), fetchDeleteCollectionProtocolNo() (+58 more)
|
||||
|
||||
### Community 3 - "Community 3"
|
||||
Cohesion: 0.03
|
||||
Nodes (82): batchSavePilotCheckup(), deletePilotCheckupActualItem(), fetchCheckupInstitutionList(), fetchCheckupInstitutionPackageDetail(), fetchCheckupInstitutionPackages(), fetchCheckupInstitutionProjects(), fetchPilotCheckupActualItems(), fetchPilotCheckupDetail() (+74 more)
|
||||
Nodes (40): fetchGetMenuList(), fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), clickTab(), handleTouchEnd(), setTransition() (+32 more)
|
||||
|
||||
### Community 4 - "Community 4"
|
||||
Cohesion: 0.03
|
||||
Nodes (40): fetchGetMenuList(), fetchGetMenuCodes(), fetchGetUserInfo(), fetchLogin(), fetchRefreshToken(), clickTab(), handleTouchEnd(), setTransition() (+32 more)
|
||||
Nodes (22): fetchAllConsumablePacks(), fetchAllConsumables(), fetchAllConsumableStockLogs(), fetchAllOutputTypes(), fetchAllProcessCategories(), fetchAllProcesses(), fetchAllReagents(), fetchAllReagentStockLogs() (+14 more)
|
||||
|
||||
### Community 5 - "Community 5"
|
||||
Cohesion: 0.05
|
||||
Nodes (34): fetchServiceOrderTxnList(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolList(), fetchPaymentDetailList(), fetchReceiptConfirmedList(), fetchReceiptConfirmList(), fetchReceiptConfirmSubmit(), fetchRefundConfirmedList() (+26 more)
|
||||
Cohesion: 0.03
|
||||
Nodes (22): fetchLeaveDetail(), fetchLeaveList(), fetchMaterialList(), fetchMaterialOptions(), fetchMaterialStockLogList(), fetchProductionWorkOrderDetail(), fetchProductionWorkOrderList(), fetchQcMaterialList() (+14 more)
|
||||
|
||||
### Community 6 - "Community 6"
|
||||
Cohesion: 0.06
|
||||
Nodes (11): fetchGetLatestUpgradeLog(), getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager (+3 more)
|
||||
Cohesion: 0.05
|
||||
Nodes (36): fetchServiceOrderTxnList(), fetchCollectionProtocolAuditList(), fetchCollectionProtocolList(), fetchPaymentDetailList(), fetchReceiptConfirmedList(), fetchReceiptConfirmList(), fetchReceiptConfirmSubmit(), fetchRefundConfirmedList() (+28 more)
|
||||
|
||||
### Community 7 - "Community 7"
|
||||
Cohesion: 0.06
|
||||
Nodes (39): completePilotApplication(), deletePilotApplication(), fetchFullmoonTodoList(), fetchPilotApplicationList(), fetchUpdateFullmoonTodoInsurance(), startPilotApplication(), voidPilotApplication(), fetchCollectionProtocolDetail() (+31 more)
|
||||
Cohesion: 0.07
|
||||
Nodes (8): updateInfusionScheduleStatus(), handleSubmit(), handleReset(), handleSearch(), WebSocketClient, TableCache, emit(), handleSubmit()
|
||||
|
||||
### Community 8 - "Community 8"
|
||||
Cohesion: 0.06
|
||||
Nodes (36): disableDevTools(), handleLock(), handleUnlock(), isMobile(), toLogin(), base64ToBytes(), bytesToBase64(), decryptJson() (+28 more)
|
||||
Cohesion: 0.04
|
||||
Nodes (28): fetchAttachmentDownloadUrl(), fetchOrderAdjusts(), fetchOrderDetail(), fetchOrderItems(), fetchOrderList(), fetchPriceVersionDetail(), fetchSystemConfig(), handleDownloadAttachment() (+20 more)
|
||||
|
||||
### Community 9 - "Community 9"
|
||||
Cohesion: 0.06
|
||||
Nodes (38): fetchCreateSite(), fetchCreateUser(), fetchDeleteSite(), fetchDeleteUser(), fetchDictDataOptions(), fetchResetPassword(), fetchRoleOptions(), fetchSiteList() (+30 more)
|
||||
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.06
|
||||
Nodes (26): for(), set(), walk(), annotateForkEdges(), chainApiToElements(), nextAncestorLink(), if(), ensureCategoryPathDataLoaded() (+18 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.1
|
||||
Nodes (2): WebSocketClient, TableCache
|
||||
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.07
|
||||
Nodes (21): auditorDisplay(), dash(), expressName(), formatYm(), formatYmd(), sampleTypeName(), siteName(), unitName() (+13 more)
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
### Community 10 - "Community 10"
|
||||
Cohesion: 0.08
|
||||
Nodes (32): toLogin(), base64ToBytes(), bytesToBase64(), decryptJson(), deriveKey(), encryptJson(), fromBase64Url(), hexToBytes() (+24 more)
|
||||
|
||||
### Community 11 - "Community 11"
|
||||
Cohesion: 0.07
|
||||
Nodes (22): handleBlur(), handleFocus(), handleInput(), fetchEthnicList(), addSample(), applyDonationBundleToForm(), confirmBatchTransportApply(), createEmptyTransport() (+14 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
### Community 12 - "Community 12"
|
||||
Cohesion: 0.09
|
||||
Nodes (8): getDefaultLanguage(), checkStorageCompatibility(), StorageConfig, getSystemStorage(), getSysVersion(), StorageKeyManager, StorageCompatibilityManager, validateStorageData()
|
||||
|
||||
### Community 13 - "Community 13"
|
||||
Cohesion: 0.07
|
||||
Nodes (20): fetchAttachmentDownloadUrl(), handleDownloadAttachment(), handleAttachmentDownload(), handleReceptionAttachmentDownload(), attachmentFullUrl(), handleAttachmentDownload(), handleAttachmentPreview(), formatDetectMethods() (+12 more)
|
||||
Nodes (17): fetchCreateMenu(), fetchDeleteMenu(), fetchMenuTree(), fetchUpdateMenu(), collectIds(), postorder(), preorder(), applyTreeExpand() (+9 more)
|
||||
|
||||
### Community 14 - "Community 14"
|
||||
Cohesion: 0.08
|
||||
Nodes (11): maskIdCard(), buildOutboundPilotContextSections(), applicationStatusLabel(), applicationStatusTagType(), infusionMethodLabel(), infusionStatusLabel(), infusionStatusTagType(), qcStatusLabel() (+3 more)
|
||||
|
||||
### Community 15 - "Community 15"
|
||||
Cohesion: 0.09
|
||||
Nodes (11): maskIdCard(), buildOutboundPilotContextSections(), applicationStatusLabel(), applicationStatusTagType(), infusionMethodLabel(), infusionStatusLabel(), infusionStatusTagType(), qcStatusLabel() (+3 more)
|
||||
Nodes (9): fetchGetAllUpgradeLogs(), fetchGetChinaMapData(), fetchGetComments(), fetchGetCurrentVersion(), fetchGetLatestUpgradeLog(), fetchGetSystemConfig(), fetchGetUpgradeLogs(), systemUpgrade() (+1 more)
|
||||
|
||||
### Community 16 - "Community 16"
|
||||
Cohesion: 0.1
|
||||
Nodes (19): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchHospitalList(), fetchUpdateCollectionRegion(), fetchUpdateHospital() (+11 more)
|
||||
Cohesion: 0.09
|
||||
Nodes (20): fetchCreateSite(), fetchDeleteSite(), fetchSiteList(), fetchUpdateSite(), fetchUserList(), formatHandoverUserLabel(), formatNowDatetime(), remoteSearchHandoverUsers() (+12 more)
|
||||
|
||||
### Community 17 - "Community 17"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
|
||||
Cohesion: 0.11
|
||||
Nodes (21): fetchFullmoonTodoList(), fetchUpdateFullmoonTodoInsurance(), fetchCollectionProtocolDetail(), buildInsuranceAttachmentsValue(), buildInsurancePayload(), formatYmdFromAny(), fullFileUrl(), handleBatchEdit() (+13 more)
|
||||
|
||||
### Community 18 - "Community 18"
|
||||
Cohesion: 0.1
|
||||
Nodes (6): fetchProductionTaskDetail(), fetchProductionTaskList(), fetchUpdateProductionTask(), handleClose(), handleSubmit(), loadTaskDetail()
|
||||
Nodes (19): fetchCollectionRegionList(), fetchCreateCollectionRegion(), fetchCreateHospital(), fetchDeleteCollectionRegion(), fetchDeleteHospital(), fetchHospitalList(), fetchUpdateCollectionRegion(), fetchUpdateHospital() (+11 more)
|
||||
|
||||
### Community 19 - "Community 19"
|
||||
Cohesion: 0.13
|
||||
Nodes (18): completePilotApplication(), deletePilotApplication(), fetchPilotApplicationList(), startPilotApplication(), voidPilotApplication(), clearTableSelection(), handleBatchComplete(), handleBatchStart() (+10 more)
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.12
|
||||
Nodes (15): addCopyButton(), highlightCode(), insertLineNumbers(), isBlockProcessed(), markBlockAsProcessed(), processBlock(), setupHighlightDirective(), setupRippleDirective() (+7 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
Cohesion: 0.1
|
||||
Nodes (6): fetchProductionTaskDetail(), fetchProductionTaskList(), fetchUpdateProductionTask(), handleClose(), handleSubmit(), loadTaskDetail()
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
Cohesion: 0.14
|
||||
Nodes (17): fetchCreateUser(), fetchDeleteUser(), fetchResetPassword(), fetchUpdateUser(), fetchUserDetail(), formatUserDeptDisplay(), handleAdd(), handleDelete() (+9 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
Cohesion: 0.16
|
||||
Nodes (10): useTable(), useTableImpl(), getColumnChecks(), useTableColumns(), createErrorHandler(), createSmartDebounce(), defaultResponseAdapter(), extractPagination() (+2 more)
|
||||
|
||||
### Community 20 - "Community 20"
|
||||
Cohesion: 0.15
|
||||
Nodes (12): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), handleAdd(), handleBatchDelete(), handleDelete(), handleReset() (+4 more)
|
||||
|
||||
### Community 21 - "Community 21"
|
||||
### Community 24 - "Community 24"
|
||||
Cohesion: 0.2
|
||||
Nodes (2): ComponentLoader, RouteTransformer
|
||||
|
||||
### Community 22 - "Community 22"
|
||||
### Community 25 - "Community 25"
|
||||
Cohesion: 0.27
|
||||
Nodes (14): useChart(), useChartComponent(), useChartOps(), colourBlend(), getCssVar(), getDarkColor(), getLightColor(), handleElementThemeColor() (+6 more)
|
||||
|
||||
### Community 23 - "Community 23"
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.17
|
||||
Nodes (11): fetchCreateCustomer(), fetchCustomerList(), fetchDeleteCustomer(), fetchUpdateCustomer(), handleAdd(), handleDelete(), handleReset(), handleSearch() (+3 more)
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.14
|
||||
Nodes (8): useSettingsHandlers(), useSettingsPanel(), useSettingsState(), useCeremony(), useTheme(), themeAnimation(), toggleTheme(), toggleTransition()
|
||||
|
||||
### Community 25 - "Community 25"
|
||||
### Community 29 - "Community 29"
|
||||
Cohesion: 0.17
|
||||
Nodes (3): fetchResearchDonationAuditList(), fetchResearchDonationDetail(), fetchResearchDonationList()
|
||||
|
||||
### Community 26 - "Community 26"
|
||||
Cohesion: 0.2
|
||||
Nodes (2): fetchQcMaterialList(), fetchQcMaterialOptions()
|
||||
|
||||
### Community 27 - "Community 27"
|
||||
Cohesion: 0.28
|
||||
Nodes (1): FireworkSystem
|
||||
|
||||
### Community 29 - "Community 29"
|
||||
### Community 31 - "Community 31"
|
||||
Cohesion: 0.39
|
||||
Nodes (6): isIgnorableScriptError(), normalizeErrorMessage(), registerPromiseErrorHandler(), registerResourceErrorHandler(), scriptErrorHandler(), setupErrorHandle()
|
||||
|
||||
### Community 30 - "Community 30"
|
||||
### Community 32 - "Community 32"
|
||||
Cohesion: 0.36
|
||||
Nodes (1): TableHeightCalculator
|
||||
|
||||
### Community 31 - "Community 31"
|
||||
### Community 33 - "Community 33"
|
||||
Cohesion: 0.25
|
||||
Nodes (2): fetchQcTaskDetail(), fetchQcTaskList()
|
||||
|
||||
### Community 34 - "Community 34"
|
||||
Cohesion: 0.32
|
||||
Nodes (3): annotateForkEdges(), chainApiToElements(), nextAncestorLink()
|
||||
|
||||
### Community 35 - "Community 35"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): logApiCryptoBootInfo(), maskSecret()
|
||||
|
||||
### Community 32 - "Community 32"
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 0.47
|
||||
Nodes (3): isPaymentDueWithinWarnWindow(), parsePaymentDueYmd(), startOfLocalDay()
|
||||
|
||||
### Community 36 - "Community 36"
|
||||
Cohesion: 0.5
|
||||
Nodes (3): reload(), handleMenuJump(), openExternalLink()
|
||||
### Community 39 - "Community 39"
|
||||
Cohesion: 0.33
|
||||
Nodes (2): fetchQcLabTestItemList(), fetchQcLabTestItemsActive()
|
||||
|
||||
### Community 37 - "Community 37"
|
||||
### Community 41 - "Community 41"
|
||||
Cohesion: 0.5
|
||||
Nodes (2): focusInput(), handleKeydown()
|
||||
|
||||
### Community 38 - "Community 38"
|
||||
### Community 42 - "Community 42"
|
||||
Cohesion: 0.67
|
||||
Nodes (2): if(), logSidebarMenu()
|
||||
|
||||
### Community 39 - "Community 39"
|
||||
### Community 43 - "Community 43"
|
||||
Cohesion: 0.5
|
||||
Nodes (1): fetchSampleSmsNotificationList()
|
||||
Nodes (1): fetchStorageEntrySmsList()
|
||||
|
||||
### Community 41 - "Community 41"
|
||||
### Community 45 - "Community 45"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): getSettingDefaults(), resetToDefaults()
|
||||
|
||||
### Community 43 - "Community 43"
|
||||
### Community 47 - "Community 47"
|
||||
Cohesion: 1.0
|
||||
Nodes (2): defaultAvatarPathByRole(), resolveUserAvatarUrl()
|
||||
|
||||
## Knowledge Gaps
|
||||
- **Thin community `Community 11`** (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`** (17 nodes): `ComponentLoader`, `.constructor()`, `.createEmptyComponent()`, `.createErrorComponent()`, `.load()`, `.loadIframe()`, `.loadLayout()`, `.load()`, `RouteTransformer`, `.extractFirstSegment()`, `.handleFirstLevelRoute()`, `.handleIframeRoute()`, `.handleNormalRoute()`, `.isFirstLevelRoute()`, `.transform()`, `ComponentLoader.ts`, `RouteTransformer.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 21`** (17 nodes): `ComponentLoader`, `.constructor()`, `.createEmptyComponent()`, `.createErrorComponent()`, `.load()`, `.loadIframe()`, `.loadLayout()`, `.load()`, `RouteTransformer`, `.extractFirstSegment()`, `.handleFirstLevelRoute()`, `.handleIframeRoute()`, `.handleNormalRoute()`, `.isFirstLevelRoute()`, `.transform()`, `ComponentLoader.ts`, `RouteTransformer.ts`
|
||||
- **Thin community `Community 32`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 26`** (10 nodes): `fetchBatchStockInQcMaterial()`, `fetchBatchStockOutQcMaterial()`, `fetchCreateQcMaterial()`, `fetchDeleteQcMaterial()`, `fetchQcMaterialList()`, `fetchQcMaterialOptions()`, `fetchStockInQcMaterial()`, `fetchStockOutQcMaterial()`, `fetchUpdateQcMaterial()`, `qc-material.ts`
|
||||
- **Thin community `Community 33`** (8 nodes): `fetchCompleteQcTask()`, `fetchCreateQcTask()`, `fetchDeleteQcTask()`, `fetchQcTaskDetail()`, `fetchQcTaskList()`, `fetchStartQcTask()`, `fetchUpdateQcTaskAttachments()`, `qc-task.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 27`** (9 nodes): `FireworkSystem`, `.constructor()`, `.createFirework()`, `.createParticle()`, `.getAvailableParticle()`, `.initializePool()`, `.preloadAllImages()`, `.preloadImage()`, `index.vue`
|
||||
- **Thin community `Community 35`** (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 30`** (8 nodes): `TableHeightCalculator`, `.calculate()`, `.calculateOffset()`, `.calculatePaginationOffset()`, `.constructor()`, `.getHeaderHeight()`, `useTableHeight()`, `useTableHeight.ts`
|
||||
- **Thin community `Community 39`** (6 nodes): `fetchCreateQcLabTestItem()`, `fetchDeleteQcLabTestItem()`, `fetchQcLabTestItemList()`, `fetchQcLabTestItemsActive()`, `fetchUpdateQcLabTestItem()`, `qc-lab-test-item.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 31`** (7 nodes): `App.vue`, `main.ts`, `refreshButtonAutoIcons()`, `refreshPanelTitleIcons()`, `logApiCryptoBootInfo()`, `maskSecret()`, `console.ts`
|
||||
- **Thin community `Community 41`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 37`** (5 nodes): `focusInput()`, `handleKeydown()`, `if()`, `search()`, `index.vue`
|
||||
- **Thin community `Community 42`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 38`** (4 nodes): `if()`, `logSidebarMenu()`, `index.vue`, `SidebarSubmenu.vue`
|
||||
- **Thin community `Community 43`** (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 39`** (4 nodes): `fetchGenerateSms()`, `fetchSampleSmsNotificationList()`, `fetchToggleSmsNotified()`, `sample-sms-notification.ts`
|
||||
- **Thin community `Community 45`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 41`** (3 nodes): `getSettingDefaults()`, `resetToDefaults()`, `setting.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
- **Thin community `Community 43`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
|
||||
- **Thin community `Community 47`** (3 nodes): `defaultAvatarPathByRole()`, `resolveUserAvatarUrl()`, `user-avatar.ts`
|
||||
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 7`, `Community 39`, `Community 9`, `Community 10`, `Community 13`, `Community 16`, `Community 18`, `Community 20`, `Community 25`, `Community 26`?**
|
||||
_High betweenness centrality (0.284) - this node is a cross-community bridge._
|
||||
- **Why does `push()` connect `Community 3` to `Community 1`, `Community 36`, `Community 6`, `Community 7`, `Community 8`, `Community 9`, `Community 10`, `Community 11`, `Community 13`, `Community 14`, `Community 15`, `Community 27`?**
|
||||
_High betweenness centrality (0.142) - this node is a cross-community bridge._
|
||||
- **Are the 192 inferred relationships involving `get()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `buildTreePathLabelFromFlat()`) actually correct?**
|
||||
_`get()` has 192 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 32 inferred relationships involving `push()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `fetchAllPagedRecords()`) actually correct?**
|
||||
_`push()` has 32 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 15 inferred relationships involving `withFullscreenLoading()` (e.g. with `.close()` and `handleSubmit()`) actually correct?**
|
||||
_`withFullscreenLoading()` has 15 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Why does `get()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`, `Community 4`, `Community 5`, `Community 6`, `Community 8`, `Community 9`, `Community 11`, `Community 13`, `Community 15`, `Community 16`, `Community 17`, `Community 18`, `Community 19`, `Community 21`, `Community 22`, `Community 26`, `Community 29`, `Community 33`, `Community 34`, `Community 39`, `Community 43`?**
|
||||
_High betweenness centrality (0.283) - this node is a cross-community bridge._
|
||||
- **Why does `push()` connect `Community 1` to `Community 0`, `Community 7`, `Community 8`, `Community 10`, `Community 11`, `Community 14`, `Community 15`, `Community 16`, `Community 17`?**
|
||||
_High betweenness centrality (0.122) - this node is a cross-community bridge._
|
||||
- **Are the 195 inferred relationships involving `get()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `buildTreePathLabelFromFlat()`) actually correct?**
|
||||
_`get()` has 195 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 33 inferred relationships involving `push()` (e.g. with `collectLeafFacilityNodesInTreeOrder()` and `fetchAllPagedRecords()`) actually correct?**
|
||||
_`push()` has 33 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 16 inferred relationships involving `withFullscreenLoading()` (e.g. with `.close()` and `handleSubmit()`) actually correct?**
|
||||
_`withFullscreenLoading()` has 16 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Are the 15 inferred relationships involving `emit()` (e.g. with `handleSubmit()` and `handleBlur()`) actually correct?**
|
||||
_`emit()` has 15 INFERRED edges - model-reasoned connections that need verification._
|
||||
- **Should `Community 0` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.01 - nodes in this community are weakly interconnected._
|
||||
- **Should `Community 1` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.01 - nodes in this community are weakly interconnected._
|
||||
@@ -57,12 +57,12 @@
|
||||
</div>
|
||||
<div id="legend"></div>
|
||||
</div>
|
||||
<div id="stats">1816 nodes · 2360 edges · 253 communities</div>
|
||||
<div id="stats">1844 nodes · 2394 edges · 262 communities</div>
|
||||
</div>
|
||||
<script>
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 31, "community_name": "Community 31", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 31, "community_name": "Community 31", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 31, "community_name": "Community 31", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 31, "community_name": "Community 31", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 31, "community_name": "Community 31", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#F28E2B", "border": "#F28E2B", "highlight": {"background": "#ffffff", "border": "#F28E2B"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 31, "community_name": "Community 31", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 75, "community_name": "Community 75", "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": 76, "community_name": "Community 76", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 17, "community_name": "Community 17", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 17, "community_name": "Community 17", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 17, "community_name": "Community 17", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 17, "community_name": "Community 17", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#FF9DA7", "border": "#FF9DA7", "highlight": {"background": "#ffffff", "border": "#FF9DA7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 17, "community_name": "Community 17", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective", "laLine truncated
|
||||
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 267}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 182}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 127}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 120}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 103}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 56}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 55}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 51}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 49}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 47}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 42}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 42}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 41}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 40}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 35}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 29}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 27}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 22}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 22}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 17}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 17}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 17}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 16}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 15}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 13}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 12}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 10}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 9}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 9}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 8}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 8}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 7}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 6}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 6}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 6}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 6}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 5}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 5}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 4}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 4}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 4}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 3}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 3}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 3}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 3}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 3}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 3}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 3}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 3}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 3}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 2}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 2}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 2}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 2}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 2}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 2}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 2}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 2}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 2}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 2}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 2}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 2}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 2}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 2}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 2}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 2}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 2}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 2}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 2}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "count": 2}, {Line truncated
|
||||
const RAW_NODES = [{"id": "app_vue", "label": "App.vue", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "App.vue", "community": 35, "community_name": "Community 35", "source_file": "App.vue", "file_type": "code", "degree": 3}, {"id": "src_app_refreshbuttonautoicons", "label": "refreshButtonAutoIcons()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshButtonAutoIcons()", "community": 35, "community_name": "Community 35", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "src_app_refreshpaneltitleicons", "label": "refreshPanelTitleIcons()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "refreshPanelTitleIcons()", "community": 35, "community_name": "Community 35", "source_file": "App.vue", "file_type": "code", "degree": 1}, {"id": "main_ts", "label": "main.ts", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.4, "font": {"size": 0, "color": "#ffffff"}, "title": "main.ts", "community": 35, "community_name": "Community 35", "source_file": "main.ts", "file_type": "code", "degree": 4}, {"id": "src_main_masksecret", "label": "maskSecret()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "maskSecret()", "community": 35, "community_name": "Community 35", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "src_main_logapicryptobootinfo", "label": "logApiCryptoBootInfo()", "color": {"background": "#EDC948", "border": "#EDC948", "highlight": {"background": "#ffffff", "border": "#EDC948"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "logApiCryptoBootInfo()", "community": 35, "community_name": "Community 35", "source_file": "main.ts", "file_type": "code", "degree": 2}, {"id": "env_d_ts", "label": "env.d.ts", "color": {"background": "#BAB0AC", "border": "#BAB0AC", "highlight": {"background": "#ffffff", "border": "#BAB0AC"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "env.d.ts", "community": 79, "community_name": "Community 79", "source_file": "env.d.ts", "file_type": "code", "degree": 0}, {"id": "shims_loose_d_ts", "label": "shims-loose.d.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.0, "font": {"size": 0, "color": "#ffffff"}, "title": "shims-loose.d.ts", "community": 80, "community_name": "Community 80", "source_file": "shims-loose.d.ts", "file_type": "code", "degree": 0}, {"id": "directives_index_ts", "label": "index.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.1, "font": {"size": 0, "color": "#ffffff"}, "title": "index.ts", "community": 20, "community_name": "Community 20", "source_file": "directives/index.ts", "file_type": "code", "degree": 1}, {"id": "directives_index_setupglobdirectives", "label": "setupGlobDirectives()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.5, "font": {"size": 0, "color": "#ffffff"}, "title": "setupGlobDirectives()", "community": 20, "community_name": "Community 20", "source_file": "directives/index.ts", "file_type": "code", "degree": 5}, {"id": "directives_core_roles_ts", "label": "roles.ts", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.3, "font": {"size": 0, "color": "#ffffff"}, "title": "roles.ts", "community": 20, "community_name": "Community 20", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 3}, {"id": "core_roles_checkrolepermission", "label": "checkRolePermission()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "checkRolePermission()", "community": 20, "community_name": "Community 20", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_removeelement", "label": "removeElement()", "color": {"background": "#4E79A7", "border": "#4E79A7", "highlight": {"background": "#ffffff", "border": "#4E79A7"}}, "size": 10.2, "font": {"size": 0, "color": "#ffffff"}, "title": "removeElement()", "community": 20, "community_name": "Community 20", "source_file": "directives/core/roles.ts", "file_type": "code", "degree": 2}, {"id": "core_roles_setuprolesdirective", "laLine truncated
|
||||
const RAW_EDGES = [{"from": "app_vue", "to": "src_app_refreshbuttonautoicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "src_app_refreshpaneltitleicons", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "app_vue", "to": "main_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "utils_sys_console_ts", "label": "imports_from", "title": "imports_from [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_masksecret", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "main_ts", "to": "src_main_logapicryptobootinfo", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "src_main_masksecret", "to": "src_main_logapicryptobootinfo", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_ts", "to": "directives_index_setupglobdirectives", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_index_setupglobdirectives", "to": "core_auth_setupauthdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "core_roles_setuprolesdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_highlight_setuphighlightdirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_index_setupglobdirectives", "to": "business_ripple_setuprippledirective", "label": "calls", "title": "calls [INFERRED]", "dashes": true, "width": 1, "color": {"opacity": 0.35}, "confidence": "INFERRED"}, {"from": "directives_core_roles_ts", "to": "core_roles_checkrolepermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_roles_ts", "to": "core_roles_setuprolesdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_roles_checkrolepermission", "to": "core_roles_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_checkauthpermission", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_removeelement", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_core_auth_ts", "to": "core_auth_setupauthdirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "core_auth_checkauthpermission", "to": "core_auth_removeelement", "label": "calls", "title": "calls [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_ripple_ts", "to": "business_ripple_setuprippledirective", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_highlightcode", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_insertlinenumbers", "label": "contains", "title": "contains [EXTRACTED]", "dashes": false, "width": 2, "color": {"opacity": 0.7}, "confidence": "EXTRACTED"}, {"from": "directives_business_highlight_ts", "to": "business_highlight_addcopybutton", "label": "contains", "title": "contains [EXTRACTED]", "dLine truncated
|
||||
const LEGEND = [{"cid": 0, "color": "#4E79A7", "label": "Community 0", "count": 303}, {"cid": 1, "color": "#F28E2B", "label": "Community 1", "count": 146}, {"cid": 2, "color": "#E15759", "label": "Community 2", "count": 113}, {"cid": 3, "color": "#76B7B2", "label": "Community 3", "count": 104}, {"cid": 4, "color": "#59A14F", "label": "Community 4", "count": 65}, {"cid": 5, "color": "#EDC948", "label": "Community 5", "count": 59}, {"cid": 6, "color": "#B07AA1", "label": "Community 6", "count": 58}, {"cid": 7, "color": "#FF9DA7", "label": "Community 7", "count": 54}, {"cid": 8, "color": "#9C755F", "label": "Community 8", "count": 53}, {"cid": 9, "color": "#BAB0AC", "label": "Community 9", "count": 43}, {"cid": 10, "color": "#4E79A7", "label": "Community 10", "count": 40}, {"cid": 11, "color": "#F28E2B", "label": "Community 11", "count": 40}, {"cid": 12, "color": "#E15759", "label": "Community 12", "count": 36}, {"cid": 13, "color": "#76B7B2", "label": "Community 13", "count": 35}, {"cid": 14, "color": "#59A14F", "label": "Community 14", "count": 31}, {"cid": 15, "color": "#EDC948", "label": "Community 15", "count": 31}, {"cid": 16, "color": "#B07AA1", "label": "Community 16", "count": 30}, {"cid": 17, "color": "#FF9DA7", "label": "Community 17", "count": 27}, {"cid": 18, "color": "#9C755F", "label": "Community 18", "count": 27}, {"cid": 19, "color": "#BAB0AC", "label": "Community 19", "count": 24}, {"cid": 20, "color": "#4E79A7", "label": "Community 20", "count": 22}, {"cid": 21, "color": "#F28E2B", "label": "Community 21", "count": 22}, {"cid": 22, "color": "#E15759", "label": "Community 22", "count": 20}, {"cid": 23, "color": "#76B7B2", "label": "Community 23", "count": 17}, {"cid": 24, "color": "#59A14F", "label": "Community 24", "count": 17}, {"cid": 25, "color": "#EDC948", "label": "Community 25", "count": 16}, {"cid": 26, "color": "#B07AA1", "label": "Community 26", "count": 16}, {"cid": 27, "color": "#FF9DA7", "label": "Community 27", "count": 15}, {"cid": 28, "color": "#9C755F", "label": "Community 28", "count": 13}, {"cid": 29, "color": "#BAB0AC", "label": "Community 29", "count": 12}, {"cid": 30, "color": "#4E79A7", "label": "Community 30", "count": 9}, {"cid": 31, "color": "#F28E2B", "label": "Community 31", "count": 8}, {"cid": 32, "color": "#E15759", "label": "Community 32", "count": 8}, {"cid": 33, "color": "#76B7B2", "label": "Community 33", "count": 8}, {"cid": 34, "color": "#59A14F", "label": "Community 34", "count": 8}, {"cid": 35, "color": "#EDC948", "label": "Community 35", "count": 7}, {"cid": 36, "color": "#B07AA1", "label": "Community 36", "count": 6}, {"cid": 37, "color": "#FF9DA7", "label": "Community 37", "count": 6}, {"cid": 38, "color": "#9C755F", "label": "Community 38", "count": 6}, {"cid": 39, "color": "#BAB0AC", "label": "Community 39", "count": 6}, {"cid": 40, "color": "#4E79A7", "label": "Community 40", "count": 6}, {"cid": 41, "color": "#F28E2B", "label": "Community 41", "count": 5}, {"cid": 42, "color": "#E15759", "label": "Community 42", "count": 4}, {"cid": 43, "color": "#76B7B2", "label": "Community 43", "count": 4}, {"cid": 44, "color": "#59A14F", "label": "Community 44", "count": 4}, {"cid": 45, "color": "#EDC948", "label": "Community 45", "count": 3}, {"cid": 46, "color": "#B07AA1", "label": "Community 46", "count": 3}, {"cid": 47, "color": "#FF9DA7", "label": "Community 47", "count": 3}, {"cid": 48, "color": "#9C755F", "label": "Community 48", "count": 3}, {"cid": 49, "color": "#BAB0AC", "label": "Community 49", "count": 3}, {"cid": 50, "color": "#4E79A7", "label": "Community 50", "count": 3}, {"cid": 51, "color": "#F28E2B", "label": "Community 51", "count": 3}, {"cid": 52, "color": "#E15759", "label": "Community 52", "count": 3}, {"cid": 53, "color": "#76B7B2", "label": "Community 53", "count": 3}, {"cid": 54, "color": "#59A14F", "label": "Community 54", "count": 2}, {"cid": 55, "color": "#EDC948", "label": "Community 55", "count": 2}, {"cid": 56, "color": "#B07AA1", "label": "Community 56", "count": 2}, {"cid": 57, "color": "#FF9DA7", "label": "Community 57", "count": 2}, {"cid": 58, "color": "#9C755F", "label": "Community 58", "count": 2}, {"cid": 59, "color": "#BAB0AC", "label": "Community 59", "count": 2}, {"cid": 60, "color": "#4E79A7", "label": "Community 60", "count": 2}, {"cid": 61, "color": "#F28E2B", "label": "Community 61", "count": 2}, {"cid": 62, "color": "#E15759", "label": "Community 62", "count": 2}, {"cid": 63, "color": "#76B7B2", "label": "Community 63", "count": 2}, {"cid": 64, "color": "#59A14F", "label": "Community 64", "count": 2}, {"cid": 65, "color": "#EDC948", "label": "Community 65", "count": 2}, {"cid": 66, "color": "#B07AA1", "label": "Community 66", "count": 2}, {"cid": 67, "color": "#FF9DA7", "label": "Community 67", "count": 2}, {"cid": 68, "color": "#9C755F", "label": "Community 68", "count": 2}, {"cid": 69, "color": "#BAB0AC", "label": "Community 69", "count": 2}, {"cid": 70, "color": "#4E79A7", "label": "Community 70", "count": 2},Line truncated
|
||||
|
||||
// HTML-escape helper — prevents XSS when injecting graph data into innerHTML
|
||||
function esc(s) {
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1778582068.0254738,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1778582068.026978,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1778582068.0246184,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1779802025.7709928,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1779800759.9701834,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1779830492.5032978,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1779829952.0242388,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1778582068.0293276,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1778775532.895603,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1778582068.004279,
|
||||
@@ -44,7 +44,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fullscreen-loading.ts": 1779117454.818312,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/router.ts": 1778582068.0393815,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/protocol-payment-labels.ts": 1779199125.4894068,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1779800687.4427748,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts": 1779826490.6921933,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/fetch-paged-list.ts": 1778775532.895782,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/sort-order.ts": 1779790341.2952042,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/outbound-qc-pilot-view-sections.ts": 1779801033.9457984,
|
||||
@@ -174,16 +174,18 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/media/art-cutter-img/index.vue": 1779525781.6051006,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-finance-confirm-fields/index.vue": 1779790325.7480125,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-finance-confirm-fields/finance-attachments.ts": 1778987672.644368,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue": 1779790326.0876904,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue": 1779824466.9487405,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/dialog-form-body/index.vue": 1779790325.0384986,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-qc-info-cards/index.vue": 1779790326.2389948,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-qc-info-cards/index.vue": 1779824462.0648055,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/order-operate-local-dialog/index.vue": 1779790325.101185,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/product-view-dialog/index.vue": 1779790325.4147334,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-production-info-cards/index.vue": 1779790326.1909053,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-storage-release-info/index.vue": 1779542542.2434525,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/infusion-schedule-outbound-chain-view/index.vue": 1779830492.0922873,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/work-order-remark-dialog/index.vue": 1779790326.4187233,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/outbound-material-form-sections/index.vue": 1779830388.1680186,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/protocol-payment-detail-readonly/index.vue": 1779525781.6098895,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/system-view-dialog/index.vue": 1779790326.3887281,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/system-view-dialog/index.vue": 1779811081.0609167,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/production-task-view-dialog/index.vue": 1779790325.572335,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/order-operate-dialog-bridge/index.vue": 1779790325.0697935,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/components/common/amount-input/index.vue": 1779790326.44512,
|
||||
@@ -203,6 +205,8 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/leave.ts": 1778666177.2933538,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/sample-reception-check-item.ts": 1779007909.2933023,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/production.ts": 1779790323.3656075,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts": 1779828287.9085703,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts": 1779827106.6848028,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-third-party-item.ts": 1779017836.101254,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts": 1779615813.3431153,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/workflow.ts": 1779790323.5709383,
|
||||
@@ -213,7 +217,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/chinaRegion.ts": 1778582067.8620915,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-entry-sms-notification.ts": 1779691739.8547683,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/business.ts": 1779159587.6033742,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts": 1779790323.0007687,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts": 1779809235.6101413,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/ethnic.ts": 1778929297.3919344,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-lab-test-item.ts": 1779017838.8231559,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts": 1779594447.3049195,
|
||||
@@ -241,7 +245,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-info/index.vue": 1779790344.50928,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/storage-cert/index.vue": 1779790347.9803133,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/index.vue": 1779790344.752764,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/material/index.vue": 1779790344.1219785,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/material/index.vue": 1779805190.0284903,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/fullmoon-todo/index.vue": 1779790343.5693362,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/storage-entry-info/index.vue": 1779790348.068867,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-confirm/index.vue": 1779790344.275864,
|
||||
@@ -294,8 +298,8 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/source-chain/chainToFlow.ts": 1779790358.1472504,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue": 1779790359.1428778,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/sop/index.vue": 1779790360.846551,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-task/index.vue": 1779801736.748366,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/reagent/index.vue": 1779790360.6636097,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-task/index.vue": 1779805190.0299485,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/reagent/index.vue": 1779805190.0304468,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue": 1779801416.9937282,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1779790360.26504,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1779790359.9189978,
|
||||
@@ -307,30 +311,31 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/DispatchTaskDialog.vue": 1779801620.1553593,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue": 1779701250.4415438,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/consumable-pack/index.vue": 1779790358.448969,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/consumable/index.vue": 1779790358.8545897,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/consumable/index.vue": 1779805190.029252,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process/index.vue": 1779790359.3889246,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-category/index.vue": 1779790358.9345891,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/index/index.vue": 1779525781.6949234,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1779799317.640114,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1779829827.0884113,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/OutboundQcViewDialog.vue": 1779801046.9472926,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception-check-item/index.vue": 1779790362.415862,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/lab-test-item/index.vue": 1779790361.1549423,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/index.vue": 1779790361.5001903,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/index.vue": 1779805190.0313084,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue": 1779790362.5893314,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue": 1779801145.0078545,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue": 1779790362.6423476,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue": 1779701230.688804,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue": 1779790362.6831455,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material-usage/index.vue": 1779790361.21638,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/third-party-company/index.vue": 1779790362.7538998,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/third-party-company/index.vue": 1779805190.032101,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/500/index.vue": 1779546148.0553653,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/403/index.vue": 1779546144.3909206,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/exception/404/index.vue": 1779546146.242008,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/cell-product-type/index.vue": 1779790350.9383118,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/checkup-institution/index.vue": 1779790354.3513076,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/checkup-institution/CheckupPackagePanel.vue": 1779790354.1964018,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/index.vue": 1779790354.895791,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/ScheduleViewDialog.vue": 1779791979.0257256,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/index.vue": 1779828418.9739575,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue": 1779830388.4782345,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/ScheduleViewDialog.vue": 1779830147.6915238,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/ScheduleCancelDialog.vue": 1779785733.3155978,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/ScheduleEditDialog.vue": 1779790355.024449,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/channel-service-order/index.vue": 1779792910.3544722,
|
||||
@@ -338,9 +343,13 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/channel-service-order/ServiceOrderTxnDialog.vue": 1779792403.5058112,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/channel/index.vue": 1779790353.7514098,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/channel/ChannelViewDialog.vue": 1779790352.7457726,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue": 1779786942.5154335,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/index.vue": 1779825987.213009,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue": 1779830031.9999688,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue": 1779824973.037717,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseMaterialDialog.vue": 1779829323.2023485,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue": 1779805793.0933356,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationDetailDialog.vue": 1779790355.5310535,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue": 1779790356.8604274,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue": 1779830501.0475526,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationFormDialog.vue": 1779791712.2092516,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/cell-product-price/index.vue": 1779790350.411278,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/checkup-management/index.vue": 1779790354.8558528,
|
||||
@@ -377,7 +386,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/RoutePermissionValidator.ts": 1778582068.0127926,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/index.ts": 1778582068.0138035,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/core/IframeRouteManager.ts": 1778582068.0121944,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts": 1779772106.7115505,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts": 1779809233.3957787,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/index.ts": 1778598510.6043763,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/exception.ts": 1779546186.993006,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/router/routes/staticRoutes.ts": 1779546226.1088154,
|
||||
|
||||
@@ -2217,19 +2217,6 @@ export const createOutboundQcWorkOrder = (data: any) =>
|
||||
params: data
|
||||
})
|
||||
|
||||
// ========== 中试服务 - 放行审核 ==========
|
||||
export const createReleaseReview = (data: any) =>
|
||||
request.post({ url: '/web/admin/pilot-release-review/create', params: data })
|
||||
|
||||
export const fetchReleaseReviewList = (params: any) =>
|
||||
request.get({ url: '/web/admin/pilot-release-review/list', params })
|
||||
|
||||
export const updateReleaseReview = (data: any) =>
|
||||
request.put({ url: '/web/admin/pilot-release-review/update', params: data })
|
||||
|
||||
export const deleteReleaseReview = (data: any) =>
|
||||
request.del({ url: '/web/admin/pilot-release-review/delete', data })
|
||||
|
||||
// ========== 中试服务 - 应用确认 ==========
|
||||
export const createInfusionConfirm = (data: any) =>
|
||||
request.post({ url: '/web/admin/pilot-infusion-confirm/create', params: data })
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import request from '@/utils/http'
|
||||
|
||||
export type OutboundHandoverInfo = {
|
||||
handoverUserId?: string
|
||||
handoverPerson?: string
|
||||
handoverTime?: string
|
||||
handoverLocation?: string
|
||||
receiverPerson?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export type OutboundHandoverDetail = {
|
||||
scheduleSummary?: {
|
||||
applicationNo?: string
|
||||
patientName?: string
|
||||
infusionTime?: string
|
||||
}
|
||||
outboundMaterial?: {
|
||||
remark?: string
|
||||
cells?: Array<{ cellProductId?: string; remark?: string }>
|
||||
qcMaterials?: Array<{ qcMaterialId?: string; qty?: number; remark?: string }>
|
||||
paperMaterials?: Array<{
|
||||
paperName?: string
|
||||
copies?: number
|
||||
attachmentUrl?: string
|
||||
remark?: string
|
||||
}>
|
||||
}
|
||||
handover?: OutboundHandoverInfo
|
||||
}
|
||||
|
||||
export function fetchInfusionScheduleOutboundHandover(scheduleId: string) {
|
||||
return request.get<OutboundHandoverDetail>({
|
||||
url: `/web/admin/pilot-infusion-schedule-mgmt/${scheduleId}/outbound-handover`
|
||||
})
|
||||
}
|
||||
|
||||
export function saveInfusionScheduleOutboundHandover(
|
||||
scheduleId: string,
|
||||
data: {
|
||||
outboundMaterial: {
|
||||
remark?: string
|
||||
cells: { cellProductId: string; remark?: string }[]
|
||||
qcMaterials: { materialId: string; qty: number; remark?: string }[]
|
||||
paperMaterials: { paperName: string; copies: number; attachmentUrl: string; remark?: string }[]
|
||||
}
|
||||
handover: {
|
||||
handoverUserId: string
|
||||
handoverTime: string
|
||||
handoverLocation?: string
|
||||
receiverPerson: string
|
||||
remark?: string
|
||||
}
|
||||
}
|
||||
) {
|
||||
return request.put<OutboundHandoverDetail>({
|
||||
url: `/web/admin/pilot-infusion-schedule-mgmt/${scheduleId}/outbound-handover`,
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
import request from '@/utils/http'
|
||||
import type {
|
||||
OutboundQcDetailApplication,
|
||||
OutboundQcDetailPatient,
|
||||
OutboundQcDetailInfusionPlan,
|
||||
OutboundQcDetailInfusionSchedule,
|
||||
OutboundQcDetailProduct,
|
||||
} from '@/api/qc-work-order'
|
||||
|
||||
type PageList<T> = {
|
||||
list: T[]
|
||||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
export type OutboundReleaseReview = {
|
||||
id: string
|
||||
scheduleId?: string
|
||||
/** pilot_service=中试服务 clinical_application=临床应用 */
|
||||
bizType?: string
|
||||
bizTypeLabel?: string
|
||||
applicationNo?: string
|
||||
patientName?: string
|
||||
cellType?: string
|
||||
specText?: string
|
||||
infusionTime?: string
|
||||
scheduleStatus?: string
|
||||
scheduleStatusLabel?: string
|
||||
qcStatus?: string
|
||||
qcStatusLabel?: string
|
||||
reviewConclusion?: string
|
||||
reviewConclusionLabel?: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
reviewByName?: string
|
||||
reviewTime?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
export type {
|
||||
OutboundQcDetailApplication,
|
||||
OutboundQcDetailPatient,
|
||||
OutboundQcDetailInfusionPlan,
|
||||
OutboundQcDetailInfusionSchedule,
|
||||
OutboundQcDetailProduct,
|
||||
}
|
||||
|
||||
export type OutboundReviewInfo = {
|
||||
id?: string
|
||||
scheduleId?: string
|
||||
reviewConclusion?: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
reviewByName?: string
|
||||
reviewTime?: string
|
||||
}
|
||||
|
||||
export type OutboundProductionTask = {
|
||||
id?: string
|
||||
taskNo?: string
|
||||
taskName?: string
|
||||
taskStatus?: string
|
||||
taskStatusLabel?: string
|
||||
conclusion?: string
|
||||
conclusionLabel?: string
|
||||
startedTime?: string
|
||||
completedTime?: string
|
||||
}
|
||||
|
||||
export type OutboundProductionInfo = {
|
||||
workOrderNo?: string
|
||||
workOrderStatus?: string
|
||||
workOrderStatusLabel?: string
|
||||
tasks?: OutboundProductionTask[]
|
||||
}
|
||||
|
||||
export type OutboundQcTask = {
|
||||
id?: string
|
||||
taskType?: string
|
||||
taskTypeLabel?: string
|
||||
taskStatus?: string
|
||||
taskStatusLabel?: string
|
||||
conclusion?: string
|
||||
conclusionLabel?: string
|
||||
}
|
||||
|
||||
export type OutboundQcInfo = {
|
||||
workOrderNo?: string
|
||||
workOrderStatus?: string
|
||||
workOrderStatusLabel?: string
|
||||
tasks?: OutboundQcTask[]
|
||||
}
|
||||
|
||||
export type OutboundReleaseReviewDetail = {
|
||||
application?: OutboundQcDetailApplication
|
||||
patient?: OutboundQcDetailPatient
|
||||
infusionPlan?: OutboundQcDetailInfusionPlan
|
||||
infusionSchedule?: OutboundQcDetailInfusionSchedule
|
||||
products?: OutboundQcDetailProduct[]
|
||||
productionInfo?: OutboundProductionInfo
|
||||
qcInfo?: OutboundQcInfo
|
||||
reviewInfo?: OutboundReviewInfo
|
||||
outboundMaterial?: OutboundReleaseMaterialDetail
|
||||
}
|
||||
|
||||
export type OutboundReleaseCellRow = {
|
||||
id?: string
|
||||
cellProductId?: string
|
||||
productCode?: string
|
||||
productName?: string
|
||||
productTypeName?: string
|
||||
specName?: string
|
||||
passage?: string
|
||||
batchNo?: string
|
||||
storageSlotName?: string
|
||||
productStatus?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export type OutboundReleaseQcMaterialRow = {
|
||||
id?: string
|
||||
qcMaterialId?: string
|
||||
qty?: number
|
||||
materialCode?: string
|
||||
materialName?: string
|
||||
specModel?: string
|
||||
unit?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export type OutboundReleasePaperRow = {
|
||||
id?: string
|
||||
paperName?: string
|
||||
copies?: number
|
||||
attachmentUrl?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export type OutboundReleaseMaterialDetail = {
|
||||
id?: string
|
||||
infusionScheduleId?: string
|
||||
paperAttachments?: string
|
||||
paperMaterials?: OutboundReleasePaperRow[]
|
||||
remark?: string
|
||||
cells?: OutboundReleaseCellRow[]
|
||||
qcMaterials?: OutboundReleaseQcMaterialRow[]
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
export function fetchOutboundReleaseReviewList(params?: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
scheduleStatus?: string
|
||||
reviewConclusion?: string
|
||||
qcStatus?: string
|
||||
keyword?: string
|
||||
}) {
|
||||
return request.get<PageList<OutboundReleaseReview>>({
|
||||
url: '/web/admin/pilot-release-reviews',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchOutboundReleaseReviewDetail(scheduleId: string) {
|
||||
return request.get<OutboundReleaseReviewDetail>({
|
||||
url: `/web/admin/pilot-release-reviews/${scheduleId}/detail`,
|
||||
})
|
||||
}
|
||||
|
||||
export function submitOutboundReleaseReview(
|
||||
scheduleId: string,
|
||||
data: {
|
||||
reviewConclusion: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
}
|
||||
) {
|
||||
return request.put<OutboundReleaseReview>({
|
||||
url: `/web/admin/pilot-release-reviews/${scheduleId}/review`,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function batchReviewOutboundRelease(data: {
|
||||
scheduleIds: string[]
|
||||
reviewConclusion: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
}) {
|
||||
return request.put<void>({
|
||||
url: '/web/admin/pilot-release-reviews/batch-review',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchOutboundReleaseMaterial(scheduleId: string) {
|
||||
return request.get<OutboundReleaseMaterialDetail>({
|
||||
url: `/web/admin/pilot-release-reviews/${scheduleId}/outbound-materials`,
|
||||
})
|
||||
}
|
||||
|
||||
export function saveOutboundReleaseMaterial(
|
||||
scheduleId: string,
|
||||
data: {
|
||||
paperMaterials?: { paperName: string; copies: number; attachmentUrl: string; remark?: string }[]
|
||||
remark?: string
|
||||
cells: { cellProductId: string; remark?: string }[]
|
||||
qcMaterials: { materialId: string; qty: number; remark?: string }[]
|
||||
}
|
||||
) {
|
||||
return request.put<OutboundReleaseMaterialDetail>({
|
||||
url: `/web/admin/pilot-release-reviews/${scheduleId}/outbound-materials`,
|
||||
data,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,682 @@
|
||||
<!-- 输注计划出库链路只读区(生产/质检/物料/放行/交接/应用确认) -->
|
||||
<template>
|
||||
<div v-loading="loading" class="infusion-schedule-outbound-chain">
|
||||
<template v-if="detail?.id || scheduleId">
|
||||
<div class="outbound-chain-section">
|
||||
<div class="section-subtitle">出库生产工单 + 任务</div>
|
||||
<SampleProductionInfoCards :production-info="detail?.productionInfo" />
|
||||
</div>
|
||||
|
||||
<div class="outbound-chain-section">
|
||||
<div class="section-subtitle">出库质检工单 + 任务</div>
|
||||
<SampleQcInfoCards :qc-info="detail?.qcInfo" hide-sample-context />
|
||||
</div>
|
||||
|
||||
<div class="outbound-chain-section">
|
||||
<div class="section-subtitle">出库物料准备</div>
|
||||
<div class="outbound-material-view">
|
||||
<div v-if="!hasOutboundMaterial" class="outbound-material-empty">暂无出库物料准备记录</div>
|
||||
<template v-else>
|
||||
<div v-if="outboundMaterialCells.length" class="outbound-material-block">
|
||||
<h4 class="outbound-material-subtitle">出库细胞</h4>
|
||||
<el-table
|
||||
:data="outboundMaterialCells"
|
||||
class="list-table inner-table"
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column
|
||||
prop="productCode"
|
||||
label="产品编码"
|
||||
min-width="150"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="productName"
|
||||
label="产品名称"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="productTypeName" label="类型" min-width="90" />
|
||||
<el-table-column prop="specName" label="规格" min-width="90" />
|
||||
<el-table-column
|
||||
prop="storageSlotName"
|
||||
label="储物格"
|
||||
min-width="130"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="状态" min-width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.productStatus"
|
||||
:type="cellProductStatusTagType(row.productStatus) as any"
|
||||
size="small"
|
||||
>
|
||||
{{ cellProductStatusLabel(row.productStatus) }}
|
||||
</el-tag>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div v-if="outboundMaterialQcRows.length" class="outbound-material-block">
|
||||
<h4 class="outbound-material-subtitle">使用的物料</h4>
|
||||
<el-table
|
||||
:data="outboundMaterialQcRows"
|
||||
class="list-table inner-table"
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column
|
||||
prop="materialName"
|
||||
label="物料名称"
|
||||
min-width="140"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="specModel" label="规格型号" min-width="110" />
|
||||
<el-table-column label="计划用量" min-width="100" align="center">
|
||||
<template #default="{ row }">{{ row.qty ?? '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="unit" label="单位" min-width="72" align="center" />
|
||||
</el-table>
|
||||
</div>
|
||||
<div v-if="outboundPaperRows.length" class="outbound-material-block">
|
||||
<h4 class="outbound-material-subtitle">其他纸质材料</h4>
|
||||
<el-table
|
||||
:data="outboundPaperRows"
|
||||
class="list-table inner-table"
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column prop="paperName" label="名称" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="份数" min-width="80" align="center">
|
||||
<template #default="{ row }">{{ row.copies ?? '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件" min-width="320">
|
||||
<template #default="{ row }">
|
||||
<div v-if="parsePaperAttachments(row.attachmentUrl).length" class="paper-att-cards">
|
||||
<div
|
||||
v-for="(att, attIdx) in parsePaperAttachments(row.attachmentUrl)"
|
||||
:key="attIdx"
|
||||
class="paper-att-card"
|
||||
>
|
||||
<el-icon class="paper-att-card__icon"><Document /></el-icon>
|
||||
<span class="paper-att-card__name" :title="att.fileName">{{
|
||||
att.fileName || '附件'
|
||||
}}</span>
|
||||
<div class="paper-att-card__actions">
|
||||
<el-icon
|
||||
v-if="isImageFile(att)"
|
||||
class="paper-att-card__action"
|
||||
title="预览"
|
||||
@click="handlePreviewPaperAttachment(att)"
|
||||
>
|
||||
<View />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="paper-att-card__action"
|
||||
title="下载"
|
||||
@click="handleDownloadPaperAttachment(att)"
|
||||
>
|
||||
<Download />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<p v-if="outboundMaterialRemark" class="outbound-material-remark">
|
||||
备注:{{ outboundMaterialRemark }}
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="outbound-chain-section">
|
||||
<div class="section-subtitle">出库放行审核</div>
|
||||
<div class="data-grid data-grid--3">
|
||||
<div class="data-item">
|
||||
<div class="data-label">审核结论</div>
|
||||
<div class="data-value">
|
||||
<el-tag :type="reviewConclusionTag as any" size="small">
|
||||
{{ reviewConclusionDisplay }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">审核人</div>
|
||||
<div class="data-value">{{ dash(review.reviewByName) }}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">审核时间</div>
|
||||
<div class="data-value">{{ dash(review.reviewTime) }}</div>
|
||||
</div>
|
||||
<div class="data-item data-item--full">
|
||||
<div class="data-label">审核备注</div>
|
||||
<div class="data-value data-value--muted">{{ dash(review.reviewRemark) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="reviewAttachmentFiles.length" class="attachment-row">
|
||||
<div class="data-label">审核附件</div>
|
||||
<ProtocolAttachmentReadonly
|
||||
:files="reviewAttachmentFiles"
|
||||
@preview="handleAttachmentPreview"
|
||||
@download="handleAttachmentDownload"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="outbound-chain-section">
|
||||
<div class="section-subtitle">出库交接</div>
|
||||
<div class="data-grid data-grid--3">
|
||||
<div class="data-item">
|
||||
<div class="data-label">交接人</div>
|
||||
<div class="data-value">{{ dash(outboundHandover.handoverPerson) }}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">交接时间</div>
|
||||
<div class="data-value">{{ dash(outboundHandover.handoverTime) }}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">交接地点</div>
|
||||
<div class="data-value">{{ dash(outboundHandover.handoverLocation) }}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">接收人</div>
|
||||
<div class="data-value">{{ dash(outboundHandover.receiverPerson) }}</div>
|
||||
</div>
|
||||
<div class="data-item data-item--full">
|
||||
<div class="data-label">备注</div>
|
||||
<div class="data-value data-value--muted">{{ dash(outboundHandover.remark) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="outbound-chain-section">
|
||||
<div class="section-subtitle">应用确认</div>
|
||||
<div class="data-grid data-grid--3">
|
||||
<template v-if="hasInfusionConfirm(infusionConfirm)">
|
||||
<div class="data-item">
|
||||
<div class="data-label">确认人</div>
|
||||
<div class="data-value">{{ dash(infusionConfirm.confirmUserName) }}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">确认时间</div>
|
||||
<div class="data-value">{{ dash(infusionConfirm.confirmTime) }}</div>
|
||||
</div>
|
||||
<div class="data-item data-item--full">
|
||||
<div class="data-label">确认说明</div>
|
||||
<div class="data-value data-value--muted">{{ dash(infusionConfirm.confirmDesc) }}</div>
|
||||
</div>
|
||||
<div class="data-item data-item--full">
|
||||
<div class="data-label">备注</div>
|
||||
<div class="data-value data-value--muted">{{ dash(infusionConfirm.remark) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="isInfusionConfirmed">
|
||||
<div class="data-item">
|
||||
<div class="data-label">确认状态</div>
|
||||
<div class="data-value">
|
||||
<el-tag :type="infusionStatusTagType('yrqr') as any" size="small">
|
||||
{{ infusionStatusLabel('yrqr') }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-item data-item--full">
|
||||
<div class="data-label">说明</div>
|
||||
<div class="data-value data-value--muted">
|
||||
已在输注计划管理中确认,暂无独立应用确认记录
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="data-item">
|
||||
<div class="data-label">确认状态</div>
|
||||
<div class="data-value">
|
||||
<el-tag type="info" size="small">未确认</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-dialog
|
||||
v-model="previewVisible"
|
||||
title="文件预览"
|
||||
width="800px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
style="backdrop-filter: none"
|
||||
>
|
||||
<div v-if="previewType === 'image'" class="preview-container">
|
||||
<img
|
||||
:src="previewUrl"
|
||||
alt="预览"
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="preview-container">
|
||||
<video
|
||||
:src="previewUrl"
|
||||
controls
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { Document, Download, View } from '@element-plus/icons-vue'
|
||||
import SampleProductionInfoCards from '@/components/business/sample-production-info-cards/index.vue'
|
||||
import SampleQcInfoCards from '@/components/business/sample-qc-info-cards/index.vue'
|
||||
import ProtocolAttachmentReadonly from '@/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue'
|
||||
import { fetchInfusionScheduleMgmtDetail } from '@/api/admin'
|
||||
import {
|
||||
storageReleaseConclusionLabel,
|
||||
storageReleaseConclusionTagType,
|
||||
infusionStatusLabel,
|
||||
infusionStatusTagType,
|
||||
cellProductStatusLabel,
|
||||
cellProductStatusTagType
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
|
||||
defineOptions({ name: 'InfusionScheduleOutboundChainView' })
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
scheduleId?: string
|
||||
/** 外部已加载的详情(如 ScheduleViewDialog),传入则不再请求 */
|
||||
detailData?: Record<string, any> | null
|
||||
/** 懒加载:为 false 时不请求(申请单查看折叠态) */
|
||||
active?: boolean
|
||||
}>(),
|
||||
{
|
||||
scheduleId: '',
|
||||
detailData: null,
|
||||
active: true
|
||||
}
|
||||
)
|
||||
|
||||
const loading = ref(false)
|
||||
const fetchedDetail = ref<Record<string, any> | null>(null)
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const previewType = ref<'image' | 'video'>('image')
|
||||
|
||||
type PaperAttachmentView = {
|
||||
fileName: string
|
||||
attachmentUrl: string
|
||||
fileUrl: string
|
||||
fileType?: string
|
||||
}
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
const detail = computed(() => props.detailData || fetchedDetail.value)
|
||||
|
||||
const review = computed(() => detail.value?.reviewInfo || {})
|
||||
const outboundHandover = computed(() => detail.value?.outboundHandover || {})
|
||||
const infusionConfirm = computed(() => detail.value?.infusionConfirm || {})
|
||||
const outboundMaterial = computed(() => detail.value?.outboundMaterial || null)
|
||||
|
||||
const outboundMaterialCells = computed(() => outboundMaterial.value?.cells || [])
|
||||
const outboundMaterialQcRows = computed(() => outboundMaterial.value?.qcMaterials || [])
|
||||
const outboundMaterialRemark = computed(() => outboundMaterial.value?.remark || '')
|
||||
|
||||
const outboundPaperRows = computed(() => {
|
||||
const materials = outboundMaterial.value?.paperMaterials || []
|
||||
if (materials.length) {
|
||||
return materials.map((p: Record<string, any>) => ({
|
||||
paperName: p.paperName || '',
|
||||
copies: p.copies,
|
||||
attachmentUrl: p.attachmentUrl || ''
|
||||
}))
|
||||
}
|
||||
const urls = outboundMaterial.value?.paperAttachments || ''
|
||||
if (!urls.trim()) return []
|
||||
return urls
|
||||
.split(',')
|
||||
.map((u: string) => u.trim())
|
||||
.filter(Boolean)
|
||||
.map((attachmentUrl: string) => ({
|
||||
paperName: paperAttachmentName(attachmentUrl),
|
||||
copies: 1,
|
||||
attachmentUrl
|
||||
}))
|
||||
})
|
||||
|
||||
const hasOutboundMaterial = computed(
|
||||
() =>
|
||||
outboundMaterialCells.value.length > 0 ||
|
||||
outboundMaterialQcRows.value.length > 0 ||
|
||||
outboundPaperRows.value.length > 0 ||
|
||||
!!outboundMaterialRemark.value.trim()
|
||||
)
|
||||
|
||||
const reviewAttachmentFiles = computed(() => {
|
||||
const urls = review.value?.reviewAttachments || ''
|
||||
if (!urls.trim()) return []
|
||||
return urls
|
||||
.split(',')
|
||||
.map((u: string) => u.trim())
|
||||
.filter(Boolean)
|
||||
.map((fileUrl: string, i: number) => ({
|
||||
id: `att_${i}`,
|
||||
fileUrl,
|
||||
fileName: fileUrl.split('/').pop() || ''
|
||||
}))
|
||||
})
|
||||
|
||||
const reviewConclusionValue = computed(() => review.value?.reviewConclusion || '')
|
||||
const reviewConclusionDisplay = computed(() =>
|
||||
reviewConclusionValue.value
|
||||
? storageReleaseConclusionLabel(reviewConclusionValue.value)
|
||||
: '待审核'
|
||||
)
|
||||
const reviewConclusionTag = computed(() =>
|
||||
reviewConclusionValue.value
|
||||
? storageReleaseConclusionTagType(reviewConclusionValue.value)
|
||||
: 'info'
|
||||
)
|
||||
|
||||
const isInfusionConfirmed = computed(() => {
|
||||
const d = detail.value || {}
|
||||
const sched = d.infusionSchedule || {}
|
||||
return sched.infusionStatus === 'yrqr' || d.infusionStatus === 'yrqr'
|
||||
})
|
||||
|
||||
function dash(v: unknown) {
|
||||
if (v == null || v === '') return '—'
|
||||
return String(v)
|
||||
}
|
||||
|
||||
function hasInfusionConfirm(confirm: Record<string, any>) {
|
||||
return !!(confirm?.id || confirm?.confirmTime || confirm?.confirmUserName)
|
||||
}
|
||||
|
||||
function paperAttachmentName(url: string) {
|
||||
return url.split('/').pop() || '附件'
|
||||
}
|
||||
|
||||
function splitUrls(str: string): string[] {
|
||||
if (!str?.trim()) return []
|
||||
return str
|
||||
.split(',')
|
||||
.map((u) => u.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function parsePaperAttachments(urlStr: string): PaperAttachmentView[] {
|
||||
return splitUrls(urlStr).map((url) => ({
|
||||
fileName: paperAttachmentName(url),
|
||||
attachmentUrl: url,
|
||||
fileUrl: url
|
||||
}))
|
||||
}
|
||||
|
||||
function isImageFile(file: PaperAttachmentView) {
|
||||
const url = (file.fileUrl || file.attachmentUrl || '').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: PaperAttachmentView) {
|
||||
const url = (file.fileUrl || file.attachmentUrl || '').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 attachmentFullUrl(url: string) {
|
||||
const u = String(url || '').trim()
|
||||
if (!u) return ''
|
||||
if (/^https?:\/\//i.test(u)) return u
|
||||
return minioBaseUrl ? `${minioBaseUrl}/${u.replace(/^\//, '')}` : u
|
||||
}
|
||||
|
||||
function handlePreviewPaperAttachment(att: PaperAttachmentView) {
|
||||
if (!att.attachmentUrl) return
|
||||
previewUrl.value = attachmentFullUrl(att.attachmentUrl)
|
||||
previewType.value = isVideoFile(att) ? 'video' : 'image'
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
function handleDownloadPaperAttachment(att: PaperAttachmentView) {
|
||||
if (!att.attachmentUrl) return
|
||||
downloadAttachmentFile(
|
||||
{
|
||||
fileName: att.fileName,
|
||||
fileUrl: att.attachmentUrl,
|
||||
fileType: att.fileType || ''
|
||||
},
|
||||
{ fullFileUrl: attachmentFullUrl }
|
||||
)
|
||||
}
|
||||
|
||||
function handleAttachmentPreview(file: Record<string, unknown>) {
|
||||
const url = attachmentFullUrl(String(file.fileUrl || ''))
|
||||
if (url) window.open(url, '_blank')
|
||||
}
|
||||
|
||||
function handleAttachmentDownload(file: Record<string, unknown>) {
|
||||
downloadAttachmentFile(file, { fullFileUrl: attachmentFullUrl })
|
||||
}
|
||||
|
||||
async function loadDetail() {
|
||||
const id = (props.scheduleId || '').trim()
|
||||
if (!id || props.detailData) return
|
||||
loading.value = true
|
||||
try {
|
||||
const res: any = await fetchInfusionScheduleMgmtDetail({ scheduleId: id })
|
||||
fetchedDetail.value =
|
||||
res && typeof res === 'object' && !Array.isArray(res)
|
||||
? (res as Record<string, any>)
|
||||
: ((res as any)?.data ?? null)
|
||||
} catch {
|
||||
fetchedDetail.value = null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.scheduleId, props.active, props.detailData] as const,
|
||||
([id, active, external]) => {
|
||||
if (external) {
|
||||
fetchedDetail.value = null
|
||||
return
|
||||
}
|
||||
if (!id || !active) return
|
||||
loadDetail()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.infusion-schedule-outbound-chain {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px dashed #e2e8f0;
|
||||
}
|
||||
|
||||
.outbound-chain-section {
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
margin: 0 0 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.outbound-material-view {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.outbound-material-empty {
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.outbound-material-block {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.outbound-material-subtitle {
|
||||
margin: 0 0 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.outbound-material-remark {
|
||||
margin: 12px 0 0;
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.attachment-row {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.data-grid {
|
||||
display: grid;
|
||||
gap: 12px 16px;
|
||||
|
||||
&--3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.data-item--full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
font-size: 14px;
|
||||
color: #0f172a;
|
||||
font-weight: 500;
|
||||
word-break: break-word;
|
||||
|
||||
&--muted {
|
||||
color: #475569;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.sample-card__section-label) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.sample-card__section--qc),
|
||||
:deep(.sample-card__section--production) {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.paper-att-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.paper-att-card {
|
||||
width: 100px;
|
||||
height: 72px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
background: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.paper-att-card__icon {
|
||||
color: #0284c7;
|
||||
font-size: 18px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.paper-att-card__name {
|
||||
width: 100%;
|
||||
font-size: 11px;
|
||||
color: #334155;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.paper-att-card__actions {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.paper-att-card:hover .paper-att-card__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.paper-att-card__action {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #10b981;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,977 @@
|
||||
<!-- 出库物料编辑区(细胞 / 质检物料 / 纸质材料) -->
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<div class="outbound-material-form-sections">
|
||||
<div class="material-sections">
|
||||
<!-- 出库细胞 -->
|
||||
<section class="section-card">
|
||||
<div class="section-head">
|
||||
<h3 class="section-title section-title--mint-bar">出库细胞</h3>
|
||||
<el-button type="primary" size="small" class="btn-main" @click="openCellSelect">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加细胞产品
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="list-body list-wrap inner-table-wrap">
|
||||
<el-table
|
||||
:data="cellRows"
|
||||
class="list-table inner-table"
|
||||
stripe
|
||||
max-height="280"
|
||||
empty-text="请从细胞产品库勾选"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column
|
||||
prop="productCode"
|
||||
label="产品编码"
|
||||
min-width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="productName"
|
||||
label="产品名称"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="productTypeName" label="产品类型" min-width="100" />
|
||||
<el-table-column prop="specName" label="规格" min-width="90" />
|
||||
<el-table-column prop="passage" label="代次" min-width="70" align="center" />
|
||||
<el-table-column
|
||||
prop="storageSlotName"
|
||||
label="储物格"
|
||||
min-width="140"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="状态" min-width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.productStatus"
|
||||
:type="cellProductStatusTagType(row.productStatus) as any"
|
||||
size="small"
|
||||
>
|
||||
{{ cellProductStatusLabel(row.productStatus) }}
|
||||
</el-tag>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="80" align="center" fixed="right">
|
||||
<template #default="{ $index }">
|
||||
<el-button type="danger" link size="small" @click="removeCellRow($index)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 使用的物料 -->
|
||||
<section class="section-card">
|
||||
<div class="section-head">
|
||||
<h3 class="section-title section-title--mint-bar">使用的物料</h3>
|
||||
<el-button type="primary" size="small" class="btn-main" @click="addMaterialRow">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加物料
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-if="materialRows.length"
|
||||
:data="materialRows"
|
||||
:row-key="(row) => row._rowKey"
|
||||
stripe
|
||||
class="list-table inner-table"
|
||||
max-height="280"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column label="物料名称" min-width="180">
|
||||
<template #default="{ row, $index }">
|
||||
<el-select
|
||||
v-model="row.materialId"
|
||||
filterable
|
||||
placeholder="搜索并选择物料"
|
||||
class="inner-cell-input"
|
||||
@change="(v) => onMaterialSelect($index, v)"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in materialOptions"
|
||||
:key="opt.id"
|
||||
:label="opt.label"
|
||||
:value="opt.id"
|
||||
:disabled="isMaterialOptionDisabled(opt.id, $index)"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格型号" min-width="120">
|
||||
<template #default="{ row }">{{ row.specModel || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计划用量" min-width="130">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.qty"
|
||||
:min="0.0001"
|
||||
:precision="4"
|
||||
:step="1"
|
||||
controls-position="right"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="72" align="center">
|
||||
<template #default="{ row }">{{ row.unit || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前库存" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ stockQtyDisplay(row.materialId) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="80" align="center" fixed="right">
|
||||
<template #default="{ $index }">
|
||||
<el-button type="danger" link size="small" @click="removeMaterialRow($index)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<p v-else class="section-empty-hint">点击「添加物料」从质检物料库勾选并填写计划用量</p>
|
||||
</section>
|
||||
|
||||
<!-- 其他纸质材料 -->
|
||||
<section class="section-card">
|
||||
<div class="section-head">
|
||||
<h3 class="section-title section-title--mint-bar">其他纸质材料</h3>
|
||||
<el-button type="primary" size="small" class="btn-main" @click="addPaperRow">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加纸质材料
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-if="paperRows.length"
|
||||
:data="paperRows"
|
||||
:row-key="(row) => row._rowKey"
|
||||
stripe
|
||||
class="list-table inner-table"
|
||||
max-height="280"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column label="名称" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.paperName"
|
||||
placeholder="请输入材料名称"
|
||||
maxlength="200"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="份数" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.copies"
|
||||
:min="1"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
controls-position="right"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件" min-width="320">
|
||||
<template #default="{ row, $index }">
|
||||
<div class="paper-att-cards">
|
||||
<div
|
||||
v-for="(att, attIdx) in row.attachments"
|
||||
:key="attIdx"
|
||||
class="paper-att-card"
|
||||
>
|
||||
<el-icon class="paper-att-card__icon"><Document /></el-icon>
|
||||
<span class="paper-att-card__name" :title="att.fileName">{{
|
||||
att.fileName || '附件'
|
||||
}}</span>
|
||||
<div class="paper-att-card__actions">
|
||||
<el-icon
|
||||
v-if="isImageFile(att)"
|
||||
class="paper-att-card__action"
|
||||
title="预览"
|
||||
@click="handlePreviewPaperAttachment(att)"
|
||||
>
|
||||
<View />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="paper-att-card__action"
|
||||
title="下载"
|
||||
@click="handleDownloadPaperAttachment(att)"
|
||||
>
|
||||
<Download />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="paper-att-card__action paper-att-card__action--danger"
|
||||
title="删除"
|
||||
@click="removePaperAttachment($index, attIdx)"
|
||||
>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<el-upload
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept="image/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.zip,.rar"
|
||||
@change="(file) => handlePaperAttachmentChange($index, file)"
|
||||
>
|
||||
<div
|
||||
class="paper-att-card paper-att-card--trigger"
|
||||
:class="{ 'is-loading': row._uploading }"
|
||||
>
|
||||
<el-icon class="paper-att-card__trigger-icon"><Plus /></el-icon>
|
||||
<span>{{ row._uploading ? '上传中' : '上传' }}</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="80" align="center" fixed="right">
|
||||
<template #default="{ $index }">
|
||||
<el-button type="danger" link size="small" @click="removePaperRow($index)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<p v-else class="section-empty-hint">点击「添加纸质材料」逐条填写名称、份数并上传附件;仅作台账留存,不扣库存</p>
|
||||
</section>
|
||||
|
||||
<el-form label-position="top" class="biz-dialog-form">
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
v-model="formRemark"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="可选"
|
||||
maxlength="512"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 细胞产品选择 -->
|
||||
<el-dialog
|
||||
v-model="cellSelectVisible"
|
||||
title="选择细胞产品"
|
||||
width="1200px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
style="backdrop-filter: none"
|
||||
>
|
||||
<el-input
|
||||
v-model="cellSearchKeyword"
|
||||
placeholder="搜索产品编码或名称"
|
||||
clearable
|
||||
style="margin-bottom: 12px"
|
||||
@input="loadCellProductOptions"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="list-body list-wrap">
|
||||
<el-table
|
||||
ref="cellSelectTableRef"
|
||||
class="list-table"
|
||||
:data="cellProductOptions"
|
||||
stripe
|
||||
row-key="id"
|
||||
height="380"
|
||||
v-loading="cellProductLoading"
|
||||
@selection-change="onCellSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
type="selection"
|
||||
min-width="45"
|
||||
align="center"
|
||||
:selectable="isCellRowSelectable"
|
||||
/>
|
||||
<el-table-column prop="productCode" label="产品编码" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="productName" label="产品名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="productTypeName" label="产品类型" min-width="90" />
|
||||
<el-table-column prop="passage" label="代次" min-width="70" align="center" />
|
||||
<el-table-column prop="storageSlotName" label="储物格" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="状态" min-width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="cellProductStatusTagType(row.productStatus) as any" size="small">
|
||||
{{ cellProductStatusLabel(row.productStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="batchNo" label="批次号" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="specName" label="规格" min-width="90" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="cellSelectVisible = false">取消</el-button>
|
||||
<el-button type="primary" :disabled="!cellSelectedRows.length" @click="confirmCellSelect">
|
||||
确定(已选 {{ cellSelectedRows.length }} 项)
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="previewVisible"
|
||||
title="文件预览"
|
||||
width="800px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
style="backdrop-filter: none"
|
||||
>
|
||||
<div v-if="previewType === 'image'" class="preview-container">
|
||||
<img
|
||||
:src="previewUrl"
|
||||
alt="预览"
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="preview-container">
|
||||
<video
|
||||
:src="previewUrl"
|
||||
controls
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
Plus,
|
||||
Delete,
|
||||
Search,
|
||||
Document,
|
||||
Download,
|
||||
View
|
||||
} from '@element-plus/icons-vue'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import { fetchCellProductList } from '@/api/cell-product'
|
||||
import { fetchQcMaterialOptions, type QcMaterialOption } from '@/api/qc-material'
|
||||
import type { OutboundReleaseCellRow } from '@/api/outbound-release-review'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
import { cellProductStatusLabel, cellProductStatusTagType } from '@/utils/status-tag-registry'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/** 出库交接等需扣库存场景:仅允许选择已冻存细胞,且已选行保留可选 */
|
||||
requireFrozenCellProduct?: boolean
|
||||
}>(),
|
||||
{
|
||||
requireFrozenCellProduct: false
|
||||
}
|
||||
)
|
||||
|
||||
type AttachmentLike = {
|
||||
fileName?: string
|
||||
fileUrl?: string
|
||||
attachmentUrl?: string
|
||||
fileType?: string
|
||||
}
|
||||
|
||||
type PaperAttachment = {
|
||||
attachmentUrl: string
|
||||
fileName: string
|
||||
fileType?: string
|
||||
}
|
||||
|
||||
type PaperRow = {
|
||||
_rowKey: string
|
||||
paperName: string
|
||||
copies?: number
|
||||
attachments: PaperAttachment[]
|
||||
_uploading?: boolean
|
||||
}
|
||||
|
||||
type MaterialRow = {
|
||||
_rowKey: string
|
||||
materialId: string
|
||||
materialName: string
|
||||
specModel: string
|
||||
unit: string
|
||||
qty?: number
|
||||
remark?: string
|
||||
}
|
||||
|
||||
defineOptions({ name: 'OutboundMaterialFormSections' })
|
||||
|
||||
const loading = ref(false)
|
||||
const formRemark = ref('')
|
||||
const cellRows = ref<OutboundReleaseCellRow[]>([])
|
||||
const materialRows = ref<MaterialRow[]>([])
|
||||
const paperRows = ref<PaperRow[]>([])
|
||||
const materialOptions = ref<QcMaterialOption[]>([])
|
||||
|
||||
const cellSelectVisible = ref(false)
|
||||
const cellSearchKeyword = ref('')
|
||||
const cellProductOptions = ref<Record<string, any>[]>([])
|
||||
const cellProductLoading = ref(false)
|
||||
const cellSelectedRows = ref<Record<string, any>[]>([])
|
||||
const cellSelectTableRef = ref()
|
||||
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const previewType = ref<'image' | 'video'>('image')
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
let materialRowSeq = 0
|
||||
let paperRowSeq = 0
|
||||
function nextMaterialRowKey() {
|
||||
materialRowSeq += 1
|
||||
return `mr_${Date.now()}_${materialRowSeq}`
|
||||
}
|
||||
function nextPaperRowKey() {
|
||||
paperRowSeq += 1
|
||||
return `pr_${Date.now()}_${paperRowSeq}`
|
||||
}
|
||||
|
||||
function fullFileUrl(url: string) {
|
||||
if (!url) return ''
|
||||
if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/')) {
|
||||
return url
|
||||
}
|
||||
return minioBaseUrl ? `${minioBaseUrl}/${url.replace(/^\//, '')}` : url
|
||||
}
|
||||
|
||||
function isImageFile(file: AttachmentLike) {
|
||||
const url = (file.fileUrl || file.attachmentUrl || '').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: AttachmentLike) {
|
||||
const url = (file.fileUrl || file.attachmentUrl || '').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 stockQtyDisplay(materialId: string) {
|
||||
const opt = materialOptions.value.find((o) => o.id === materialId)
|
||||
if (!opt || opt.stockQty == null) return '—'
|
||||
return String(opt.stockQty)
|
||||
}
|
||||
|
||||
function isMaterialOptionDisabled(materialId: string, currentIndex: number) {
|
||||
return materialRows.value.some((r, i) => i !== currentIndex && r.materialId === materialId)
|
||||
}
|
||||
|
||||
function onMaterialSelect(index: number, id: string) {
|
||||
const row = materialRows.value[index]
|
||||
if (!row) return
|
||||
if (!id) {
|
||||
row.materialId = ''
|
||||
row.materialName = ''
|
||||
row.specModel = ''
|
||||
row.unit = ''
|
||||
return
|
||||
}
|
||||
if (isMaterialOptionDisabled(id, index)) {
|
||||
ElMessage.warning('该物料已在列表中,请勿重复添加')
|
||||
row.materialId = ''
|
||||
return
|
||||
}
|
||||
const opt = materialOptions.value.find((o) => o.id === id)
|
||||
row.materialId = id
|
||||
row.materialName = opt?.materialName || ''
|
||||
row.specModel = opt?.specModel || ''
|
||||
row.unit = opt?.unit || ''
|
||||
}
|
||||
|
||||
function addMaterialRow() {
|
||||
materialRows.value.push({
|
||||
_rowKey: nextMaterialRowKey(),
|
||||
materialId: '',
|
||||
materialName: '',
|
||||
specModel: '',
|
||||
unit: '',
|
||||
qty: undefined
|
||||
})
|
||||
}
|
||||
|
||||
function removeMaterialRow(index: number) {
|
||||
materialRows.value.splice(index, 1)
|
||||
}
|
||||
|
||||
function addPaperRow() {
|
||||
paperRows.value.push({
|
||||
_rowKey: nextPaperRowKey(),
|
||||
paperName: '',
|
||||
copies: 1,
|
||||
attachments: []
|
||||
})
|
||||
}
|
||||
|
||||
function removePaperRow(index: number) {
|
||||
paperRows.value.splice(index, 1)
|
||||
}
|
||||
|
||||
function removePaperAttachment(rowIndex: number, attachmentIndex: number) {
|
||||
const row = paperRows.value[rowIndex]
|
||||
if (!row) return
|
||||
row.attachments.splice(attachmentIndex, 1)
|
||||
}
|
||||
|
||||
function attachmentFileName(url: string) {
|
||||
return url.split('/').pop() || '附件'
|
||||
}
|
||||
|
||||
function parseAttachmentList(urlStr: string): PaperAttachment[] {
|
||||
if (!urlStr?.trim()) return []
|
||||
return urlStr
|
||||
.split(',')
|
||||
.map((u) => u.trim())
|
||||
.filter(Boolean)
|
||||
.map((url) => ({
|
||||
attachmentUrl: url,
|
||||
fileName: attachmentFileName(url)
|
||||
}))
|
||||
}
|
||||
|
||||
function mapPaperRowsFromApi(
|
||||
paperMaterials: Array<{
|
||||
paperName?: string
|
||||
copies?: number
|
||||
attachmentUrl?: string
|
||||
}> = [],
|
||||
legacyUrls = ''
|
||||
) {
|
||||
if (paperMaterials.length) {
|
||||
return paperMaterials.map((p) => ({
|
||||
_rowKey: nextPaperRowKey(),
|
||||
paperName: p.paperName || '',
|
||||
copies: p.copies && p.copies > 0 ? p.copies : 1,
|
||||
attachments: parseAttachmentList(p.attachmentUrl || '')
|
||||
}))
|
||||
}
|
||||
if (!legacyUrls.trim()) return []
|
||||
return legacyUrls
|
||||
.split(',')
|
||||
.map((u) => u.trim())
|
||||
.filter(Boolean)
|
||||
.map((attachmentUrl) => ({
|
||||
_rowKey: nextPaperRowKey(),
|
||||
paperName: attachmentFileName(attachmentUrl),
|
||||
copies: 1,
|
||||
attachments: [{ attachmentUrl, fileName: attachmentFileName(attachmentUrl) }]
|
||||
}))
|
||||
}
|
||||
|
||||
function removeCellRow(index: number) {
|
||||
cellRows.value.splice(index, 1)
|
||||
}
|
||||
|
||||
function isCellRowSelectable(row: Record<string, any>) {
|
||||
const ids = new Set(cellRows.value.map((c) => c.cellProductId))
|
||||
if (ids.has(row.id)) return false
|
||||
if (props.requireFrozenCellProduct && row.productStatus && row.productStatus !== 'frozen') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function onCellSelectionChange(rows: Record<string, any>[]) {
|
||||
cellSelectedRows.value = rows
|
||||
}
|
||||
|
||||
function openCellSelect() {
|
||||
cellSearchKeyword.value = ''
|
||||
cellSelectedRows.value = []
|
||||
cellSelectVisible.value = true
|
||||
loadCellProductOptions()
|
||||
}
|
||||
|
||||
async function loadCellProductOptions() {
|
||||
cellProductLoading.value = true
|
||||
try {
|
||||
const res = await fetchCellProductList({
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
keyword: cellSearchKeyword.value?.trim() || undefined,
|
||||
productStatus: props.requireFrozenCellProduct ? 'frozen' : undefined
|
||||
})
|
||||
cellProductOptions.value = res.list || []
|
||||
} finally {
|
||||
cellProductLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function confirmCellSelect() {
|
||||
if (!cellSelectedRows.value.length) return
|
||||
const existing = new Set(cellRows.value.map((c) => c.cellProductId))
|
||||
let added = 0
|
||||
for (const row of cellSelectedRows.value) {
|
||||
if (existing.has(row.id)) continue
|
||||
cellRows.value.push({
|
||||
cellProductId: row.id,
|
||||
productCode: row.productCode || '',
|
||||
productName: row.productName || '',
|
||||
productTypeName: row.productTypeName || '',
|
||||
specName: row.specName || '',
|
||||
passage: row.passage || '',
|
||||
batchNo: row.batchNo || '',
|
||||
storageSlotName: row.storageSlotName || '',
|
||||
productStatus: row.productStatus || ''
|
||||
})
|
||||
existing.add(row.id)
|
||||
added += 1
|
||||
}
|
||||
if (added) {
|
||||
cellSelectVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFromApiData(data: {
|
||||
remark?: string
|
||||
cells?: OutboundReleaseCellRow[]
|
||||
qcMaterials?: Array<{
|
||||
qcMaterialId?: string
|
||||
materialName?: string
|
||||
specModel?: string
|
||||
unit?: string
|
||||
qty?: number
|
||||
remark?: string
|
||||
}>
|
||||
paperMaterials?: Array<{
|
||||
paperName?: string
|
||||
copies?: number
|
||||
attachmentUrl?: string
|
||||
}>
|
||||
paperAttachments?: string
|
||||
}) {
|
||||
formRemark.value = data.remark || ''
|
||||
cellRows.value = (data.cells || []).map((c) => ({ ...c }))
|
||||
materialRows.value = (data.qcMaterials || []).map((m) => ({
|
||||
_rowKey: nextMaterialRowKey(),
|
||||
materialId: m.qcMaterialId || '',
|
||||
materialName: m.materialName || '',
|
||||
specModel: m.specModel || '',
|
||||
unit: m.unit || '',
|
||||
qty: m.qty,
|
||||
remark: m.remark || ''
|
||||
}))
|
||||
paperRows.value = mapPaperRowsFromApi(data.paperMaterials, data.paperAttachments)
|
||||
}
|
||||
|
||||
async function ensureMaterialOptions() {
|
||||
if (materialOptions.value.length) return
|
||||
try {
|
||||
materialOptions.value = (await fetchQcMaterialOptions()) || []
|
||||
} catch {
|
||||
materialOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function validateMaterial() {
|
||||
if (paperRows.value.some((r) => r._uploading)) {
|
||||
ElMessage.warning('纸质材料附件上传中,请稍候')
|
||||
return false
|
||||
}
|
||||
for (const row of materialRows.value) {
|
||||
if (!row.materialId) {
|
||||
ElMessage.warning('请为每条物料选择名称')
|
||||
return false
|
||||
}
|
||||
const qty = row.qty
|
||||
if (qty == null || !Number.isFinite(Number(qty)) || Number(qty) <= 0) {
|
||||
ElMessage.warning('物料计划用量须大于 0')
|
||||
return false
|
||||
}
|
||||
}
|
||||
for (const row of paperRows.value) {
|
||||
if (!row.paperName?.trim()) {
|
||||
ElMessage.warning('请填写每条纸质材料的名称')
|
||||
return false
|
||||
}
|
||||
const copies = row.copies
|
||||
if (copies == null || !Number.isFinite(Number(copies)) || Number(copies) <= 0) {
|
||||
ElMessage.warning('纸质材料份数须大于 0')
|
||||
return false
|
||||
}
|
||||
if (!row.attachments.length) {
|
||||
ElMessage.warning('请为每条纸质材料上传附件')
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function buildSavePayload() {
|
||||
return {
|
||||
remark: formRemark.value.trim() || undefined,
|
||||
paperMaterials: paperRows.value.map((p) => ({
|
||||
paperName: p.paperName.trim(),
|
||||
copies: Number(p.copies),
|
||||
attachmentUrl: p.attachments.map((a) => a.attachmentUrl).join(',')
|
||||
})),
|
||||
cells: cellRows.value.map((c) => ({
|
||||
cellProductId: String(c.cellProductId || ''),
|
||||
remark: c.remark
|
||||
})),
|
||||
qcMaterials: materialRows.value.map((m) => ({
|
||||
materialId: m.materialId,
|
||||
qty: Number(m.qty),
|
||||
remark: m.remark
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
ensureMaterialOptions()
|
||||
})
|
||||
|
||||
function resetState() {
|
||||
cellRows.value = []
|
||||
materialRows.value = []
|
||||
paperRows.value = []
|
||||
formRemark.value = ''
|
||||
cellSelectVisible.value = false
|
||||
}
|
||||
|
||||
function handlePreviewPaperAttachment(att: PaperAttachment) {
|
||||
if (!att.attachmentUrl) return
|
||||
previewUrl.value = fullFileUrl(att.attachmentUrl)
|
||||
previewType.value = isVideoFile(att) ? 'video' : 'image'
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
function handleDownloadPaperAttachment(att: PaperAttachment) {
|
||||
if (!att.attachmentUrl) return
|
||||
downloadAttachmentFile(
|
||||
{
|
||||
fileName: att.fileName,
|
||||
fileUrl: att.attachmentUrl,
|
||||
fileType: att.fileType || ''
|
||||
},
|
||||
{ fullFileUrl }
|
||||
)
|
||||
}
|
||||
|
||||
async function handlePaperAttachmentChange(
|
||||
index: number,
|
||||
uploadFile: { raw?: File }
|
||||
) {
|
||||
const file = uploadFile.raw
|
||||
const row = paperRows.value[index]
|
||||
if (!file || !row || row._uploading) return
|
||||
row._uploading = true
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('folder', 'outbound-release-material')
|
||||
fd.append('file', file)
|
||||
const res = await fetchUploadAttachment(fd)
|
||||
row.attachments.push({
|
||||
attachmentUrl: res.fileUrl,
|
||||
fileName: file.name || res.fileName || attachmentFileName(res.fileUrl),
|
||||
fileType: res.fileType || file.type || ''
|
||||
})
|
||||
} catch {
|
||||
ElMessage.error('文件上传失败')
|
||||
} finally {
|
||||
row._uploading = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
loading,
|
||||
loadFromApiData,
|
||||
resetState,
|
||||
validateMaterial,
|
||||
buildSavePayload,
|
||||
ensureMaterialOptions
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.material-summary {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
background: #f8fafc;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px 20px;
|
||||
}
|
||||
|
||||
.material-summary-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.material-summary-label {
|
||||
color: #64748b;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.material-summary-value {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.material-sections {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px 16px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.section-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.section-empty-hint {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.inner-cell-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.paper-att-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.paper-att-card {
|
||||
width: 100px;
|
||||
height: 72px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
background: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.paper-att-card__icon {
|
||||
color: #0284c7;
|
||||
font-size: 18px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.paper-att-card__name {
|
||||
width: 100%;
|
||||
font-size: 11px;
|
||||
color: #334155;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.paper-att-card__actions {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.paper-att-card:hover .paper-att-card__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.paper-att-card__action {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
&--danger:hover {
|
||||
background: #ef4444;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card--trigger {
|
||||
border: 1px dashed #a7f3d0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: #10b981;
|
||||
background: #ecfdf5;
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card__trigger-icon {
|
||||
font-size: 16px;
|
||||
color: #10b981;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.paper-att-card--trigger span {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -103,6 +103,8 @@
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
taskId?: string
|
||||
/** 不展示样本信息、运输信息、样本接收(出库放行审核等) */
|
||||
hideSampleContext?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -265,9 +267,10 @@
|
||||
}
|
||||
]
|
||||
: []
|
||||
const sampleSections: ViewSection[] = isTempQc
|
||||
? []
|
||||
: [
|
||||
const sampleSections: ViewSection[] =
|
||||
props.hideSampleContext || isTempQc
|
||||
? []
|
||||
: [
|
||||
{
|
||||
title: '样本信息',
|
||||
fields: [
|
||||
|
||||
@@ -84,7 +84,11 @@
|
||||
</template>
|
||||
|
||||
<Teleport to="body">
|
||||
<QcTaskViewDialog v-model:visible="taskViewVisible" :task-id="taskViewId" />
|
||||
<QcTaskViewDialog
|
||||
v-model:visible="taskViewVisible"
|
||||
:task-id="taskViewId"
|
||||
:hide-sample-context="hideSampleContext"
|
||||
/>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
@@ -144,6 +148,8 @@
|
||||
const props = defineProps<{
|
||||
qcInfo?: SampleQcInfo | null
|
||||
sampleReception?: SampleReceptionBrief | null
|
||||
/** 任务详情弹窗不展示样本信息、样本接收(出库放行等场景) */
|
||||
hideSampleContext?: boolean
|
||||
}>()
|
||||
|
||||
const taskViewVisible = ref(false)
|
||||
|
||||
@@ -45,12 +45,12 @@
|
||||
<dt>{{ field.label }}</dt>
|
||||
<dd>
|
||||
<el-tag v-if="field.tagType" :type="field.tagType" size="small">{{
|
||||
field.value ?? '—'
|
||||
formatViewValue(field.value)
|
||||
}}</el-tag>
|
||||
<ul v-else-if="field.items?.length" class="view-plain-list">
|
||||
<li v-for="(item, idx) in field.items" :key="idx">{{ item }}</li>
|
||||
</ul>
|
||||
<template v-else>{{ field.value ?? '—' }}</template>
|
||||
<template v-else>{{ formatViewValue(field.value) }}</template>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@@ -84,6 +84,13 @@
|
||||
return val != null && (typeof val === 'object' || typeof val === 'function')
|
||||
}
|
||||
|
||||
/** 查看弹窗字段展示:null/undefined/空字符串 → —;布尔转是/否 */
|
||||
function formatViewValue(val: unknown): string {
|
||||
if (val === null || val === undefined || val === '') return '—'
|
||||
if (typeof val === 'boolean') return val ? '是' : '否'
|
||||
return String(val)
|
||||
}
|
||||
|
||||
export type TagType = 'success' | 'warning' | 'danger' | 'info' | '' | 'primary'
|
||||
|
||||
export interface ViewField {
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin_ts", "label": "admin.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L1"}, {"id": "api_admin_fetchgetmenulist", "label": "fetchGetMenuList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L10"}, {"id": "api_admin_fetchgetrolelist", "label": "fetchGetRoleList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L19"}, {"id": "api_admin_fetchuseroptions", "label": "fetchUserOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L26"}, {"id": "api_admin_fetchroleoptions", "label": "fetchRoleOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L33"}, {"id": "api_admin_fetchtenantlist", "label": "fetchTenantList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L43"}, {"id": "api_admin_fetchcreatetenant", "label": "fetchCreateTenant()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L56"}, {"id": "api_admin_fetchupdatetenant", "label": "fetchUpdateTenant()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L71"}, {"id": "api_admin_fetchdeletetenant", "label": "fetchDeleteTenant()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L89"}, {"id": "api_admin_fetchuserlist", "label": "fetchUserList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L100"}, {"id": "api_admin_fetchuserdetail", "label": "fetchUserDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L119"}, {"id": "api_admin_fetchcreateuser", "label": "fetchCreateUser()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L125"}, {"id": "api_admin_fetchupdateuser", "label": "fetchUpdateUser()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L147"}, {"id": "api_admin_fetchdeleteuser", "label": "fetchDeleteUser()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L170"}, {"id": "api_admin_fetchresetpassword", "label": "fetchResetPassword()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L177"}, {"id": "api_admin_fetchuploaduseravatar", "label": "fetchUploadUserAvatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L185"}, {"id": "api_admin_fetchregionlist", "label": "fetchRegionList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L196"}, {"id": "api_admin_fetchactiveregions", "label": "fetchActiveRegions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L214"}, {"id": "api_admin_fetchnotdeletedregions", "label": "fetchNotDeletedRegions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L231"}, {"id": "api_admin_fetchcategorylist", "label": "fetchCategoryList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L259"}, {"id": "api_admin_fetchcreatecategory", "label": "fetchCreateCategory()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L277"}, {"id": "api_admin_fetchupdatecategory", "label": "fetchUpdateCategory()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L290"}, {"id": "api_admin_fetchdeletecategory", "label": "fetchDeleteCategory()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L307"}, {"id": "api_admin_fetchuploadcategoryimage", "label": "fetchUploadCategoryImage()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L313"}, {"id": "api_admin_fetchunitlist", "label": "fetchUnitList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L324"}, {"id": "api_admin_fetchcreateunit", "label": "fetchCrLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_outbound_material_form_sections_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/outbound-material-form-sections/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry_ts", "label": "status-tag-registry.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L1"}, {"id": "utils_status_tag_registry_createlabelgetter", "label": "createLabelGetter()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L29"}, {"id": "utils_status_tag_registry_createtagtypegetter", "label": "createTagTypeGetter()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L33"}, {"id": "utils_status_tag_registry_txntypelabel", "label": "txnTypeLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L381"}, {"id": "utils_status_tag_registry_txntypetagtype", "label": "txnTypeTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L392"}, {"id": "utils_status_tag_registry_txnstatuslabel", "label": "txnStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L404"}, {"id": "utils_status_tag_registry_txnstatustagtype", "label": "txnStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L414"}, {"id": "utils_status_tag_registry_applicationstatuslabel", "label": "applicationStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L425"}, {"id": "utils_status_tag_registry_applicationstatustagtype", "label": "applicationStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L429"}, {"id": "utils_status_tag_registry_checkupstatuslabel", "label": "checkupStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L442"}, {"id": "utils_status_tag_registry_checkupstatustagtype", "label": "checkupStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L450"}, {"id": "utils_status_tag_registry_feestatuslabel", "label": "feeStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L458"}, {"id": "utils_status_tag_registry_feestatustagtype", "label": "feeStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L466"}, {"id": "utils_status_tag_registry_schedulestatuslabel", "label": "scheduleStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L475"}, {"id": "utils_status_tag_registry_schedulestatustagtype", "label": "scheduleStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L484"}, {"id": "utils_status_tag_registry_infusionstatuslabel", "label": "infusionStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L495"}, {"id": "utils_status_tag_registry_infusionstatustagtype", "label": "infusionStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L504"}, {"id": "utils_status_tag_registry_infusionmethodlabel", "label": "infusionMethodLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L515"}, {"id": "utils_status_tag_registry_qcstatuslabel", "label": "qcStatusLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L526"}, {"id": "utils_status_tag_registry_qcstatustagtype", "label": "qcStatusTagType()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L530"}, {"id": "utils_status_tag_registry_releasereviewconclusionlabel", "label": "releaseReviewConclusionLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L536"}, {"id": "utils_status_tag_registry_releasereviewconclusiontagtype", "label": "releaseReviewConclusionTagTypeLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "label": "InfusionScheduleHandoverDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L1"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_formatnowdatetime", "label": "formatNowDatetime()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L158"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_formathandoveruserlabel", "label": "formatHandoverUserLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L164"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_applydefaulthandoveruser", "label": "applyDefaultHandoverUser()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L174"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_resethandoverform", "label": "resetHandoverForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L193"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_remotesearchhandoverusers", "label": "remoteSearchHandoverUsers()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L203"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_onhandoveruserchange", "label": "onHandoverUserChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L232"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L115", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L116", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_infusion_schedule_handover", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L117", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_outbound_material_form_sections_index_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L121", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L122", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "infusion_schedule_management_infusionschedulehandoverdialog_formatnowdatetime", "relation": "contains", "confidence": "EXTRACTED", "source_file": "Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1"}, {"id": "qc_work_order_index_formatconfiguredlabitems", "label": "formatConfiguredLabItems()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1034"}, {"id": "qc_work_order_index_formatconfiguredthirditems", "label": "formatConfiguredThirdItems()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1040"}, {"id": "qc_work_order_index_buildtasktypesections", "label": "buildTaskTypeSections()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1051"}, {"id": "qc_work_order_index_buildconfiguredonlysection", "label": "buildConfiguredOnlySection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1109"}, {"id": "qc_work_order_index_formatdetectmethods", "label": "formatDetectMethods()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1127"}, {"id": "qc_work_order_index_formatmetric", "label": "formatMetric()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1134"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formatconfiguredlabitems", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1034", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formatconfiguredthirditems", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1040", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_buildtasktypesections", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1051", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_buildconfiguredonlysection", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1109", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formatdetectmethods", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1127", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formatmetric", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1134", "weight": 1.0}], "raw_calls": [{"caller_nid": "qc_work_order_index_formatconfiguredlabitems", "callee": "join", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1036"}, {"caller_nid": "qc_work_order_index_formatconfiguredlabitems", "callee": "map", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1036"}, {"caller_nid": "qc_work_order_index_formatconfiguredthirditems", "callee": "join", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1042"}, {"caller_nid": "qc_work_order_index_formatconfiguredthirditems", "callee": "map", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1042"}, {"caller_nid": "qc_wLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_sample_qc_info_cards_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/sample-qc-info-cards/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleaseviewdialog_vue", "label": "OutboundReleaseViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L1"}, {"id": "outbound_release_review_outboundreleaseviewdialog_handleattachmentpreview", "label": "handleAttachmentPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L366"}, {"id": "outbound_release_review_outboundreleaseviewdialog_handleattachmentdownload", "label": "handleAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L371"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleaseviewdialog_vue", "target": "outbound_release_review_outboundreleaseviewdialog_handleattachmentpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L366", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleaseviewdialog_vue", "target": "outbound_release_review_outboundreleaseviewdialog_handleattachmentdownload", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L371", "weight": 1.0}], "raw_calls": [{"caller_nid": "outbound_release_review_outboundreleaseviewdialog_handleattachmentpreview", "callee": "attachmentFullUrl", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L367"}, {"caller_nid": "outbound_release_review_outboundreleaseviewdialog_handleattachmentpreview", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L367"}, {"caller_nid": "outbound_release_review_outboundreleaseviewdialog_handleattachmentpreview", "callee": "open", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L368"}, {"caller_nid": "outbound_release_review_outboundreleaseviewdialog_handleattachmentdownload", "callee": "downloadAttachmentFile", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L372"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_outbound_material_form_sections_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/outbound-material-form-sections/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleasematerialdialog_vue", "label": "OutboundReleaseMaterialDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseMaterialDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleaseviewdialog_vue", "label": "OutboundReleaseViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseViewDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_scheduleviewdialog_vue", "label": "ScheduleViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/ScheduleViewDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_router_modules_sqxp_ts", "label": "sqxp.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_router_modules_sqxp_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_types_router", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/router/modules/sqxp.ts", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleasematerialdialog_vue", "label": "OutboundReleaseMaterialDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseMaterialDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_infusion_schedule_outbound_chain_view_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/infusion-schedule-outbound-chain-view/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "label": "InfusionScheduleHandoverDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L1"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_formatnowdatetime", "label": "formatNowDatetime()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L158"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_formathandoveruserlabel", "label": "formatHandoverUserLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L164"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_applydefaulthandoveruser", "label": "applyDefaultHandoverUser()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L174"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_resethandoverform", "label": "resetHandoverForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L193"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_remotesearchhandoverusers", "label": "remoteSearchHandoverUsers()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L203"}, {"id": "infusion_schedule_management_infusionschedulehandoverdialog_onhandoveruserchange", "label": "onHandoverUserChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L232"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L115", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L116", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_infusion_schedule_handover", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L117", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_outbound_material_form_sections_index_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L121", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/InfusionScheduleHandoverDialog.vue", "source_location": "L122", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_infusionschedulehandoverdialog_vue", "target": "infusion_schedule_management_infusionschedulehandoverdialog_formatnowdatetime", "relation": "contains", "confidence": "EXTRACTED", "source_file": "Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_infusion_schedule_handover_ts", "label": "infusion-schedule-handover.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts", "source_location": "L1"}, {"id": "api_infusion_schedule_handover_fetchinfusionscheduleoutboundhandover", "label": "fetchInfusionScheduleOutboundHandover()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts", "source_location": "L32"}, {"id": "api_infusion_schedule_handover_saveinfusionscheduleoutboundhandover", "label": "saveInfusionScheduleOutboundHandover()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts", "source_location": "L38"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_infusion_schedule_handover_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_infusion_schedule_handover_ts", "target": "api_infusion_schedule_handover_fetchinfusionscheduleoutboundhandover", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_infusion_schedule_handover_ts", "target": "api_infusion_schedule_handover_saveinfusionscheduleoutboundhandover", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts", "source_location": "L38", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_infusion_schedule_handover_fetchinfusionscheduleoutboundhandover", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts", "source_location": "L33"}, {"caller_nid": "api_infusion_schedule_handover_saveinfusionscheduleoutboundhandover", "callee": "put", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/infusion-schedule-handover.ts", "source_location": "L56"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_system_view_dialog_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/system-view-dialog/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_qc_task_view_dialog_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L1"}, {"id": "qc_task_view_dialog_index_attachmentfullurl", "label": "attachmentFullUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L396"}, {"id": "qc_task_view_dialog_index_handleattachmentpreview", "label": "handleAttachmentPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L403"}, {"id": "qc_task_view_dialog_index_handleattachmentdownload", "label": "handleAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L408"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_qc_task_view_dialog_index_vue", "target": "qc_task_view_dialog_index_attachmentfullurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L396", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_qc_task_view_dialog_index_vue", "target": "qc_task_view_dialog_index_handleattachmentpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L403", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_qc_task_view_dialog_index_vue", "target": "qc_task_view_dialog_index_handleattachmentdownload", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L408", "weight": 1.0}, {"source": "qc_task_view_dialog_index_handleattachmentpreview", "target": "qc_task_view_dialog_index_attachmentfullurl", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L404", "weight": 1.0}], "raw_calls": [{"caller_nid": "qc_task_view_dialog_index_attachmentfullurl", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L397"}, {"caller_nid": "qc_task_view_dialog_index_attachmentfullurl", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L397"}, {"caller_nid": "qc_task_view_dialog_index_attachmentfullurl", "callee": "test", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L399"}, {"caller_nid": "qc_task_view_dialog_index_attachmentfullurl", "callee": "replace", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L400"}, {"caller_nid": "qc_task_view_dialog_index_handleattachmentpreview", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L404"}, {"caller_nid": "qc_task_view_dialog_index_handleattachmentpreview", "callee": "open", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L405"}, {"caller_nid": "qc_task_view_dialog_index_handleattachmentdownload", "callee": "downloadAttachmentFile", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L409"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_pilot_application_applicationviewdialog_vue", "label": "ApplicationViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue", "source_location": "L1"}, {"id": "pilot_application_applicationviewdialog_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue", "source_location": "L951"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_pilot_application_applicationviewdialog_vue", "target": "pilot_application_applicationviewdialog_for", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue", "source_location": "L951", "weight": 1.0}], "raw_calls": [{"caller_nid": "pilot_application_applicationviewdialog_for", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue", "source_location": "L952"}, {"caller_nid": "pilot_application_applicationviewdialog_for", "callee": "Number", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue", "source_location": "L955"}, {"caller_nid": "pilot_application_applicationviewdialog_for", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue", "source_location": "L956"}, {"caller_nid": "pilot_application_applicationviewdialog_for", "callee": "set", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/ApplicationViewDialog.vue", "source_location": "L958"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleasereviewdialog_vue", "label": "OutboundReleaseReviewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L1"}, {"id": "outbound_release_review_outboundreleasereviewdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L235"}, {"id": "outbound_release_review_outboundreleasereviewdialog_isimagefile", "label": "isImageFile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L243"}, {"id": "outbound_release_review_outboundreleasereviewdialog_isvideofile", "label": "isVideoFile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L253"}, {"id": "outbound_release_review_outboundreleasereviewdialog_removeattachment", "label": "removeAttachment()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L263"}, {"id": "outbound_release_review_outboundreleasereviewdialog_handlepreviewattachment", "label": "handlePreviewAttachment()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L267"}, {"id": "outbound_release_review_outboundreleasereviewdialog_handledownloadattachment", "label": "handleDownloadAttachment()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L274"}, {"id": "outbound_release_review_outboundreleasereviewdialog_resetform", "label": "resetForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L332"}, {"id": "outbound_release_review_outboundreleasereviewdialog_handlesubmit", "label": "handleSubmit()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L341"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleasereviewdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L170", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleasereviewdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L171", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleasereviewdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_business", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L181", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleasereviewdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_download_attachment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L182", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_outbound_release_review_outboundreleasereviewdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/outbound-release-review/OutboundReleaseReviewDialog.vue", "source_location": "L183", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_scheduleviewdialog_vue", "label": "ScheduleViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/ScheduleViewDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_pilot_application_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L1"}, {"id": "pilot_application_index_handleselectionchange", "label": "handleSelectionChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L342"}, {"id": "pilot_application_index_cleartableselection", "label": "clearTableSelection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L346"}, {"id": "pilot_application_index_loaddata", "label": "loadData()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L365"}, {"id": "pilot_application_index_handlesearch", "label": "handleSearch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L386"}, {"id": "pilot_application_index_handlereset", "label": "handleReset()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L391"}, {"id": "pilot_application_index_handleview", "label": "handleView()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L403"}, {"id": "pilot_application_index_handlecreate", "label": "handleCreate()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L412"}, {"id": "pilot_application_index_caneditapplication", "label": "canEditApplication()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L418"}, {"id": "pilot_application_index_handleedit", "label": "handleEdit()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L420"}, {"id": "pilot_application_index_onformsaved", "label": "onFormSaved()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L425"}, {"id": "pilot_application_index_handledelete", "label": "handleDelete()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L430"}, {"id": "pilot_application_index_runbatchstatusaction", "label": "runBatchStatusAction()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L443"}, {"id": "pilot_application_index_handlebatchstart", "label": "handleBatchStart()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L511"}, {"id": "pilot_application_index_handlebatchcomplete", "label": "handleBatchComplete()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L520"}, {"id": "pilot_application_index_handlebatchvoid", "label": "handleBatchVoid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L529"}, {"id": "pilot_application_index_handlestart", "label": "handleStart()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L539"}, {"id": "pilot_application_index_handlecomplete", "label": "handleComplete()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L556"}, {"id": "pilot_application_index_handlevoid", "label": "handleVoid()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L573"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_pilot_application_index_vue", "target": "pilot_application_index_handleselectionchange", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/pilot-application/index.vue", "source_location": "L342", "weight": 1.0}, {"sLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "label": "outbound-release-review.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L1"}, {"id": "api_outbound_release_review_fetchoutboundreleasereviewlist", "label": "fetchOutboundReleaseReviewList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L154"}, {"id": "api_outbound_release_review_fetchoutboundreleasereviewdetail", "label": "fetchOutboundReleaseReviewDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L168"}, {"id": "api_outbound_release_review_submitoutboundreleasereview", "label": "submitOutboundReleaseReview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L174"}, {"id": "api_outbound_release_review_batchreviewoutboundrelease", "label": "batchReviewOutboundRelease()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L188"}, {"id": "api_outbound_release_review_fetchoutboundreleasematerial", "label": "fetchOutboundReleaseMaterial()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L200"}, {"id": "api_outbound_release_review_saveoutboundreleasematerial", "label": "saveOutboundReleaseMaterial()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L206"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_work_order", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_fetchoutboundreleasereviewlist", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L154", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_fetchoutboundreleasereviewdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L168", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_submitoutboundreleasereview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L174", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_batchreviewoutboundrelease", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L188", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_fetchoutboundreleasematerial", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L200", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_outbound_release_review_ts", "target": "api_outbound_release_review_saveoutboundreleasematerial", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L206", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_outbound_release_review_fetchoutboundreleasereviewlist", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/outbound-release-review.ts", "source_location": "L162"}, {"caller_nid": "api_outbound_release_review_fetchoutboundreleasereviewdetail", "callee": "get", "source_file": "/Users/wLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/index.vue", "source_location": "L1"}, {"id": "infusion_schedule_management_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/index.vue", "source_location": "L489"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_infusion_schedule_management_index_vue", "target": "infusion_schedule_management_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/infusion-schedule-management/index.vue", "source_location": "L489", "weight": 1.0}], "raw_calls": []}
|
||||
@@ -728,6 +728,17 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
|
||||
keepAlive: true,
|
||||
menuCode: 'pilot-service:infusion-schedule-management:list'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '80000000000000000000000000000013',
|
||||
name: 'OutboundReleaseReview',
|
||||
path: 'outbound-release-review',
|
||||
component: '/pilot-service/outbound-release-review/index',
|
||||
meta: {
|
||||
title: '出库放行审核',
|
||||
keepAlive: true,
|
||||
menuCode: 'pilot-service:outbound-release-review:list'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -577,3 +577,27 @@ export function productStatusTagType(status: string): string {
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
|
||||
// ========== 细胞产品库 - 产品状态 ==========
|
||||
export function cellProductStatusLabel(status: string): string {
|
||||
const map: Record<string, string> = {
|
||||
frozen: '已冻存',
|
||||
in_production: '生产中',
|
||||
sold: '已售卖',
|
||||
used: '已使用',
|
||||
voided: '已作废',
|
||||
thawed: '已复苏'
|
||||
}
|
||||
return map[status] || status || '—'
|
||||
}
|
||||
export function cellProductStatusTagType(status: string): string {
|
||||
const map: Record<string, string> = {
|
||||
frozen: 'success',
|
||||
in_production: 'warning',
|
||||
sold: 'warning',
|
||||
used: 'info',
|
||||
voided: 'danger',
|
||||
thawed: 'info'
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
<!-- 输注计划管理 — 出库交接 -->
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
title="出库交接"
|
||||
width="1120px"
|
||||
class="dialog-form-layout outbound-handover-dialog"
|
||||
align-center
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
style="backdrop-filter: none"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<div v-if="scheduleSummary" class="material-summary">
|
||||
<div class="material-summary-row">
|
||||
<span class="material-summary-label">申请单号</span>
|
||||
<span class="material-summary-value">{{ scheduleSummary.applicationNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="material-summary-row">
|
||||
<span class="material-summary-label">患者姓名</span>
|
||||
<span class="material-summary-value">{{ scheduleSummary.patientName || '—' }}</span>
|
||||
</div>
|
||||
<div class="material-summary-row">
|
||||
<span class="material-summary-label">输注时间</span>
|
||||
<span class="material-summary-value">{{ scheduleSummary.infusionTime || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-loading="loading" class="handover-body">
|
||||
<OutboundMaterialFormSections ref="materialSectionsRef" require-frozen-cell-product />
|
||||
|
||||
<section class="section-card handover-form-section">
|
||||
<h3 class="section-title section-title--mint-bar">交接信息</h3>
|
||||
<el-form label-position="top" class="biz-dialog-form">
|
||||
<div class="form-grid-2">
|
||||
<el-form-item label="交接人" required>
|
||||
<el-select
|
||||
v-model="handoverUserId"
|
||||
filterable
|
||||
remote
|
||||
clearable
|
||||
reserve-keyword
|
||||
placeholder="搜索并选择用户"
|
||||
:remote-method="remoteSearchHandoverUsers"
|
||||
:loading="handoverUserLoading"
|
||||
class="w-full"
|
||||
@change="onHandoverUserChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="u in handoverUserOptions"
|
||||
:key="u.id"
|
||||
:label="formatHandoverUserLabel(u)"
|
||||
:value="u.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="交接时间" required>
|
||||
<el-date-picker
|
||||
v-model="handoverTime"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择交接时间"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="交接地点">
|
||||
<el-input
|
||||
v-model="handoverLocation"
|
||||
placeholder="可选"
|
||||
maxlength="200"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="接收人" required>
|
||||
<el-input
|
||||
v-model="receiverPerson"
|
||||
placeholder="请输入接收人"
|
||||
maxlength="200"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" class="form-col-span-2">
|
||||
<el-input
|
||||
v-model="handoverRemark"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="可选"
|
||||
maxlength="512"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="emit('update:modelValue', false)">
|
||||
<el-icon><Close /></el-icon>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="submitting" @click="handleSubmit">
|
||||
<el-icon><Check /></el-icon>
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Check, Close } from '@element-plus/icons-vue'
|
||||
import { fetchUserList } from '@/api/admin'
|
||||
import {
|
||||
fetchInfusionScheduleOutboundHandover,
|
||||
saveInfusionScheduleOutboundHandover
|
||||
} from '@/api/infusion-schedule-handover'
|
||||
import OutboundMaterialFormSections from '@/components/business/outbound-material-form-sections/index.vue'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
defineOptions({ name: 'InfusionScheduleHandoverDialog' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
scheduleId: string
|
||||
scheduleRow?: Record<string, any> | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', val: boolean): void
|
||||
(e: 'saved'): void
|
||||
}>()
|
||||
|
||||
const userStore = useUserStore()
|
||||
const materialSectionsRef = ref<InstanceType<typeof OutboundMaterialFormSections> | null>(null)
|
||||
|
||||
const loading = ref(false)
|
||||
const submitting = ref(false)
|
||||
const scheduleSummary = ref<{
|
||||
applicationNo?: string
|
||||
patientName?: string
|
||||
infusionTime?: string
|
||||
} | null>(null)
|
||||
|
||||
const handoverUserId = ref('')
|
||||
const handoverTime = ref('')
|
||||
const handoverLocation = ref('')
|
||||
const receiverPerson = ref('')
|
||||
const handoverRemark = ref('')
|
||||
|
||||
const handoverUserOptions = ref<Api.User.UserItem[]>([])
|
||||
const handoverSelectedSnapshot = ref<Api.User.UserItem | null>(null)
|
||||
const handoverUserLoading = ref(false)
|
||||
|
||||
function formatNowDatetime() {
|
||||
const d = new Date()
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
||||
}
|
||||
|
||||
function formatHandoverUserLabel(u: Api.User.UserItem) {
|
||||
const name = (u.nickName || '').trim() || '-'
|
||||
const no = (u.userNo || '').trim()
|
||||
const phone = (u.phoneNo || '').trim()
|
||||
const parts = [name]
|
||||
if (no) parts.push(no)
|
||||
if (phone) parts.push(phone)
|
||||
return parts.join(' · ')
|
||||
}
|
||||
|
||||
function applyDefaultHandoverUser() {
|
||||
const info = (userStore.info || {}) as Record<string, any>
|
||||
const uid = String(info.userId ?? info.id ?? '').trim()
|
||||
const nick = String(
|
||||
info.nickName ?? info.realName ?? info.userName ?? info.username ?? ''
|
||||
).trim()
|
||||
if (!uid) return
|
||||
handoverUserId.value = uid
|
||||
handoverSelectedSnapshot.value = {
|
||||
id: uid,
|
||||
nickName: nick || uid,
|
||||
userNo: info.userName || info.username || '',
|
||||
phoneNo: info.phone || info.phoneNo || ''
|
||||
} as Api.User.UserItem
|
||||
handoverUserOptions.value = handoverSelectedSnapshot.value
|
||||
? [handoverSelectedSnapshot.value]
|
||||
: []
|
||||
}
|
||||
|
||||
function resetHandoverForm() {
|
||||
handoverUserId.value = ''
|
||||
handoverTime.value = formatNowDatetime()
|
||||
handoverLocation.value = ''
|
||||
receiverPerson.value = ''
|
||||
handoverRemark.value = ''
|
||||
handoverUserOptions.value = []
|
||||
handoverSelectedSnapshot.value = null
|
||||
}
|
||||
|
||||
async function remoteSearchHandoverUsers(query: string) {
|
||||
handoverUserLoading.value = true
|
||||
try {
|
||||
const q = (query || '').trim()
|
||||
const res = await fetchUserList({
|
||||
page: 1,
|
||||
pageSize: 50,
|
||||
keyword: q || undefined,
|
||||
status: '1'
|
||||
})
|
||||
let list = [...(res.list || [])]
|
||||
const curId = handoverUserId.value
|
||||
if (
|
||||
curId &&
|
||||
handoverSelectedSnapshot.value?.id === curId &&
|
||||
!list.some((x) => x.id === curId)
|
||||
) {
|
||||
list = [handoverSelectedSnapshot.value, ...list]
|
||||
}
|
||||
handoverUserOptions.value = list
|
||||
} catch {
|
||||
handoverUserOptions.value = handoverSelectedSnapshot.value
|
||||
? [handoverSelectedSnapshot.value]
|
||||
: []
|
||||
} finally {
|
||||
handoverUserLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onHandoverUserChange(val: string | null | undefined) {
|
||||
if (!val) {
|
||||
handoverUserId.value = ''
|
||||
handoverSelectedSnapshot.value = null
|
||||
handoverUserOptions.value = []
|
||||
return
|
||||
}
|
||||
const hit = handoverUserOptions.value.find((u) => u.id === val)
|
||||
if (hit) handoverSelectedSnapshot.value = hit
|
||||
}
|
||||
|
||||
function fillHandoverFromApi(handover?: {
|
||||
handoverUserId?: string
|
||||
handoverPerson?: string
|
||||
handoverTime?: string
|
||||
handoverLocation?: string
|
||||
receiverPerson?: string
|
||||
remark?: string
|
||||
}) {
|
||||
const hasRecord = !!(handover?.handoverUserId || handover?.handoverTime || handover?.receiverPerson)
|
||||
if (!hasRecord) {
|
||||
resetHandoverForm()
|
||||
applyDefaultHandoverUser()
|
||||
return
|
||||
}
|
||||
handoverUserId.value = handover?.handoverUserId || ''
|
||||
handoverTime.value = handover?.handoverTime || formatNowDatetime()
|
||||
handoverLocation.value = handover?.handoverLocation || ''
|
||||
receiverPerson.value = handover?.receiverPerson || ''
|
||||
handoverRemark.value = handover?.remark || ''
|
||||
if (handoverUserId.value) {
|
||||
handoverSelectedSnapshot.value = {
|
||||
id: handoverUserId.value,
|
||||
nickName: handover?.handoverPerson || handoverUserId.value,
|
||||
userNo: '',
|
||||
phoneNo: ''
|
||||
} as Api.User.UserItem
|
||||
handoverUserOptions.value = [handoverSelectedSnapshot.value]
|
||||
}
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
if (!props.scheduleId) return
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await fetchInfusionScheduleOutboundHandover(props.scheduleId)
|
||||
scheduleSummary.value = data.scheduleSummary || {
|
||||
applicationNo: props.scheduleRow?.applicationNo,
|
||||
patientName: props.scheduleRow?.patientName,
|
||||
infusionTime: props.scheduleRow?.infusionTime
|
||||
}
|
||||
await materialSectionsRef.value?.ensureMaterialOptions()
|
||||
await materialSectionsRef.value?.loadFromApiData({
|
||||
remark: data.outboundMaterial?.remark,
|
||||
cells: data.outboundMaterial?.cells as any,
|
||||
qcMaterials: data.outboundMaterial?.qcMaterials,
|
||||
paperMaterials: data.outboundMaterial?.paperMaterials
|
||||
})
|
||||
fillHandoverFromApi(data.handover)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.modelValue, props.scheduleId] as const,
|
||||
([vis, id]) => {
|
||||
if (vis && id) {
|
||||
nextTick(() => loadData())
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function handleClosed() {
|
||||
materialSectionsRef.value?.resetState()
|
||||
scheduleSummary.value = null
|
||||
resetHandoverForm()
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!props.scheduleId) return
|
||||
const sections = materialSectionsRef.value
|
||||
if (!sections?.validateMaterial()) return
|
||||
|
||||
if (!handoverUserId.value?.trim()) {
|
||||
ElMessage.warning('请选择交接人')
|
||||
return
|
||||
}
|
||||
if (!handoverTime.value?.trim()) {
|
||||
ElMessage.warning('请选择交接时间')
|
||||
return
|
||||
}
|
||||
if (!receiverPerson.value?.trim()) {
|
||||
ElMessage.warning('请填写接收人')
|
||||
return
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
await saveInfusionScheduleOutboundHandover(props.scheduleId, {
|
||||
outboundMaterial: sections.buildSavePayload(),
|
||||
handover: {
|
||||
handoverUserId: handoverUserId.value.trim(),
|
||||
handoverTime: handoverTime.value.trim(),
|
||||
handoverLocation: handoverLocation.value.trim() || undefined,
|
||||
receiverPerson: receiverPerson.value.trim(),
|
||||
remark: handoverRemark.value.trim() || undefined
|
||||
}
|
||||
})
|
||||
ElMessage.success('出库交接已保存')
|
||||
emit('update:modelValue', false)
|
||||
emit('saved')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.material-summary {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
background: #f8fafc;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px 20px;
|
||||
}
|
||||
|
||||
.material-summary-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.material-summary-label {
|
||||
color: #64748b;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.material-summary-value {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.handover-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px 16px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.handover-form-section :deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
+616
-94
@@ -1,105 +1,217 @@
|
||||
<!-- 输注计划管理 — 查看弹窗 -->
|
||||
<!-- 输注计划管理 — 查看弹窗(全链路关联信息) -->
|
||||
<template>
|
||||
<SystemViewDialog
|
||||
v-model:visible="innerVisible"
|
||||
:title="viewData.patientName || '输注计划详情'"
|
||||
:title="heroTitle"
|
||||
:icon="Document"
|
||||
:code="viewData.applicationNo"
|
||||
:code="heroCode"
|
||||
code-label="申请单号"
|
||||
:sections="sections"
|
||||
:loading="loading"
|
||||
:grid-columns="3"
|
||||
width="1080px"
|
||||
:grid-columns="4"
|
||||
compact
|
||||
width="1120px"
|
||||
append-to-body
|
||||
modal-class="infusion-schedule-view-dialog-mask"
|
||||
>
|
||||
<template #products>
|
||||
<div class="list-body list-wrap view-inner-table-wrap">
|
||||
<el-table
|
||||
v-if="viewData.products && viewData.products.length"
|
||||
:data="viewData.products"
|
||||
v-if="products.length"
|
||||
:data="products"
|
||||
stripe
|
||||
class="list-table inner-table"
|
||||
max-height="320"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="projectName"
|
||||
label="项目名称"
|
||||
min-width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="cellTypeName"
|
||||
label="细胞类型"
|
||||
min-width="120"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="specText"
|
||||
label="规格"
|
||||
min-width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
label="协议价格(元)"
|
||||
min-width="120"
|
||||
align="right"
|
||||
header-align="center"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="68" align="center" />
|
||||
<el-table-column prop="projectName" label="项目名称" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="cellTypeName" label="细胞类型" min-width="120" align="center" />
|
||||
<el-table-column prop="specText" label="规格" min-width="100" align="center" />
|
||||
<el-table-column label="协议价格(元)" min-width="120" align="right">
|
||||
<template #default="{ row }">
|
||||
{{ row.agreementPrice != null ? `¥${formatMoneyDisplay(row.agreementPrice)}` : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="实际价格(元)"
|
||||
min-width="120"
|
||||
align="right"
|
||||
header-align="center"
|
||||
>
|
||||
<el-table-column label="实际价格(元)" min-width="120" align="right">
|
||||
<template #default="{ row }">
|
||||
{{ row.actualPrice != null ? `¥${formatMoneyDisplay(row.actualPrice)}` : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="quantity"
|
||||
label="数量"
|
||||
min-width="80"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column prop="quantity" label="数量" min-width="80" align="center" />
|
||||
</el-table>
|
||||
<el-empty v-else description="暂无产品" :image-size="48" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #production-info>
|
||||
<SampleProductionInfoCards :production-info="detail?.productionInfo" />
|
||||
</template>
|
||||
|
||||
<template #qc-info>
|
||||
<SampleQcInfoCards :qc-info="detail?.qcInfo" hide-sample-context />
|
||||
</template>
|
||||
|
||||
<template #outbound-material>
|
||||
<div class="view-section-slot outbound-material-view">
|
||||
<div v-if="!hasOutboundMaterial" class="outbound-material-empty">暂无出库物料准备记录</div>
|
||||
<template v-else>
|
||||
<div v-if="outboundMaterialCells.length" class="outbound-material-block">
|
||||
<h4 class="outbound-material-subtitle">出库细胞</h4>
|
||||
<el-table :data="outboundMaterialCells" class="list-table inner-table" stripe size="small">
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column prop="productCode" label="产品编码" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="productName" label="产品名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="productTypeName" label="类型" min-width="90" />
|
||||
<el-table-column prop="specName" label="规格" min-width="90" />
|
||||
<el-table-column prop="storageSlotName" label="储物格" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column label="状态" min-width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.productStatus"
|
||||
:type="cellProductStatusTagType(row.productStatus) as any"
|
||||
size="small"
|
||||
>
|
||||
{{ cellProductStatusLabel(row.productStatus) }}
|
||||
</el-tag>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div v-if="outboundMaterialQcRows.length" class="outbound-material-block">
|
||||
<h4 class="outbound-material-subtitle">使用的物料</h4>
|
||||
<el-table :data="outboundMaterialQcRows" class="list-table inner-table" stripe size="small">
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="specModel" label="规格型号" min-width="110" />
|
||||
<el-table-column label="计划用量" min-width="100" align="center">
|
||||
<template #default="{ row }">{{ row.qty ?? '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="unit" label="单位" min-width="72" align="center" />
|
||||
</el-table>
|
||||
</div>
|
||||
<div v-if="outboundPaperRows.length" class="outbound-material-block">
|
||||
<h4 class="outbound-material-subtitle">其他纸质材料</h4>
|
||||
<el-table :data="outboundPaperRows" class="list-table inner-table" stripe size="small">
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column prop="paperName" label="名称" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="份数" min-width="80" align="center">
|
||||
<template #default="{ row }">{{ row.copies ?? '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件" min-width="320">
|
||||
<template #default="{ row }">
|
||||
<div v-if="parsePaperAttachments(row.attachmentUrl).length" class="paper-att-cards">
|
||||
<div
|
||||
v-for="(att, attIdx) in parsePaperAttachments(row.attachmentUrl)"
|
||||
:key="attIdx"
|
||||
class="paper-att-card"
|
||||
>
|
||||
<el-icon class="paper-att-card__icon"><Document /></el-icon>
|
||||
<span class="paper-att-card__name" :title="att.fileName">{{
|
||||
att.fileName || '附件'
|
||||
}}</span>
|
||||
<div class="paper-att-card__actions">
|
||||
<el-icon
|
||||
v-if="isImageFile(att)"
|
||||
class="paper-att-card__action"
|
||||
title="预览"
|
||||
@click="handlePreviewPaperAttachment(att)"
|
||||
>
|
||||
<View />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="paper-att-card__action"
|
||||
title="下载"
|
||||
@click="handleDownloadPaperAttachment(att)"
|
||||
>
|
||||
<Download />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<p v-if="outboundMaterialRemark" class="outbound-material-remark">
|
||||
备注:{{ outboundMaterialRemark }}
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #review-attachments>
|
||||
<div class="view-section-slot">
|
||||
<ProtocolAttachmentReadonly
|
||||
:files="reviewAttachmentFiles"
|
||||
@preview="handleAttachmentPreview"
|
||||
@download="handleAttachmentDownload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</SystemViewDialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="previewVisible"
|
||||
title="文件预览"
|
||||
width="800px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
style="backdrop-filter: none"
|
||||
>
|
||||
<div v-if="previewType === 'image'" class="preview-container">
|
||||
<img
|
||||
:src="previewUrl"
|
||||
alt="预览"
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="preview-container">
|
||||
<video
|
||||
:src="previewUrl"
|
||||
controls
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { Document } from '@element-plus/icons-vue'
|
||||
import { Document, Download, View } from '@element-plus/icons-vue'
|
||||
import SystemViewDialog, {
|
||||
type ViewSection,
|
||||
type TagType
|
||||
} from '@/components/business/system-view-dialog/index.vue'
|
||||
import { fetchDictDataOptions, fetchInfusionScheduleMgmtDetail } from '@/api/admin'
|
||||
import SampleProductionInfoCards from '@/components/business/sample-production-info-cards/index.vue'
|
||||
import SampleQcInfoCards from '@/components/business/sample-qc-info-cards/index.vue'
|
||||
import ProtocolAttachmentReadonly from '@/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue'
|
||||
import { fetchInfusionScheduleMgmtDetail } from '@/api/admin'
|
||||
import {
|
||||
applicationStatusLabel,
|
||||
applicationStatusTagType,
|
||||
channelServiceOrderTypeLabel,
|
||||
channelServiceOrderTypeTagType,
|
||||
storageReleaseConclusionLabel,
|
||||
storageReleaseConclusionTagType,
|
||||
scheduleStatusLabel,
|
||||
scheduleStatusTagType,
|
||||
infusionStatusLabel,
|
||||
infusionStatusTagType,
|
||||
qcStatusLabel,
|
||||
qcStatusTagType,
|
||||
infusionMethodLabel,
|
||||
cellProductStatusLabel,
|
||||
cellProductStatusTagType,
|
||||
sexLabel
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import { maskIdCard } from '@/utils/mask-sensitive'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
|
||||
defineOptions({ name: 'ScheduleViewDialog' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
@@ -113,56 +225,247 @@
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const viewData = ref<Record<string, any>>({})
|
||||
const infusionMethodOptions = ref<Api.Dict.DictDataOption[]>([])
|
||||
const detail = ref<Record<string, any> | null>(null)
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const previewType = ref<'image' | 'video'>('image')
|
||||
|
||||
const formatInfusionMethod = (code: unknown) => {
|
||||
if (!code) return undefined
|
||||
const hit = infusionMethodOptions.value.find((o) => o.value === code)
|
||||
return hit?.label || String(code)
|
||||
type PaperAttachmentView = {
|
||||
fileName: string
|
||||
attachmentUrl: string
|
||||
fileUrl: string
|
||||
fileType?: string
|
||||
}
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
const heroTitle = computed(() => {
|
||||
const patient = detail.value?.patient || {}
|
||||
return patient.patientName || detail.value?.patientName || '输注计划详情'
|
||||
})
|
||||
|
||||
const heroCode = computed(() => {
|
||||
const app = detail.value?.application || {}
|
||||
return app.applicationNo || detail.value?.applicationNo || ''
|
||||
})
|
||||
|
||||
const products = computed(() => detail.value?.products || [])
|
||||
|
||||
const outboundMaterial = computed(() => detail.value?.outboundMaterial || null)
|
||||
const outboundMaterialCells = computed(() => outboundMaterial.value?.cells || [])
|
||||
const outboundMaterialQcRows = computed(() => outboundMaterial.value?.qcMaterials || [])
|
||||
const outboundMaterialRemark = computed(() => outboundMaterial.value?.remark || '')
|
||||
const outboundPaperRows = computed(() => {
|
||||
const materials = outboundMaterial.value?.paperMaterials || []
|
||||
if (materials.length) {
|
||||
return materials.map((p: Record<string, any>) => ({
|
||||
paperName: p.paperName || '',
|
||||
copies: p.copies,
|
||||
attachmentUrl: p.attachmentUrl || ''
|
||||
}))
|
||||
}
|
||||
const urls = outboundMaterial.value?.paperAttachments || ''
|
||||
if (!urls.trim()) return []
|
||||
return urls
|
||||
.split(',')
|
||||
.map((u: string) => u.trim())
|
||||
.filter(Boolean)
|
||||
.map((attachmentUrl: string) => ({
|
||||
paperName: paperAttachmentName(attachmentUrl),
|
||||
copies: 1,
|
||||
attachmentUrl
|
||||
}))
|
||||
})
|
||||
const hasOutboundMaterial = computed(
|
||||
() =>
|
||||
outboundMaterialCells.value.length > 0 ||
|
||||
outboundMaterialQcRows.value.length > 0 ||
|
||||
outboundPaperRows.value.length > 0 ||
|
||||
!!outboundMaterialRemark.value.trim()
|
||||
)
|
||||
|
||||
const reviewAttachmentFiles = computed(() => {
|
||||
const urls = detail.value?.reviewInfo?.reviewAttachments || ''
|
||||
if (!urls.trim()) return []
|
||||
return urls
|
||||
.split(',')
|
||||
.map((u: string) => u.trim())
|
||||
.filter(Boolean)
|
||||
.map((fileUrl: string, i: number) => ({
|
||||
id: `att_${i}`,
|
||||
fileUrl,
|
||||
fileName: fileUrl.split('/').pop() || ''
|
||||
}))
|
||||
})
|
||||
|
||||
function formatCourseNo(schedule: Record<string, any>) {
|
||||
const sortNo = schedule?.sortNo ?? detail.value?.sortNo
|
||||
const total = schedule?.totalCourseCount ?? detail.value?.totalCourseCount
|
||||
if (sortNo != null && total != null) {
|
||||
return `第 ${sortNo} 次 / 共 ${total} 次`
|
||||
}
|
||||
if (sortNo != null) return `第 ${sortNo} 次`
|
||||
return '—'
|
||||
}
|
||||
|
||||
function formatNeedCheckup(v: unknown) {
|
||||
if (v === 1 || v === '1') return '需要'
|
||||
if (v === 0 || v === '0') return '不需要'
|
||||
return '—'
|
||||
}
|
||||
|
||||
function formatCellType(plan: Record<string, any>, schedule: Record<string, any>) {
|
||||
return schedule.cellType || plan.cellType || detail.value?.cellType || '—'
|
||||
}
|
||||
|
||||
function hasInfusionConfirm(confirm: Record<string, any>) {
|
||||
return !!(confirm?.id || confirm?.confirmTime || confirm?.confirmUserName)
|
||||
}
|
||||
|
||||
const sections = computed((): ViewSection[] => {
|
||||
const row = viewData.value
|
||||
if (!row?.id) return []
|
||||
const d = detail.value
|
||||
if (!d?.id) return []
|
||||
|
||||
const app = d.application || {}
|
||||
const patient = d.patient || {}
|
||||
const plan = d.infusionPlan || {}
|
||||
const schedule = d.infusionSchedule || {}
|
||||
const review = d.reviewInfo || {}
|
||||
const oh = d.outboundHandover || {}
|
||||
const confirm = d.infusionConfirm || {}
|
||||
|
||||
const reviewConclusionValue = review.reviewConclusion || ''
|
||||
const reviewConclusionDisplay = reviewConclusionValue
|
||||
? storageReleaseConclusionLabel(reviewConclusionValue)
|
||||
: '待审核'
|
||||
const reviewConclusionTag = reviewConclusionValue
|
||||
? storageReleaseConclusionTagType(reviewConclusionValue)
|
||||
: 'info'
|
||||
|
||||
const orderType = app.orderType || d.orderType || ''
|
||||
const confirmFields: ViewSection['fields'] = hasInfusionConfirm(confirm)
|
||||
? [
|
||||
{ label: '确认人', value: confirm.confirmUserName || '—' },
|
||||
{ label: '确认时间', value: confirm.confirmTime || '—' },
|
||||
{ label: '确认说明', value: confirm.confirmDesc || '—', span: true },
|
||||
{ label: '备注', value: confirm.remark || '—', span: true }
|
||||
]
|
||||
: schedule.infusionStatus === 'yrqr' || d.infusionStatus === 'yrqr'
|
||||
? [
|
||||
{
|
||||
label: '确认状态',
|
||||
value: infusionStatusLabel('yrqr'),
|
||||
tagType: infusionStatusTagType('yrqr') as TagType
|
||||
},
|
||||
{
|
||||
label: '说明',
|
||||
value: '已在输注计划管理中确认,暂无独立应用确认记录',
|
||||
span: true
|
||||
}
|
||||
]
|
||||
: [{ label: '确认状态', value: '未确认', tagType: 'info' }]
|
||||
|
||||
return [
|
||||
{
|
||||
title: '申请信息',
|
||||
title: '中试申请 / 渠道 / 服务订单',
|
||||
fields: [
|
||||
{ label: '申请单号', value: row.applicationNo },
|
||||
{ label: '渠道名称', value: row.channelName }
|
||||
{ label: '申请单号', value: app.applicationNo || d.applicationNo || '—' },
|
||||
{ label: '申请单名称', value: app.applicationName || d.applicationName || '—' },
|
||||
{
|
||||
label: '申请单状态',
|
||||
value: applicationStatusLabel(app.applicationStatus || d.applicationStatus || ''),
|
||||
tagType: applicationStatusTagType(app.applicationStatus || d.applicationStatus || '')
|
||||
},
|
||||
{ label: '渠道名称', value: app.channelName || d.channelName || '—' },
|
||||
{
|
||||
label: '服务订单',
|
||||
value: app.channelServiceOrderName || d.channelServiceOrderName || '—'
|
||||
},
|
||||
{
|
||||
label: '订单类型',
|
||||
value: orderType ? channelServiceOrderTypeLabel(orderType) : '—',
|
||||
tagType: orderType ? channelServiceOrderTypeTagType(orderType) : undefined
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '患者信息',
|
||||
fields: [
|
||||
{ label: '患者姓名', value: row.patientName },
|
||||
{ label: '性别', value: sexLabel(row.sex) || '—' },
|
||||
{ label: '年龄', value: row.age != null ? String(row.age) : undefined }
|
||||
{ label: '患者姓名', value: patient.patientName || d.patientName || '—' },
|
||||
{ label: '性别', value: sexLabel(patient.sex || d.patientSex) || '—' },
|
||||
{
|
||||
label: '年龄',
|
||||
value:
|
||||
patient.age != null && patient.age !== ''
|
||||
? String(patient.age)
|
||||
: d.patientAge != null
|
||||
? String(d.patientAge)
|
||||
: '—'
|
||||
},
|
||||
{ label: '联系电话', value: patient.phoneNo || '—' },
|
||||
{
|
||||
label: '身份证号',
|
||||
value: patient.idCardNo ? maskIdCard(patient.idCardNo) : '—'
|
||||
},
|
||||
{ label: '紧急联系人', value: patient.emergencyContact || '—' },
|
||||
{ label: '紧急联系电话', value: patient.emergencyPhone || '—' },
|
||||
{ label: '是否需要体检', value: formatNeedCheckup(patient.needCheckup) },
|
||||
{ label: '临床诊断', value: patient.clinicalDiagnosis || '—', span: true }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '输注计划',
|
||||
title: '输注方案',
|
||||
fields: [
|
||||
{ label: '规格', value: row.specText },
|
||||
{ label: '输注时间', value: row.infusionTime },
|
||||
{ label: '输注地点', value: row.infusionLocation },
|
||||
{ label: '输注方式', value: formatInfusionMethod(row.infusionMethod) },
|
||||
{ label: '细胞类型', value: formatCellType(plan, schedule) },
|
||||
{ label: '规格', value: plan.specText || schedule.specText || d.specText || '—' },
|
||||
{
|
||||
label: '总疗程数',
|
||||
value:
|
||||
plan.totalCourseCount != null
|
||||
? String(plan.totalCourseCount)
|
||||
: schedule.totalCourseCount != null
|
||||
? String(schedule.totalCourseCount)
|
||||
: d.totalCourseCount != null
|
||||
? String(d.totalCourseCount)
|
||||
: '—'
|
||||
},
|
||||
{ label: '疗程间隔', value: plan.courseInterval || '—' },
|
||||
{ label: '输注开始日期', value: plan.infusionStartDate || '—' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '本次输注计划',
|
||||
fields: [
|
||||
{ label: '疗程进度', value: formatCourseNo(schedule) },
|
||||
{ label: '细胞类型', value: formatCellType(plan, schedule) },
|
||||
{ label: '规格', value: schedule.specText || d.specText || '—' },
|
||||
{ label: '输注时间', value: schedule.infusionTime || d.infusionTime || '—' },
|
||||
{ label: '输注地点', value: schedule.infusionLocation || d.infusionLocation || '—' },
|
||||
{
|
||||
label: '输注方式',
|
||||
value: infusionMethodLabel(schedule.infusionMethod || d.infusionMethod || '')
|
||||
},
|
||||
{
|
||||
label: '生产状态',
|
||||
value: scheduleStatusLabel(row.scheduleStatus),
|
||||
tagType: scheduleStatusTagType(row.scheduleStatus) as TagType
|
||||
value: scheduleStatusLabel(schedule.scheduleStatus || d.scheduleStatus || ''),
|
||||
tagType: scheduleStatusTagType(schedule.scheduleStatus || d.scheduleStatus || '') as TagType
|
||||
},
|
||||
{
|
||||
label: '输注状态',
|
||||
value: infusionStatusLabel(row.infusionStatus),
|
||||
tagType: infusionStatusTagType(row.infusionStatus) as TagType
|
||||
value: infusionStatusLabel(schedule.infusionStatus || d.infusionStatus || ''),
|
||||
tagType: infusionStatusTagType(schedule.infusionStatus || d.infusionStatus || '') as TagType
|
||||
},
|
||||
{
|
||||
label: '质检状态',
|
||||
value: qcStatusLabel(row.qcStatus),
|
||||
tagType: qcStatusTagType(row.qcStatus) as TagType
|
||||
}
|
||||
value: qcStatusLabel(schedule.qcStatus || d.qcStatus || ''),
|
||||
tagType: qcStatusTagType(schedule.qcStatus || d.qcStatus || '') as TagType
|
||||
},
|
||||
{
|
||||
label: '取消原因',
|
||||
value: schedule.cancelReason || d.cancelReason || '—',
|
||||
span: true
|
||||
},
|
||||
{ label: '备注', value: schedule.remark || d.remark || '—', span: true }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -171,33 +474,138 @@
|
||||
slot: 'products'
|
||||
},
|
||||
{
|
||||
title: '其他信息',
|
||||
fields: [{ label: '备注', value: row.remark || undefined, span: true }]
|
||||
title: '出库生产工单 + 任务',
|
||||
slot: 'production-info',
|
||||
fields: []
|
||||
},
|
||||
{
|
||||
title: '出库质检工单 + 任务',
|
||||
slot: 'qc-info',
|
||||
fields: []
|
||||
},
|
||||
{
|
||||
title: '出库物料准备',
|
||||
slot: 'outbound-material',
|
||||
fields: []
|
||||
},
|
||||
{
|
||||
title: '出库放行审核',
|
||||
slot: 'review-attachments',
|
||||
fields: [
|
||||
{
|
||||
label: '审核结论',
|
||||
value: reviewConclusionDisplay,
|
||||
tagType: reviewConclusionTag
|
||||
},
|
||||
{ label: '审核人', value: review.reviewByName || '—' },
|
||||
{ label: '审核时间', value: review.reviewTime || '—' },
|
||||
{ label: '审核备注', value: review.reviewRemark || '—', span: true }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '出库交接',
|
||||
fields: [
|
||||
{ label: '交接人', value: oh.handoverPerson || '—' },
|
||||
{ label: '交接时间', value: oh.handoverTime || '—' },
|
||||
{ label: '交接地点', value: oh.handoverLocation || '—' },
|
||||
{ label: '接收人', value: oh.receiverPerson || '—' },
|
||||
{ label: '备注', value: oh.remark || '—', span: true }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '应用确认',
|
||||
fields: confirmFields
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const loadInfusionMethodOptions = async () => {
|
||||
try {
|
||||
infusionMethodOptions.value = (await fetchDictDataOptions('szfs')) || []
|
||||
} catch {
|
||||
infusionMethodOptions.value = []
|
||||
}
|
||||
function paperAttachmentName(url: string) {
|
||||
return url.split('/').pop() || '附件'
|
||||
}
|
||||
|
||||
function splitUrls(str: string): string[] {
|
||||
if (!str?.trim()) return []
|
||||
return str
|
||||
.split(',')
|
||||
.map((u) => u.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function parsePaperAttachments(urlStr: string): PaperAttachmentView[] {
|
||||
return splitUrls(urlStr).map((url) => ({
|
||||
fileName: paperAttachmentName(url),
|
||||
attachmentUrl: url,
|
||||
fileUrl: url
|
||||
}))
|
||||
}
|
||||
|
||||
function isImageFile(file: PaperAttachmentView) {
|
||||
const url = (file.fileUrl || file.attachmentUrl || '').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: PaperAttachmentView) {
|
||||
const url = (file.fileUrl || file.attachmentUrl || '').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 handlePreviewPaperAttachment(att: PaperAttachmentView) {
|
||||
if (!att.attachmentUrl) return
|
||||
previewUrl.value = attachmentFullUrl(att.attachmentUrl)
|
||||
previewType.value = isVideoFile(att) ? 'video' : 'image'
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
function handleDownloadPaperAttachment(att: PaperAttachmentView) {
|
||||
if (!att.attachmentUrl) return
|
||||
downloadAttachmentFile(
|
||||
{
|
||||
fileName: att.fileName,
|
||||
fileUrl: att.attachmentUrl,
|
||||
fileType: att.fileType || ''
|
||||
},
|
||||
{ fullFileUrl: attachmentFullUrl }
|
||||
)
|
||||
}
|
||||
|
||||
function attachmentFullUrl(url: string) {
|
||||
const u = String(url || '').trim()
|
||||
if (!u) return ''
|
||||
if (/^https?:\/\//i.test(u)) return u
|
||||
return minioBaseUrl ? `${minioBaseUrl}/${u.replace(/^\//, '')}` : u
|
||||
}
|
||||
|
||||
function handleAttachmentPreview(file: Record<string, unknown>) {
|
||||
const url = attachmentFullUrl(String(file.fileUrl || ''))
|
||||
if (url) window.open(url, '_blank')
|
||||
}
|
||||
|
||||
function handleAttachmentDownload(file: Record<string, unknown>) {
|
||||
downloadAttachmentFile(file, { fullFileUrl: attachmentFullUrl })
|
||||
}
|
||||
|
||||
const loadDetail = async () => {
|
||||
const id = props.scheduleRow?.id
|
||||
if (!id) return
|
||||
viewData.value = { ...(props.scheduleRow || {}) }
|
||||
detail.value = { ...(props.scheduleRow || {}) }
|
||||
loading.value = true
|
||||
try {
|
||||
await loadInfusionMethodOptions()
|
||||
const res: any = await fetchInfusionScheduleMgmtDetail({ scheduleId: id })
|
||||
const detail =
|
||||
const data =
|
||||
res && typeof res === 'object' && !Array.isArray(res)
|
||||
? (res as Record<string, any>)
|
||||
: ((res as any)?.data ?? {})
|
||||
viewData.value = { ...viewData.value, ...detail }
|
||||
detail.value = { ...detail.value, ...data }
|
||||
} catch {
|
||||
// keep initial data from row
|
||||
} finally {
|
||||
@@ -211,7 +619,7 @@
|
||||
if (val && props.scheduleRow?.id) {
|
||||
loadDetail()
|
||||
} else if (!val) {
|
||||
viewData.value = {}
|
||||
detail.value = null
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -250,4 +658,118 @@
|
||||
padding: 24px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.view-section-slot :deep(.sample-card__section-label) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.view-section-slot :deep(.sample-card__section--qc),
|
||||
.view-section-slot :deep(.sample-card__section--production) {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.outbound-material-view {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.outbound-material-empty {
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.outbound-material-block {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.outbound-material-subtitle {
|
||||
margin: 0 0 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.outbound-material-remark {
|
||||
margin: 12px 0 0;
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.paper-att-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.paper-att-card {
|
||||
width: 100px;
|
||||
height: 72px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
background: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.paper-att-card__icon {
|
||||
color: #0284c7;
|
||||
font-size: 18px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.paper-att-card__name {
|
||||
width: 100%;
|
||||
font-size: 11px;
|
||||
color: #334155;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.paper-att-card__actions {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.paper-att-card:hover .paper-att-card__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.paper-att-card__action {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #10b981;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -203,7 +203,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
min-width="420"
|
||||
min-width="520"
|
||||
fixed="right"
|
||||
align="center"
|
||||
header-align="center"
|
||||
@@ -229,6 +229,9 @@
|
||||
<el-button v-if="canEditSchedule(row)" class="btn-op-item" @click="handleCancel(row)">
|
||||
<el-icon><CircleClose /></el-icon>取消
|
||||
</el-button>
|
||||
<el-button v-if="canShowHandover(row)" class="btn-op-item" @click="handleHandover(row)">
|
||||
<el-icon><Switch /></el-icon>出库交接
|
||||
</el-button>
|
||||
<el-button class="btn-op-item" @click="handleView(row)">
|
||||
<el-icon><View /></el-icon>查看
|
||||
</el-button>
|
||||
@@ -264,6 +267,14 @@
|
||||
:patient-name="cancelPatientName"
|
||||
@success="loadData"
|
||||
/>
|
||||
|
||||
<!-- 出库交接 -->
|
||||
<InfusionScheduleHandoverDialog
|
||||
v-model="handoverDialogVisible"
|
||||
:schedule-id="handoverScheduleId"
|
||||
:schedule-row="handoverRow"
|
||||
@saved="loadData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -277,7 +288,8 @@
|
||||
EditPen,
|
||||
CircleCheck,
|
||||
CircleClose,
|
||||
SuccessFilled
|
||||
SuccessFilled,
|
||||
Switch
|
||||
} from '@element-plus/icons-vue'
|
||||
import { fetchInfusionScheduleMgmtList, updateInfusionScheduleStatus } from '@/api/admin'
|
||||
import {
|
||||
@@ -293,6 +305,7 @@
|
||||
import ScheduleEditDialog from './ScheduleEditDialog.vue'
|
||||
import ScheduleViewDialog from './ScheduleViewDialog.vue'
|
||||
import ScheduleCancelDialog from './ScheduleCancelDialog.vue'
|
||||
import InfusionScheduleHandoverDialog from './InfusionScheduleHandoverDialog.vue'
|
||||
|
||||
defineOptions({ name: 'InfusionScheduleManagement' })
|
||||
|
||||
@@ -367,6 +380,9 @@
|
||||
const cancelDialogVisible = ref(false)
|
||||
const cancelScheduleId = ref('')
|
||||
const cancelPatientName = ref('')
|
||||
const handoverDialogVisible = ref(false)
|
||||
const handoverScheduleId = ref('')
|
||||
const handoverRow = ref<any>(null)
|
||||
|
||||
// ── 数据加载 ──────────────────────────
|
||||
const loadData = async () => {
|
||||
@@ -434,6 +450,14 @@
|
||||
/** 待输注、已用户确认均可完成(待输注时将自动先确认再完成) */
|
||||
const canCompleteSchedule = (row: any) => canEditSchedule(row)
|
||||
|
||||
const canShowHandover = (row: any) => getInfusionStatus(row) !== 'yqx'
|
||||
|
||||
const handleHandover = (row: any) => {
|
||||
handoverScheduleId.value = row.id
|
||||
handoverRow.value = { ...row }
|
||||
handoverDialogVisible.value = true
|
||||
}
|
||||
|
||||
const buildCompleteConfirmMessage = (row: any) => {
|
||||
const orderTypeLabel = channelServiceOrderTypeLabel(row.orderType)
|
||||
const patient = row.patientName ? `患者「${row.patientName}」` : '该患者'
|
||||
|
||||
+1011
File diff suppressed because it is too large.
Load diff
+595
@@ -0,0 +1,595 @@
|
||||
<!-- 出库放行审核 — 审核弹窗(支持单条和批量) -->
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
:title="dialogTitle"
|
||||
width="640px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
style="backdrop-filter: none"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
@closed="resetForm"
|
||||
>
|
||||
<div v-if="isSingleMode && singleDetail" class="review-info-summary">
|
||||
<div class="review-summary-row">
|
||||
<span class="review-summary-label">申请单号</span>
|
||||
<span class="review-summary-value">{{ singleDetail.application?.applicationNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="review-summary-row">
|
||||
<span class="review-summary-label">患者姓名</span>
|
||||
<span class="review-summary-value">{{ singleDetail.patient?.patientName || '—' }}</span>
|
||||
</div>
|
||||
<div class="review-summary-row">
|
||||
<span class="review-summary-label">细胞类型</span>
|
||||
<span class="review-summary-value">{{ singleDetail.infusionPlan?.cellType || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else-if="scheduleIds.length > 1" class="audit-target-hint">
|
||||
将对已选
|
||||
<strong>{{ scheduleIds.length }}</strong> 条记录提交相同审核结果。
|
||||
</p>
|
||||
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="biz-dialog-form"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item label="审核结论" prop="reviewConclusion">
|
||||
<el-radio-group v-model="form.reviewConclusion">
|
||||
<el-radio value="tg">通过</el-radio>
|
||||
<el-radio value="btg">不通过</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
v-model="form.reviewRemark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="可选"
|
||||
maxlength="1000"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" class="form-col-span-2">
|
||||
<div class="attachment-upload">
|
||||
<div class="attachment-list">
|
||||
<el-upload
|
||||
ref="attachmentUploadRef"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:multiple="true"
|
||||
accept="image/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.zip,.rar"
|
||||
@change="handleAttachmentChange"
|
||||
>
|
||||
<div class="upload-card">
|
||||
<el-icon :size="28" color="#10b981"><Plus /></el-icon>
|
||||
<span class="upload-card-text">上传附件</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div
|
||||
v-for="(file, fIdx) in attachmentFiles"
|
||||
:key="file.id || fIdx"
|
||||
class="attachment-item"
|
||||
:class="{ 'is-uploading': file._uploading }"
|
||||
>
|
||||
<div
|
||||
class="attachment-preview"
|
||||
@click="
|
||||
isImageFile(file) && file.fileUrl ? handlePreviewAttachment(file) : undefined
|
||||
"
|
||||
>
|
||||
<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.stop="handleDownloadAttachment(file)"
|
||||
>
|
||||
<Download />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="action-btn action-btn--danger"
|
||||
title="删除"
|
||||
@click.stop="removeAttachment(fIdx)"
|
||||
>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="upload-hint">支持图片、视频、PDF、Word、Excel 等文件</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="emit('update:modelValue', false)">
|
||||
<el-icon><Close /></el-icon>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" :loading="submitting" @click="handleSubmit">
|
||||
<el-icon><Check /></el-icon>
|
||||
确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="previewVisible"
|
||||
title="文件预览"
|
||||
width="800px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
style="backdrop-filter: none"
|
||||
>
|
||||
<div v-if="previewType === 'image'" class="preview-container">
|
||||
<img
|
||||
:src="previewUrl"
|
||||
alt="预览"
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="preview-container">
|
||||
<video
|
||||
:src="previewUrl"
|
||||
controls
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
Check,
|
||||
Close,
|
||||
Plus,
|
||||
Document,
|
||||
Delete,
|
||||
Download,
|
||||
Loading,
|
||||
VideoPlay
|
||||
} from '@element-plus/icons-vue'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
import {
|
||||
fetchOutboundReleaseReviewDetail,
|
||||
submitOutboundReleaseReview,
|
||||
batchReviewOutboundRelease,
|
||||
type OutboundReleaseReviewDetail
|
||||
} from '@/api/outbound-release-review'
|
||||
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
|
||||
|
||||
type AttachmentItem = {
|
||||
id: string
|
||||
fileName: string
|
||||
fileUrl: string
|
||||
fileType: string
|
||||
_uploading?: boolean
|
||||
}
|
||||
|
||||
defineOptions({ name: 'OutboundReleaseReviewDialog' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
scheduleIds: string[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', val: boolean): void
|
||||
(e: 'submitted'): void
|
||||
}>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const submitting = ref(false)
|
||||
const singleDetail = ref<OutboundReleaseReviewDetail | null>(null)
|
||||
const attachmentFiles = ref<AttachmentItem[]>([])
|
||||
|
||||
const isSingleMode = computed(() => props.scheduleIds.length === 1)
|
||||
|
||||
const form = reactive({
|
||||
reviewConclusion: 'tg',
|
||||
reviewRemark: ''
|
||||
})
|
||||
|
||||
const rules: FormRules = {
|
||||
reviewConclusion: [{ required: true, message: '请选择审核结论', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const dialogTitle = computed(() =>
|
||||
props.scheduleIds.length > 1
|
||||
? `批量审核(${props.scheduleIds.length} 条)`
|
||||
: '出库放行审核'
|
||||
)
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
function fullFileUrl(url: string) {
|
||||
if (!url) return ''
|
||||
if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/')) {
|
||||
return url
|
||||
}
|
||||
return minioBaseUrl ? `${minioBaseUrl}/${url.replace(/^\//, '')}` : url
|
||||
}
|
||||
|
||||
function isImageFile(file: AttachmentItem) {
|
||||
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: AttachmentItem) {
|
||||
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 removeAttachment(index: number) {
|
||||
attachmentFiles.value.splice(index, 1)
|
||||
}
|
||||
|
||||
function handlePreviewAttachment(file: AttachmentItem) {
|
||||
if (file._uploading || !file.fileUrl) return
|
||||
previewUrl.value = fullFileUrl(file.fileUrl)
|
||||
previewType.value = isVideoFile(file) ? 'video' : 'image'
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
function handleDownloadAttachment(file: AttachmentItem) {
|
||||
if (file._uploading || !file.fileUrl) return
|
||||
downloadAttachmentFile(file, { fullFileUrl })
|
||||
}
|
||||
|
||||
async function handleAttachmentChange(uploadFile: { raw?: File }) {
|
||||
const file = uploadFile.raw
|
||||
if (!file) return
|
||||
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
||||
attachmentFiles.value.push({
|
||||
id: tempId,
|
||||
fileName: file.name,
|
||||
fileUrl: '',
|
||||
fileType: file.type || '',
|
||||
_uploading: true
|
||||
})
|
||||
const idx = attachmentFiles.value.length - 1
|
||||
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('folder', 'outbound-release-review')
|
||||
fd.append('file', file)
|
||||
const res = await fetchUploadAttachment(fd)
|
||||
attachmentFiles.value[idx] = {
|
||||
...attachmentFiles.value[idx],
|
||||
id: res.id || tempId,
|
||||
fileName: file.name || res.fileName,
|
||||
fileUrl: res.fileUrl,
|
||||
fileType: res.fileType || file.type || '',
|
||||
_uploading: false
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error('文件上传失败')
|
||||
attachmentFiles.value.splice(idx, 1)
|
||||
} finally {
|
||||
nextTick(() => attachmentUploadRef.value?.clearFiles())
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.modelValue, props.scheduleIds] as const,
|
||||
async ([vis, ids]) => {
|
||||
if (!vis || !ids.length) {
|
||||
singleDetail.value = null
|
||||
return
|
||||
}
|
||||
if (ids.length === 1) {
|
||||
try {
|
||||
singleDetail.value = await fetchOutboundReleaseReviewDetail(ids[0])
|
||||
} catch {
|
||||
singleDetail.value = null
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function resetForm() {
|
||||
form.reviewConclusion = 'tg'
|
||||
form.reviewRemark = ''
|
||||
attachmentFiles.value = []
|
||||
singleDetail.value = null
|
||||
previewVisible.value = false
|
||||
formRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
const valid = await formRef.value?.validate().catch(() => false)
|
||||
if (!valid || !props.scheduleIds.length) return
|
||||
|
||||
if (attachmentFiles.value.some((f) => f._uploading)) {
|
||||
ElMessage.warning('附件上传中,请稍候')
|
||||
return
|
||||
}
|
||||
|
||||
const attachmentUrls = attachmentFiles.value
|
||||
.filter((f) => f.fileUrl && !f._uploading)
|
||||
.map((f) => f.fileUrl)
|
||||
.join(',')
|
||||
|
||||
const payload = {
|
||||
reviewConclusion: form.reviewConclusion,
|
||||
reviewRemark: form.reviewRemark.trim() || undefined,
|
||||
reviewAttachments: attachmentUrls || undefined
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
if (props.scheduleIds.length === 1) {
|
||||
await submitOutboundReleaseReview(props.scheduleIds[0], payload)
|
||||
ElMessage.success('审核成功')
|
||||
} else {
|
||||
await withFullscreenLoading(async () => {
|
||||
await batchReviewOutboundRelease({
|
||||
scheduleIds: props.scheduleIds,
|
||||
...payload
|
||||
})
|
||||
ElMessage.success(`批量审核成功,共 ${props.scheduleIds.length} 条`)
|
||||
}, fullscreenLoadingText.save)
|
||||
}
|
||||
emit('update:modelValue', false)
|
||||
emit('submitted')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.audit-target-hint {
|
||||
margin: 0 0 16px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: var(--mint-text-muted, #64748b);
|
||||
|
||||
strong {
|
||||
color: #1e293b;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.review-info-summary {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
background: #f8fafc;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e2e8f0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px 20px;
|
||||
}
|
||||
|
||||
.review-summary-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.review-summary-label {
|
||||
color: #64748b;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.review-summary-value {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-col-span-2 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 附件上传卡片化布局 — 对齐 OpenSpec ui-conventions / storage-cert */
|
||||
.attachment-upload {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.attachment-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.upload-card {
|
||||
width: 140px;
|
||||
height: 96px;
|
||||
border: 1px dashed #a7f3d0;
|
||||
border-radius: 8px;
|
||||
background: #f0fdf4;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
background 0.15s;
|
||||
|
||||
&:hover {
|
||||
border-color: #10b981;
|
||||
border-style: solid;
|
||||
background: #ecfdf5;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-card-text {
|
||||
font-size: 12px;
|
||||
color: #10b981;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: #94a3b8;
|
||||
line-height: 1;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.attachment-item {
|
||||
width: 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
box-shadow 0.15s;
|
||||
|
||||
&:hover {
|
||||
border-color: #10b981;
|
||||
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
&.is-uploading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
width: 100%;
|
||||
height: 96px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8fafc;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-preview__icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&--video {
|
||||
background: #ede9fe;
|
||||
color: #7c3aed;
|
||||
}
|
||||
|
||||
&--doc {
|
||||
background: #e0f2fe;
|
||||
color: #0284c7;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.attachment-name {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 11px;
|
||||
color: #334155;
|
||||
line-height: 1.4;
|
||||
word-break: break-all;
|
||||
min-height: calc(11px * 1.4 * 2);
|
||||
}
|
||||
|
||||
.attachment-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
cursor: pointer;
|
||||
background: #f1f5f9;
|
||||
transition:
|
||||
background 0.15s,
|
||||
color 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #10b981;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&--danger:hover {
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
+588
@@ -0,0 +1,588 @@
|
||||
<!-- 出库放行审核 — 只读查看 -->
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<SystemViewDialog
|
||||
v-model:visible="innerVisible"
|
||||
title="出库放行审核详情"
|
||||
width="1120px"
|
||||
:grid-columns="4"
|
||||
compact
|
||||
:icon="CircleCheck"
|
||||
:meta="viewHeroMeta"
|
||||
:sections="viewSections"
|
||||
:loading="loading"
|
||||
append-to-body
|
||||
modal-class="outbound-release-view-dialog-mask"
|
||||
>
|
||||
<template #production-info>
|
||||
<SampleProductionInfoCards :production-info="detail?.productionInfo" />
|
||||
</template>
|
||||
|
||||
<template #qc-info>
|
||||
<SampleQcInfoCards :qc-info="detail?.qcInfo" hide-sample-context />
|
||||
</template>
|
||||
|
||||
<template #review-attachments>
|
||||
<div class="view-section-slot">
|
||||
<ProtocolAttachmentReadonly
|
||||
:files="reviewAttachmentFiles"
|
||||
@preview="handleAttachmentPreview"
|
||||
@download="handleAttachmentDownload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #outbound-material>
|
||||
<div class="view-section-slot outbound-material-view">
|
||||
<div v-if="!hasOutboundMaterial" class="outbound-material-empty">暂无出库物料准备记录</div>
|
||||
<template v-else>
|
||||
<div v-if="outboundMaterialCells.length" class="outbound-material-block">
|
||||
<h4 class="outbound-material-subtitle">出库细胞</h4>
|
||||
<el-table :data="outboundMaterialCells" class="list-table inner-table" stripe size="small">
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column prop="productCode" label="产品编码" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="productName" label="产品名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="productTypeName" label="类型" min-width="90" />
|
||||
<el-table-column prop="specName" label="规格" min-width="90" />
|
||||
<el-table-column prop="storageSlotName" label="储物格" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column label="状态" min-width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.productStatus"
|
||||
:type="cellProductStatusTagType(row.productStatus) as any"
|
||||
size="small"
|
||||
>
|
||||
{{ cellProductStatusLabel(row.productStatus) }}
|
||||
</el-tag>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div v-if="outboundMaterialQcRows.length" class="outbound-material-block">
|
||||
<h4 class="outbound-material-subtitle">使用的物料</h4>
|
||||
<el-table :data="outboundMaterialQcRows" class="list-table inner-table" stripe size="small">
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="specModel" label="规格型号" min-width="110" />
|
||||
<el-table-column label="计划用量" min-width="100" align="center">
|
||||
<template #default="{ row }">{{ row.qty ?? '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="unit" label="单位" min-width="72" align="center" />
|
||||
</el-table>
|
||||
</div>
|
||||
<div v-if="outboundPaperRows.length" class="outbound-material-block">
|
||||
<h4 class="outbound-material-subtitle">其他纸质材料</h4>
|
||||
<el-table :data="outboundPaperRows" class="list-table inner-table" stripe size="small">
|
||||
<el-table-column type="index" label="序号" min-width="56" align="center" />
|
||||
<el-table-column prop="paperName" label="名称" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="份数" min-width="80" align="center">
|
||||
<template #default="{ row }">{{ row.copies ?? '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件" min-width="320">
|
||||
<template #default="{ row }">
|
||||
<div v-if="parsePaperAttachments(row.attachmentUrl).length" class="paper-att-cards">
|
||||
<div
|
||||
v-for="(att, attIdx) in parsePaperAttachments(row.attachmentUrl)"
|
||||
:key="attIdx"
|
||||
class="paper-att-card"
|
||||
>
|
||||
<el-icon class="paper-att-card__icon"><Document /></el-icon>
|
||||
<span class="paper-att-card__name" :title="att.fileName">{{
|
||||
att.fileName || '附件'
|
||||
}}</span>
|
||||
<div class="paper-att-card__actions">
|
||||
<el-icon
|
||||
v-if="isImageFile(att)"
|
||||
class="paper-att-card__action"
|
||||
title="预览"
|
||||
@click="handlePreviewPaperAttachment(att)"
|
||||
>
|
||||
<View />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="paper-att-card__action"
|
||||
title="下载"
|
||||
@click="handleDownloadPaperAttachment(att)"
|
||||
>
|
||||
<Download />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<p v-if="outboundMaterialRemark" class="outbound-material-remark">
|
||||
备注:{{ outboundMaterialRemark }}
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</SystemViewDialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="previewVisible"
|
||||
title="文件预览"
|
||||
width="800px"
|
||||
align-center
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
style="backdrop-filter: none"
|
||||
>
|
||||
<div v-if="previewType === 'image'" class="preview-container">
|
||||
<img
|
||||
:src="previewUrl"
|
||||
alt="预览"
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="preview-container">
|
||||
<video
|
||||
:src="previewUrl"
|
||||
controls
|
||||
style="max-width: 100%; max-height: 70vh; display: block; margin: 0 auto"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CircleCheck, Document, Download, View } from '@element-plus/icons-vue'
|
||||
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
|
||||
import type { ViewSection } from '@/components/business/system-view-dialog/index.vue'
|
||||
import SampleQcInfoCards from '@/components/business/sample-qc-info-cards/index.vue'
|
||||
import SampleProductionInfoCards from '@/components/business/sample-production-info-cards/index.vue'
|
||||
import {
|
||||
fetchOutboundReleaseReviewDetail,
|
||||
type OutboundReleaseReviewDetail
|
||||
} from '@/api/outbound-release-review'
|
||||
import {
|
||||
applicationStatusLabel,
|
||||
applicationStatusTagType,
|
||||
storageReleaseConclusionLabel,
|
||||
storageReleaseConclusionTagType,
|
||||
scheduleStatusLabel,
|
||||
scheduleStatusTagType,
|
||||
qcStatusLabel,
|
||||
qcStatusTagType,
|
||||
infusionMethodLabel,
|
||||
cellProductStatusLabel,
|
||||
cellProductStatusTagType
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { maskIdCard } from '@/utils/mask-sensitive'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
|
||||
defineOptions({ name: 'OutboundReleaseViewDialog' })
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
scheduleId: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void
|
||||
}>()
|
||||
|
||||
const innerVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: (val) => emit('update:visible', val)
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const detail = ref<OutboundReleaseReviewDetail | null>(null)
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const previewType = ref<'image' | 'video'>('image')
|
||||
|
||||
type PaperAttachmentView = {
|
||||
fileName: string
|
||||
attachmentUrl: string
|
||||
fileUrl: string
|
||||
fileType?: string
|
||||
}
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
watch(
|
||||
() => [props.visible, props.scheduleId] as const,
|
||||
async ([vis, id]) => {
|
||||
if (!vis || !id) {
|
||||
detail.value = null
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
detail.value = await fetchOutboundReleaseReviewDetail(id)
|
||||
} catch {
|
||||
detail.value = null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const viewHeroMeta = computed(() => {
|
||||
if (!detail.value) return ''
|
||||
return resolvePilotDetail(detail.value).application?.applicationNo || ''
|
||||
})
|
||||
|
||||
const reviewAttachmentFiles = computed(() => {
|
||||
const urls = detail.value?.reviewInfo?.reviewAttachments || ''
|
||||
if (!urls.trim()) return []
|
||||
return urls
|
||||
.split(',')
|
||||
.map((u) => u.trim())
|
||||
.filter(Boolean)
|
||||
.map((fileUrl, i) => ({
|
||||
id: `att_${i}`,
|
||||
fileUrl,
|
||||
fileName: fileUrl.split('/').pop() || ''
|
||||
}))
|
||||
})
|
||||
|
||||
const outboundMaterial = computed(() => detail.value?.outboundMaterial || null)
|
||||
const outboundMaterialCells = computed(() => outboundMaterial.value?.cells || [])
|
||||
const outboundMaterialQcRows = computed(() => outboundMaterial.value?.qcMaterials || [])
|
||||
const outboundMaterialRemark = computed(() => outboundMaterial.value?.remark || '')
|
||||
const outboundPaperRows = computed(() => {
|
||||
const materials = outboundMaterial.value?.paperMaterials || []
|
||||
if (materials.length) {
|
||||
return materials.map((p) => ({
|
||||
paperName: p.paperName || '',
|
||||
copies: p.copies,
|
||||
attachmentUrl: p.attachmentUrl || ''
|
||||
}))
|
||||
}
|
||||
const urls = outboundMaterial.value?.paperAttachments || ''
|
||||
if (!urls.trim()) return []
|
||||
return urls
|
||||
.split(',')
|
||||
.map((u) => u.trim())
|
||||
.filter(Boolean)
|
||||
.map((attachmentUrl) => ({
|
||||
paperName: paperAttachmentName(attachmentUrl),
|
||||
copies: 1,
|
||||
attachmentUrl
|
||||
}))
|
||||
})
|
||||
const hasOutboundMaterial = computed(
|
||||
() =>
|
||||
outboundMaterialCells.value.length > 0 ||
|
||||
outboundMaterialQcRows.value.length > 0 ||
|
||||
outboundPaperRows.value.length > 0 ||
|
||||
!!outboundMaterialRemark.value.trim()
|
||||
)
|
||||
|
||||
function paperAttachmentName(url: string) {
|
||||
return url.split('/').pop() || '附件'
|
||||
}
|
||||
|
||||
function splitUrls(str: string): string[] {
|
||||
if (!str?.trim()) return []
|
||||
return str
|
||||
.split(',')
|
||||
.map((u) => u.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function parsePaperAttachments(urlStr: string): PaperAttachmentView[] {
|
||||
return splitUrls(urlStr).map((url) => ({
|
||||
fileName: paperAttachmentName(url),
|
||||
attachmentUrl: url,
|
||||
fileUrl: url
|
||||
}))
|
||||
}
|
||||
|
||||
function isImageFile(file: PaperAttachmentView) {
|
||||
const url = (file.fileUrl || file.attachmentUrl || '').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: PaperAttachmentView) {
|
||||
const url = (file.fileUrl || file.attachmentUrl || '').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 handlePreviewPaperAttachment(att: PaperAttachmentView) {
|
||||
if (!att.attachmentUrl) return
|
||||
previewUrl.value = attachmentFullUrl(att.attachmentUrl)
|
||||
previewType.value = isVideoFile(att) ? 'video' : 'image'
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
function handleDownloadPaperAttachment(att: PaperAttachmentView) {
|
||||
if (!att.attachmentUrl) return
|
||||
downloadAttachmentFile(
|
||||
{
|
||||
fileName: att.fileName,
|
||||
fileUrl: att.attachmentUrl,
|
||||
fileType: att.fileType || ''
|
||||
},
|
||||
{ fullFileUrl: attachmentFullUrl }
|
||||
)
|
||||
}
|
||||
|
||||
/** 兼容旧版嵌套 pilotContext;新版详情已与 OutboundReleaseReviewDetail 展平一致 */
|
||||
function resolvePilotDetail(d: OutboundReleaseReviewDetail) {
|
||||
const nested = (d as { pilotContext?: OutboundReleaseReviewDetail }).pilotContext
|
||||
if (nested && typeof nested === 'object') {
|
||||
return { ...nested, productionInfo: d.productionInfo, qcInfo: d.qcInfo, reviewInfo: d.reviewInfo }
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
const viewSections = computed<ViewSection[]>(() => {
|
||||
const d = detail.value
|
||||
if (!d) return []
|
||||
const ctx = resolvePilotDetail(d)
|
||||
const app = ctx.application || {}
|
||||
const patient = ctx.patient || {}
|
||||
const plan = ctx.infusionPlan || {}
|
||||
const schedule = ctx.infusionSchedule || {}
|
||||
const review = ctx.reviewInfo || {}
|
||||
|
||||
const reviewConclusionValue = review.reviewConclusion || ''
|
||||
const reviewConclusionDisplay = reviewConclusionValue
|
||||
? storageReleaseConclusionLabel(reviewConclusionValue)
|
||||
: '待审核'
|
||||
const reviewConclusionTag = reviewConclusionValue
|
||||
? storageReleaseConclusionTagType(reviewConclusionValue)
|
||||
: 'info'
|
||||
|
||||
return [
|
||||
{
|
||||
title: '中试服务申请单信息',
|
||||
fields: [
|
||||
{ label: '申请单号', value: app.applicationNo || '—' },
|
||||
{ label: '申请单名称', value: app.applicationName || '—' },
|
||||
{
|
||||
label: '申请单状态',
|
||||
value: applicationStatusLabel(app.applicationStatus || ''),
|
||||
tagType: applicationStatusTagType(app.applicationStatus || '')
|
||||
},
|
||||
{ label: '渠道名称', value: app.channelName || '—' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '患者信息',
|
||||
fields: [
|
||||
{ label: '患者姓名', value: patient.patientName || '—' },
|
||||
{ label: '性别', value: patient.sex || '—' },
|
||||
{ label: '年龄', value: patient.age || '—' },
|
||||
{ label: '联系电话', value: patient.phoneNo || '—' },
|
||||
{
|
||||
label: '身份证号',
|
||||
value: patient.idCardNo ? maskIdCard(patient.idCardNo) : '—'
|
||||
},
|
||||
{ label: '紧急联系人', value: patient.emergencyContact || '—' },
|
||||
{ label: '紧急联系电话', value: patient.emergencyPhone || '—' },
|
||||
{ label: '临床诊断', value: patient.clinicalDiagnosis || '—', span: true }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '输注方案信息',
|
||||
fields: [
|
||||
{
|
||||
label: '总疗程数',
|
||||
value: plan.totalCourseCount != null ? String(plan.totalCourseCount) : '—'
|
||||
},
|
||||
{ label: '疗程间隔', value: plan.courseInterval || '—' },
|
||||
{ label: '输注开始日期', value: plan.infusionStartDate || '—' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '本次输注计划',
|
||||
fields: [
|
||||
{ label: '输注时间', value: schedule.infusionTime || '—' },
|
||||
{ label: '输注地点', value: schedule.infusionLocation || '—' },
|
||||
{ label: '输注方式', value: infusionMethodLabel(schedule.infusionMethod || '') },
|
||||
{
|
||||
label: '计划状态',
|
||||
value: scheduleStatusLabel(schedule.scheduleStatus || ''),
|
||||
tagType: scheduleStatusTagType(schedule.scheduleStatus || '')
|
||||
},
|
||||
{
|
||||
label: '质检状态',
|
||||
value: qcStatusLabel(schedule.qcStatus || ''),
|
||||
tagType: qcStatusTagType(schedule.qcStatus || '')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '生产信息',
|
||||
slot: 'production-info',
|
||||
fields: []
|
||||
},
|
||||
{
|
||||
title: '质检信息',
|
||||
slot: 'qc-info',
|
||||
fields: []
|
||||
},
|
||||
{
|
||||
title: '出库物料准备',
|
||||
slot: 'outbound-material',
|
||||
fields: []
|
||||
},
|
||||
{
|
||||
title: '放行审核',
|
||||
slot: 'review-attachments',
|
||||
fields: [
|
||||
{
|
||||
label: '审核结论',
|
||||
value: reviewConclusionDisplay,
|
||||
tagType: reviewConclusionTag
|
||||
},
|
||||
{ label: '审核人', value: review.reviewByName || '—' },
|
||||
{ label: '审核时间', value: review.reviewTime || '—' },
|
||||
{ label: '审核备注', value: review.reviewRemark || '—', span: true }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
function attachmentFullUrl(url: string) {
|
||||
const u = String(url || '').trim()
|
||||
if (!u) return ''
|
||||
if (/^https?:\/\//i.test(u)) return u
|
||||
return minioBaseUrl ? `${minioBaseUrl}/${u.replace(/^\//, '')}` : u
|
||||
}
|
||||
|
||||
function handleAttachmentPreview(file: Record<string, unknown>) {
|
||||
const url = attachmentFullUrl(String(file.fileUrl || ''))
|
||||
if (url) window.open(url, '_blank')
|
||||
}
|
||||
|
||||
function handleAttachmentDownload(file: Record<string, unknown>) {
|
||||
downloadAttachmentFile(file, { fullFileUrl: attachmentFullUrl })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.view-section-slot :deep(.sample-card__section-label) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.view-section-slot :deep(.sample-card__section--qc),
|
||||
.view-section-slot :deep(.sample-card__section--production) {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.outbound-material-view {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.outbound-material-empty {
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.outbound-material-block {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.outbound-material-subtitle {
|
||||
margin: 0 0 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.outbound-material-remark {
|
||||
margin: 12px 0 0;
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.paper-att-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.paper-att-card {
|
||||
width: 100px;
|
||||
height: 72px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
background: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.paper-att-card__icon {
|
||||
color: #0284c7;
|
||||
font-size: 18px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.paper-att-card__name {
|
||||
width: 100%;
|
||||
font-size: 11px;
|
||||
color: #334155;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.paper-att-card__actions {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.paper-att-card:hover .paper-att-card__actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.paper-att-card__action {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #10b981;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,376 @@
|
||||
<!-- 出库放行审核 -->
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<header class="page-head">
|
||||
<h1>出库放行审核</h1>
|
||||
<p class="page-desc">审核出库放行记录,管理中试服务出库流程。</p>
|
||||
</header>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-top">
|
||||
<h2>审核列表</h2>
|
||||
</div>
|
||||
|
||||
<div class="filter-block">
|
||||
<el-form :model="searchForm" class="filter-form" @submit.prevent>
|
||||
<div class="filter-grid">
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="申请单号/患者姓名"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核结论">
|
||||
<el-select
|
||||
v-model="searchForm.reviewConclusion"
|
||||
clearable
|
||||
placeholder="全部"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="待审核" value="" />
|
||||
<el-option
|
||||
v-for="item in reviewConclusionOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="filter-actions-row">
|
||||
<span class="filter-hint">支持按申请单号、患者姓名搜索。</span>
|
||||
<div class="filter-actions-end">
|
||||
<el-button class="btn-ghost" @click="handleReset">
|
||||
<el-icon><RefreshLeft /></el-icon>重置
|
||||
</el-button>
|
||||
<el-button class="btn-main" type="primary" @click="handleSearch">
|
||||
<el-icon><Search /></el-icon>搜索
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="list-toolbar">
|
||||
<div class="list-toolbar-row">
|
||||
<div class="list-toolbar-selected">
|
||||
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
|
||||
</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="btn-main"
|
||||
:disabled="!canBatchReview"
|
||||
@click="openReviewDialog()"
|
||||
>
|
||||
<el-icon><CircleCheck /></el-icon>
|
||||
批量审核
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-body list-wrap">
|
||||
<el-table
|
||||
class="list-table outbound-release-review-table"
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
@row-dblclick="handleView"
|
||||
>
|
||||
<el-table-column type="selection" min-width="50" align="center" header-align="center" />
|
||||
<el-table-column
|
||||
prop="applicationNo"
|
||||
label="申请单号"
|
||||
min-width="160"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="patientName"
|
||||
label="患者姓名"
|
||||
min-width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="bizType"
|
||||
label="类型"
|
||||
min-width="110"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.bizType"
|
||||
:type="bizTypeTagType(row.bizType) as any"
|
||||
size="small"
|
||||
>
|
||||
{{ row.bizTypeLabel || bizTypeLabel(row.bizType) }}
|
||||
</el-tag>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="infusionTime"
|
||||
label="输注时间"
|
||||
min-width="170"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="scheduleStatus"
|
||||
label="计划状态"
|
||||
min-width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.scheduleStatus"
|
||||
:type="scheduleStatusTagType(row.scheduleStatus)"
|
||||
size="small"
|
||||
>
|
||||
{{ scheduleStatusLabel(row.scheduleStatus) }}
|
||||
</el-tag>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="qcStatus"
|
||||
label="质检状态"
|
||||
min-width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.qcStatus" :type="qcStatusTagType(row.qcStatus)" size="small">
|
||||
{{ qcStatusLabel(row.qcStatus) }}
|
||||
</el-tag>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="reviewConclusion"
|
||||
label="审核结论"
|
||||
min-width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.reviewConclusion"
|
||||
:type="storageReleaseConclusionTagType(row.reviewConclusion)"
|
||||
size="small"
|
||||
>
|
||||
{{ storageReleaseConclusionLabel(row.reviewConclusion) }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="info" size="small">待审核</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="reviewByName"
|
||||
label="审核人"
|
||||
min-width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="reviewTime"
|
||||
label="审核时间"
|
||||
min-width="170"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
min-width="260"
|
||||
fixed="right"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-button class="btn-op-item" @click="handleView(row)">
|
||||
<el-icon><View /></el-icon>查看
|
||||
</el-button>
|
||||
<el-button class="btn-op-item" @click="openMaterialDialog(row)">
|
||||
<el-icon><Box /></el-icon>出库物料
|
||||
</el-button>
|
||||
<el-button class="btn-op-item" @click="openReviewDialog(row)">
|
||||
<el-icon><CircleCheck /></el-icon>审核
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination list-pager-bar">
|
||||
<el-pagination
|
||||
:current-page="pagination.page"
|
||||
:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="(val) => ((pagination.page = 1), (pagination.pageSize = val), loadData())"
|
||||
@current-change="(val) => ((pagination.page = val), loadData())"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<OutboundReleaseViewDialog v-model:visible="viewVisible" :schedule-id="viewScheduleId" />
|
||||
|
||||
<OutboundReleaseReviewDialog
|
||||
v-model="reviewDialogVisible"
|
||||
:schedule-ids="reviewDialogIds"
|
||||
@submitted="onReviewSubmitted"
|
||||
/>
|
||||
|
||||
<OutboundReleaseMaterialDialog
|
||||
v-model="materialDialogVisible"
|
||||
:schedule-id="materialScheduleId"
|
||||
:schedule-summary="materialScheduleSummary"
|
||||
@saved="loadData"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { View, Search, RefreshLeft, CircleCheck, Box } from '@element-plus/icons-vue'
|
||||
import OutboundReleaseViewDialog from './OutboundReleaseViewDialog.vue'
|
||||
import OutboundReleaseReviewDialog from './OutboundReleaseReviewDialog.vue'
|
||||
import OutboundReleaseMaterialDialog from './OutboundReleaseMaterialDialog.vue'
|
||||
import {
|
||||
fetchOutboundReleaseReviewList,
|
||||
type OutboundReleaseReview
|
||||
} from '@/api/outbound-release-review'
|
||||
import {
|
||||
storageReleaseConclusionLabel,
|
||||
storageReleaseConclusionTagType,
|
||||
scheduleStatusLabel,
|
||||
scheduleStatusTagType,
|
||||
qcStatusLabel,
|
||||
qcStatusTagType,
|
||||
bizTypeLabel,
|
||||
bizTypeTagType
|
||||
} from '@/utils/status-tag-registry'
|
||||
|
||||
defineOptions({ name: 'PilotServiceOutboundReleaseReview' })
|
||||
|
||||
const reviewConclusionOptions = [
|
||||
{ value: 'tg', label: '通过' },
|
||||
{ value: 'btg', label: '不通过' }
|
||||
]
|
||||
|
||||
const searchForm = reactive({
|
||||
reviewConclusion: '',
|
||||
keyword: ''
|
||||
})
|
||||
const tableData = ref<OutboundReleaseReview[]>([])
|
||||
const selectedRows = ref<OutboundReleaseReview[]>([])
|
||||
const loading = ref(false)
|
||||
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
|
||||
|
||||
const handleSelectionChange = (rows: OutboundReleaseReview[]) => {
|
||||
selectedRows.value = rows
|
||||
}
|
||||
|
||||
const canBatchReview = computed(() => selectedRows.value.length > 0)
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await fetchOutboundReleaseReviewList({
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize,
|
||||
reviewConclusion: searchForm.reviewConclusion || undefined,
|
||||
keyword: searchForm.keyword || undefined
|
||||
})
|
||||
tableData.value = res.list || []
|
||||
pagination.total = res.total ?? 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.reviewConclusion = ''
|
||||
searchForm.keyword = ''
|
||||
pagination.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
const viewVisible = ref(false)
|
||||
const viewScheduleId = ref('')
|
||||
|
||||
function handleView(row: OutboundReleaseReview) {
|
||||
viewScheduleId.value = String(row.scheduleId || row.id)
|
||||
viewVisible.value = true
|
||||
}
|
||||
|
||||
const reviewDialogVisible = ref(false)
|
||||
const reviewDialogIds = ref<string[]>([])
|
||||
|
||||
function openReviewDialog(row?: OutboundReleaseReview) {
|
||||
const rows = row ? [row] : selectedRows.value
|
||||
if (!rows.length) {
|
||||
ElMessage.warning('请先选择要审核的记录')
|
||||
return
|
||||
}
|
||||
reviewDialogIds.value = rows.map((r) => String(r.scheduleId || r.id))
|
||||
reviewDialogVisible.value = true
|
||||
}
|
||||
|
||||
function onReviewSubmitted() {
|
||||
selectedRows.value = []
|
||||
loadData()
|
||||
}
|
||||
|
||||
const materialDialogVisible = ref(false)
|
||||
const materialScheduleId = ref('')
|
||||
const materialScheduleSummary = ref<{
|
||||
applicationNo?: string
|
||||
patientName?: string
|
||||
infusionTime?: string
|
||||
}>()
|
||||
|
||||
function openMaterialDialog(row: OutboundReleaseReview) {
|
||||
materialScheduleId.value = String(row.scheduleId || row.id)
|
||||
materialScheduleSummary.value = {
|
||||
applicationNo: row.applicationNo,
|
||||
patientName: row.patientName,
|
||||
infusionTime: row.infusionTime
|
||||
}
|
||||
materialDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 输入检索条件时自动搜索(防抖 300ms)
|
||||
watchDebounced(
|
||||
searchForm,
|
||||
() => {
|
||||
pagination.page = 1
|
||||
loadData()
|
||||
},
|
||||
{ debounce: 300, deep: true }
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.outbound-release-review-table {
|
||||
:deep(td.el-table__cell:has(.btn-op-item)),
|
||||
:deep(td.el-table-fixed-column--right:has(.btn-op-item)),
|
||||
:deep(th.el-table-fixed-column--right) {
|
||||
min-width: 260px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -568,6 +568,12 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InfusionScheduleOutboundChainView
|
||||
v-if="schedule.id"
|
||||
:schedule-id="schedule.id"
|
||||
:active="!!schedule._expanded"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -766,6 +772,7 @@
|
||||
type ViewSection
|
||||
} from '@/components/business/system-view-dialog/index.vue'
|
||||
import ServiceOrderTxnTable from '@/views/pilot-service/channel-service-order/ServiceOrderTxnTable.vue'
|
||||
import InfusionScheduleOutboundChainView from '@/components/business/infusion-schedule-outbound-chain-view/index.vue'
|
||||
import {
|
||||
fetchPilotApplicationDetail,
|
||||
fetchPilotPatient,
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="activeTypeTab !== 'inbound'"
|
||||
v-if="activeTypeTab !== 'inbound' && activeTypeTab !== 'outbound'"
|
||||
prop="qcBizTypeLabel"
|
||||
label="业务类型"
|
||||
min-width="100"
|
||||
|
||||
Reference in new issue
Block a user