提交
This commit is contained in:
1 parent
19de2040d0
commit
3779de12ce
78 files changed
+23068
-13824
No files matched your search
@@ -0,0 +1,165 @@
|
||||
import request from '@/utils/http'
|
||||
|
||||
type PageList<T> = {
|
||||
list: T[]
|
||||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
export type StorageReleaseReview = {
|
||||
id: string
|
||||
sampleCode?: string
|
||||
sampleName?: string
|
||||
protocolNo?: string
|
||||
receptionSource?: string
|
||||
receptionSourceLabel?: string
|
||||
reviewConclusion?: string
|
||||
reviewConclusionLabel?: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
reviewBy?: string
|
||||
reviewByName?: string
|
||||
reviewTime?: string
|
||||
storageStatus?: string
|
||||
storageStatusLabel?: string
|
||||
storageBy?: string
|
||||
storageByName?: string
|
||||
storageTime?: string
|
||||
remark?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
export type ProtocolInfo = {
|
||||
protocolNo?: string
|
||||
protocolStatus?: string
|
||||
motherName?: string
|
||||
motherPhone?: string
|
||||
fatherName?: string
|
||||
fatherPhone?: string
|
||||
newbornName?: string
|
||||
sampleCode?: string
|
||||
sampleTypeName?: string
|
||||
collectAmount?: number | null
|
||||
storageYears?: number | null
|
||||
}
|
||||
|
||||
export type TransportInfo = {
|
||||
transportType?: string
|
||||
waybillNo?: string
|
||||
expressFee?: number | null
|
||||
etaTime?: string
|
||||
carrierName?: string
|
||||
carrierPhone?: string
|
||||
}
|
||||
|
||||
export type ReceptionInfo = {
|
||||
receptionTime?: string
|
||||
handlerName?: string
|
||||
receiveTemperature?: string
|
||||
volumeMl?: number | null
|
||||
netWeightG?: number | null
|
||||
acceptConclusion?: string
|
||||
}
|
||||
|
||||
export type QcInfoTask = {
|
||||
id?: string
|
||||
taskType?: string
|
||||
taskTypeLabel?: string
|
||||
taskStatus?: string
|
||||
taskStatusLabel?: string
|
||||
conclusion?: string
|
||||
conclusionLabel?: string
|
||||
}
|
||||
|
||||
export type QcInfo = {
|
||||
workOrderNo?: string
|
||||
workOrderStatus?: string
|
||||
tasks?: QcInfoTask[]
|
||||
}
|
||||
|
||||
export type ProductionInfoTask = {
|
||||
id?: string
|
||||
taskNo?: string
|
||||
taskName?: string
|
||||
taskStatus?: string
|
||||
taskStatusLabel?: string
|
||||
startedTime?: string
|
||||
completedTime?: string
|
||||
}
|
||||
|
||||
export type ProductionInfo = {
|
||||
workOrderNo?: string
|
||||
workOrderStatus?: string
|
||||
tasks?: ProductionInfoTask[]
|
||||
}
|
||||
|
||||
export type StorageReleaseReviewDetail = {
|
||||
review: StorageReleaseReview
|
||||
protocolInfo?: ProtocolInfo
|
||||
transportInfo?: TransportInfo
|
||||
receptionInfo?: ReceptionInfo
|
||||
qcInfo?: QcInfo
|
||||
productionInfo?: ProductionInfo
|
||||
}
|
||||
|
||||
export function fetchStorageReleaseReviewList(params?: {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
receptionSource?: string
|
||||
reviewConclusion?: string
|
||||
storageStatus?: string
|
||||
keyword?: string
|
||||
}) {
|
||||
return request.get<PageList<StorageReleaseReview>>({
|
||||
url: '/web/admin/storage-release-reviews',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchStorageReleaseReviewDetail(id: string) {
|
||||
return request.get<StorageReleaseReviewDetail>({
|
||||
url: `/web/admin/storage-release-reviews/${id}`
|
||||
})
|
||||
}
|
||||
|
||||
export function submitStorageReleaseReview(
|
||||
id: string,
|
||||
data: {
|
||||
reviewConclusion: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
}
|
||||
) {
|
||||
return request.put<StorageReleaseReview>({
|
||||
url: `/web/admin/storage-release-reviews/${id}/review`,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function batchReviewStorageRelease(data: {
|
||||
ids: string[]
|
||||
reviewConclusion: string
|
||||
reviewRemark?: string
|
||||
reviewAttachments?: string
|
||||
}) {
|
||||
return request.put<void>({
|
||||
url: '/web/admin/storage-release-reviews/batch-review',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function storeSample(id: string) {
|
||||
return request.put<StorageReleaseReview>({
|
||||
url: `/web/admin/storage-release-reviews/${id}/store`
|
||||
})
|
||||
}
|
||||
|
||||
export function batchStoreSample(data: { ids: string[] }) {
|
||||
return request.put<void>({
|
||||
url: '/web/admin/storage-release-reviews/batch-store',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -361,9 +361,14 @@
|
||||
useCommon().refresh()
|
||||
return
|
||||
}
|
||||
router.push({
|
||||
path: item.path,
|
||||
query: item.query as LocationQueryRaw
|
||||
// 强制重新加载:先将目标组件从 KeepAlive 缓存中排除
|
||||
store.addKeepAliveExclude(item)
|
||||
// 等待 Vue 处理 exclude 变更、清除缓存后再导航
|
||||
nextTick(() => {
|
||||
router.push({
|
||||
path: item.path,
|
||||
query: item.query as LocationQueryRaw
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_cell_product_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L1"}, {"id": "production_work_order_index_handleview", "label": "handleView()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L454"}, {"id": "production_work_order_index_handlecomplete", "label": "handleComplete()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L458"}, {"id": "production_work_order_index_handleopendispatch", "label": "handleOpenDispatch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L468"}, {"id": "production_work_order_index_handleedittask", "label": "handleEditTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L485"}, {"id": "production_work_order_index_opentaskeditor", "label": "openTaskEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L500"}, {"id": "production_work_order_index_openedittaskinfo", "label": "openEditTaskInfo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L512"}, {"id": "production_work_order_index_handleedittaskinfosuccess", "label": "handleEditTaskInfoSuccess()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L517"}, {"id": "production_work_order_index_handledeletetask", "label": "handleDeleteTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L523"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L454", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handlecomplete", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L458", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleopendispatch", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L468", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleedittask", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L485", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_opentaskeditor", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L500", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_openedittaskinfo", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L512", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleedittaskinfosuccess", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L517", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskinputenhancedtable_vue", "label": "TaskInputEnhancedTable.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputproductdialog_vue", "label": "TaskOutputProductDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L1"}, {"id": "production_work_order_taskoutputproductdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L342"}, {"id": "production_work_order_taskoutputproductdialog_initaddform", "label": "initAddForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L408"}, {"id": "production_work_order_taskoutputproductdialog_applyproductcodetolines", "label": "applyProductCodeToLines()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L426"}, {"id": "production_work_order_taskoutputproductdialog_ondialogopened", "label": "onDialogOpened()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L431"}, {"id": "production_work_order_taskoutputproductdialog_createquantityvalue", "label": "createQuantityValue()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L437"}, {"id": "production_work_order_taskoutputproductdialog_stripproductcodesuffix", "label": "stripProductCodeSuffix()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L443"}, {"id": "production_work_order_taskoutputproductdialog_buildlineproductcode", "label": "buildLineProductCode()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L449"}, {"id": "production_work_order_taskoutputproductdialog_synclinecodesfrombase", "label": "syncLineCodesFromBase()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L455"}, {"id": "production_work_order_taskoutputproductdialog_syncproductlines", "label": "syncProductLines()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L462"}, {"id": "production_work_order_taskoutputproductdialog_oncreatecountchange", "label": "onCreateCountChange()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L475"}, {"id": "production_work_order_taskoutputproductdialog_onbaseproductcodeinput", "label": "onBaseProductCodeInput()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L481"}, {"id": "production_work_order_taskoutputproductdialog_onlineproductcodeinput", "label": "onLineProductCodeInput()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L496"}, {"id": "production_work_order_taskoutputproductdialog_resetform", "label": "resetForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L541"}, {"id": "production_work_order_taskoutputproductdialog_filenamefromurl", "label": "fileNameFromUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L562"}, {"id": "production_work_order_taskoutputproductdialog_syncimagestoform", "label": "syncImagesToForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L568"}, {"id": "production_work_order_taskoutputproductdialog_syncformtoimages", "label": "syncFormToImages()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L575"}, {"id": "production_work_order_taskoutputproducLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "label": "StepDetailPanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L1"}, {"id": "production_work_order_stepdetailpanel_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L699"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_stepdetailpanel_vue", "target": "production_work_order_stepdetailpanel_for", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L699", "weight": 1.0}], "raw_calls": [{"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "push", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L701"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "decodeURIComponent", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L704"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "pop", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L704"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "split", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L704"}, {"caller_nid": "production_work_order_stepdetailpanel_for", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue", "source_location": "L704"}]}
|
||||
+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_components_core_layouts_art_work_tab_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L1"}, {"id": "art_work_tab_index_settransition", "label": "setTransition()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L204"}, {"id": "art_work_tab_index_handlewheelscroll", "label": "handleWheelScroll()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L283"}, {"id": "art_work_tab_index_handletouchstart", "label": "handleTouchStart()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L300"}, {"id": "art_work_tab_index_handletouchmove", "label": "handleTouchMove()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L304"}, {"id": "art_work_tab_index_handletouchend", "label": "handleTouchEnd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L318"}, {"id": "art_work_tab_index_setupeventlisteners", "label": "setupEventListeners()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L322"}, {"id": "art_work_tab_index_cleanupeventlisteners", "label": "cleanupEventListeners()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L331"}, {"id": "art_work_tab_index_clicktab", "label": "clickTab()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L359"}, {"id": "art_work_tab_index_closeworktab", "label": "closeWorktab()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L375"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_settransition", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L204", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_handlewheelscroll", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L283", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_handletouchstart", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L300", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_handletouchmove", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L304", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_handletouchend", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L318", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_setupeventlisteners", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L322", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_core_layouts_art_work_tab_index_vue", "target": "art_work_tab_index_cleanupeventlisteners", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/core/layouts/art-work-tab/index.vue", "source_location": "L331", "weight": 1.0}, {"source": "users_wuzilong_work_Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "label": "StorageReleaseReviewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L1"}, {"id": "storage_release_review_storagereleasereviewdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L173"}, {"id": "storage_release_review_storagereleasereviewdialog_isimagefile", "label": "isImageFile()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L179"}, {"id": "storage_release_review_storagereleasereviewdialog_removeattachment", "label": "removeAttachment()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L185"}, {"id": "storage_release_review_storagereleasereviewdialog_getlist", "label": "_getList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L191"}, {"id": "storage_release_review_storagereleasereviewdialog_resetform", "label": "resetForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L262"}, {"id": "storage_release_review_storagereleasereviewdialog_handlesubmit", "label": "handleSubmit()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L271"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L120", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L121", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_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/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L122", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L123", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_fullscreen_loading", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L129", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "storage_release_review_storagereleasereviewdialog_fullfileurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue", "source_location": "L173", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleasereviewdialog_vue", "target": "storage_release_review_storagereleasereviewdialog_isimagefile",Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputproductdialog_vue", "label": "TaskOutputProductDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputProductDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L42"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L57"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L64"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_tree_path", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_toapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_fromapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39", "weight": 1.0}, {"source": "users_wuLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_cell_product_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/cell-product/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L42"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L57"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L64"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_tree_path", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_toapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_fromapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39", "weight": 1.0}, {"source": "users_wuLine truncated
|
||||
+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_quality_inspection_storage_release_review_storagereleaseviewdialog_vue", "label": "StorageReleaseViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue", "source_location": "L1"}, {"id": "storage_release_review_storagereleaseviewdialog_formatval", "label": "formatVal()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue", "source_location": "L175"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_storagereleaseviewdialog_vue", "target": "storage_release_review_storagereleaseviewdialog_formatval", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue", "source_location": "L175", "weight": 1.0}], "raw_calls": [{"caller_nid": "storage_release_review_storagereleaseviewdialog_formatval", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue", "source_location": "L177"}]}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputlist_vue", "label": "TaskOutputList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L1"}, {"id": "production_work_order_index_handleview", "label": "handleView()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L454"}, {"id": "production_work_order_index_handlecomplete", "label": "handleComplete()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L458"}, {"id": "production_work_order_index_handleopendispatch", "label": "handleOpenDispatch()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L468"}, {"id": "production_work_order_index_handleedittask", "label": "handleEditTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L485"}, {"id": "production_work_order_index_opentaskeditor", "label": "openTaskEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L500"}, {"id": "production_work_order_index_openedittaskinfo", "label": "openEditTaskInfo()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L512"}, {"id": "production_work_order_index_handleedittaskinfosuccess", "label": "handleEditTaskInfoSuccess()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L517"}, {"id": "production_work_order_index_handledeletetask", "label": "handleDeleteTask()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L523"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L454", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handlecomplete", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L458", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleopendispatch", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L468", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleedittask", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L485", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_opentaskeditor", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L500", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_openedittaskinfo", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L512", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_index_vue", "target": "production_work_order_index_handleedittaskinfosuccess", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue", "source_location": "L517", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputlist_vue", "label": "TaskOutputList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_index_ts", "label": "index.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L1"}, {"id": "http_index_subscribetokenrefresh", "label": "subscribeTokenRefresh()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L45"}, {"id": "http_index_onrefreshed", "label": "onRefreshed()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L49"}, {"id": "http_index_onrefreshfailed", "label": "onRefreshFailed()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L54"}, {"id": "http_index_createhttperror", "label": "createHttpError()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L188"}, {"id": "http_index_handleunauthorizedresponse", "label": "handleUnauthorizedResponse()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L195"}, {"id": "http_index_triggerlogoutwithdebounce", "label": "triggerLogoutWithDebounce()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L256"}, {"id": "http_index_resetunauthorizederror", "label": "resetUnauthorizedError()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L266"}, {"id": "http_index_logout", "label": "logOut()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L273"}, {"id": "http_index_resolveuserid", "label": "resolveUserId()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L279"}, {"id": "http_index_parseuseridfromtoken", "label": "parseUserIdFromToken()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L286"}, {"id": "http_index_ispathexcluded", "label": "isPathExcluded()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L300"}, {"id": "http_index_getencryptskipreason", "label": "getEncryptSkipReason()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L305"}, {"id": "http_index_normalizerequestdata", "label": "normalizeRequestData()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L324"}, {"id": "http_index_decryptresponseifneeded", "label": "decryptResponseIfNeeded()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L337"}, {"id": "http_index_shouldretry", "label": "shouldRetry()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L361"}, {"id": "http_index_getmaxretriesforconfig", "label": "getMaxRetriesForConfig()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L372"}, {"id": "http_index_retryrequest", "label": "retryRequest()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L378"}, {"id": "http_index_delay", "label": "delay()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L395"}, {"id": "http_index_request", "label": "request()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L400"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_index_ts", "target": "axios", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/http/index.ts", "source_location": "L17", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_index_ts", "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/utils/http/index.ts", "source_location": "L18", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_index_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http_status", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-Line truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "label": "storage-release-review.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L1"}, {"id": "api_storage_release_review_fetchstoragereleasereviewlist", "label": "fetchStorageReleaseReviewList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L108"}, {"id": "api_storage_release_review_fetchstoragereleasereviewdetail", "label": "fetchStorageReleaseReviewDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L122"}, {"id": "api_storage_release_review_submitstoragereleasereview", "label": "submitStorageReleaseReview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L128"}, {"id": "api_storage_release_review_batchreviewstoragerelease", "label": "batchReviewStorageRelease()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L142"}, {"id": "api_storage_release_review_storesample", "label": "storeSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L154"}, {"id": "api_storage_release_review_batchstoresample", "label": "batchStoreSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L160"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_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/storage-release-review.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_fetchstoragereleasereviewlist", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L108", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_fetchstoragereleasereviewdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L122", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_submitstoragereleasereview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L128", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_batchreviewstoragerelease", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L142", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_storesample", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L154", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_storage_release_review_ts", "target": "api_storage_release_review_batchstoresample", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L160", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_storage_release_review_fetchstoragereleasereviewlist", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L116"}, {"caller_nid": "api_storage_release_review_fetchstoragereleasereviewdetail", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L123"}, {"caller_nid": "api_storage_release_review_submitstoragereleasereview", "callee": "put", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "source_location": "L136"}, {"caller_nid": "api_storage_release_review_batchreviewstoragerelease", "callee": "put", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/storage-release-review.ts", "sLine truncated
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskinputenhancedtable_vue", "label": "TaskInputEnhancedTable.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskexecutiondialog_vue", "label": "TaskExecutionDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_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"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry_ts", "target": "utils_status_tag_registry_createlabelgetter", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L29", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_status_tag_registry_ts", "target": "utils_status_tag_registry_createtagtypegetter", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/utils/status-tag-registry.ts", "source_location": "L33", "weight": 1.0}], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_taskoutputlist_vue", "label": "TaskOutputList.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskOutputList.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L42"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L57"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L64"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_tree_path", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_toapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_fromapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39", "weight": 1.0}, {"source": "users_wuLine truncated
|
||||
@@ -396,6 +396,17 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
|
||||
path: 'qc-material-usage',
|
||||
component: '/quality-inspection/qc-material-usage/index',
|
||||
meta: { title: '质检物料变动记录', keepAlive: true, menuCode: 'biz:qcMaterialUsage:list' }
|
||||
},
|
||||
{
|
||||
id: '70000000000000000000000000000011',
|
||||
name: 'QualityInspectionStorageReleaseReview',
|
||||
path: 'storage-release-review',
|
||||
component: '/quality-inspection/storage-release-review/index',
|
||||
meta: {
|
||||
title: '入库放行审核',
|
||||
keepAlive: true,
|
||||
menuCode: 'biz:storageReleaseReview:list'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -204,6 +204,12 @@ async function handleUnauthorizedResponse(
|
||||
throw createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
|
||||
}
|
||||
|
||||
// refresh-token 接口本身返回 401,说明 refreshToken 已失效,直接登出,避免死锁
|
||||
if (config.url && /\/auth\/refresh-token/.test(config.url)) {
|
||||
triggerLogoutWithDebounce(message)
|
||||
throw createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
|
||||
}
|
||||
|
||||
if ((config as ExtendedAxiosRequestConfig)._retry) {
|
||||
triggerLogoutWithDebounce(message)
|
||||
throw createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
|
||||
|
||||
@@ -325,4 +325,22 @@ export const packType: StatusRegistry = {
|
||||
export const packTypeLabel = createLabelGetter(packType)
|
||||
export const packTypeTagType = createTagTypeGetter(packType)
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// 入库放行审核域
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
export const storageReleaseConclusion: StatusRegistry = {
|
||||
tg: { label: '通过', tagType: 'success' },
|
||||
btg: { label: '不通过', tagType: 'danger' }
|
||||
}
|
||||
export const storageReleaseConclusionLabel = createLabelGetter(storageReleaseConclusion)
|
||||
export const storageReleaseConclusionTagType = createTagTypeGetter(storageReleaseConclusion)
|
||||
|
||||
export const storageStatus: StatusRegistry = {
|
||||
drk: { label: '待入库', tagType: 'warning' },
|
||||
yrk: { label: '已入库', tagType: 'success' }
|
||||
}
|
||||
export const storageStatusLabel = createLabelGetter(storageStatus)
|
||||
export const storageStatusTagType = createTagTypeGetter(storageStatus)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="已冻存" value="frozen" />
|
||||
<el-option label="生产中" value="in_production" />
|
||||
<el-option label="已售卖" value="sold" />
|
||||
<el-option label="已使用" value="used" />
|
||||
<el-option label="已作废" value="voided" />
|
||||
@@ -680,9 +681,11 @@
|
||||
function productStatusTag(status: string) {
|
||||
const map: Record<string, { label: string; type: string }> = {
|
||||
frozen: { label: '已冻存', type: 'success' },
|
||||
in_production: { label: '生产中', type: 'warning' },
|
||||
sold: { label: '已售卖', type: 'warning' },
|
||||
used: { label: '已使用', type: 'info' },
|
||||
voided: { label: '已作废', type: 'danger' },
|
||||
thawed: { label: '已复苏', type: 'info' },
|
||||
}
|
||||
return map[status] || { label: status, type: 'info' }
|
||||
}
|
||||
@@ -724,6 +727,8 @@
|
||||
/* ---- 编辑时状态选项 ---- */
|
||||
const editStatusOptions = [
|
||||
{ label: '已冻存', value: 'frozen' },
|
||||
{ label: '生产中', value: 'in_production' },
|
||||
{ label: '已复苏', value: 'thawed' },
|
||||
{ label: '已售卖', value: 'sold' },
|
||||
{ label: '已使用', value: 'used' },
|
||||
{ label: '已作废', value: 'voided' },
|
||||
@@ -1190,7 +1195,7 @@
|
||||
submitLoading.value = true
|
||||
try {
|
||||
syncImagesToForm()
|
||||
const shared = {
|
||||
const shared: Record<string, any> = {
|
||||
productName: formData.productName.trim(),
|
||||
productTypeId: formData.productTypeId || undefined,
|
||||
specId: formData.specId || undefined,
|
||||
@@ -1201,10 +1206,14 @@
|
||||
freezeDate: formData.freezeDate || undefined,
|
||||
unit: formData.unit?.trim() || undefined,
|
||||
expiryDate: formData.expiryDate || undefined,
|
||||
productStatus: formData.productStatus || undefined,
|
||||
productImages: formData.productImages?.trim() || undefined,
|
||||
remark: formData.remark?.trim() || undefined,
|
||||
}
|
||||
// 仅「已冻存」可变更状态;生产中/已复苏等系统态勿提交,避免后端校验失败
|
||||
const editableStatuses = ['frozen', 'sold', 'used', 'voided']
|
||||
if (!isEdit.value || editableStatuses.includes(formData.productStatus)) {
|
||||
shared.productStatus = formData.productStatus || undefined
|
||||
}
|
||||
if (isEdit.value && editingId.value) {
|
||||
await fetchUpdateCellProduct(editingId.value, {
|
||||
...shared,
|
||||
|
||||
@@ -50,13 +50,14 @@
|
||||
<el-form label-position="top" class="route-config-step-form">
|
||||
<div class="form-grid-4">
|
||||
<el-form-item label="工序名称">
|
||||
<el-input v-model="editor.selectedStep.value.processName" />
|
||||
<el-input v-model="editor.selectedStep.value.processName" :disabled="readonly" />
|
||||
</el-form-item>
|
||||
<el-form-item label="工序分类">
|
||||
<el-select
|
||||
v-model="editor.selectedStep.value.categoryId"
|
||||
placeholder="选择分类"
|
||||
clearable
|
||||
:disabled="readonly"
|
||||
>
|
||||
<el-option
|
||||
v-for="cat in editor.categoryOptions.value"
|
||||
@@ -67,10 +68,14 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备名称">
|
||||
<el-input v-model="editor.selectedStep.value.equipmentName" />
|
||||
<el-input v-model="editor.selectedStep.value.equipmentName" :disabled="readonly" />
|
||||
</el-form-item>
|
||||
<el-form-item label="预计时长(min)">
|
||||
<el-input v-model="editor.selectedStep.value.estimatedDuration" placeholder="如 2h" />
|
||||
<el-input
|
||||
v-model="editor.selectedStep.value.estimatedDuration"
|
||||
placeholder="如 2h"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" class="step-form-col-half">
|
||||
<el-input
|
||||
@@ -78,6 +83,7 @@
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="工序描述"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="关联SOP" class="step-form-col-half">
|
||||
@@ -86,6 +92,7 @@
|
||||
multiple
|
||||
placeholder="选择SOP文档"
|
||||
filterable
|
||||
:disabled="readonly"
|
||||
@change="editor.onSopIdsChange"
|
||||
>
|
||||
<el-option
|
||||
@@ -111,7 +118,7 @@
|
||||
|
||||
<el-tabs v-model="editor.activeSubTab.value" class="route-config-tabs">
|
||||
<el-tab-pane label="试剂" name="reagent">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<div v-if="!readonly" class="route-config-tab-toolbar">
|
||||
<el-button class="btn-main" type="primary" size="small" @click="editor.addReagentRow">
|
||||
<el-icon><Plus /></el-icon>添加试剂
|
||||
</el-button>
|
||||
@@ -119,8 +126,8 @@
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.reagents"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
class="list-table inner-table"
|
||||
max-height="400"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
@@ -129,14 +136,21 @@
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="试剂编码" min-width="110" align="center">
|
||||
<el-table-column label="试剂编码" min-width="110" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.reagentCode" placeholder="编码" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.reagentCode"
|
||||
placeholder="编码"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.reagentCode || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="试剂名称" min-width="160" align="center">
|
||||
<el-table-column label="试剂名称" min-width="160" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.reagentId"
|
||||
placeholder="请选择试剂"
|
||||
clearable
|
||||
@@ -156,20 +170,29 @@
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.reagentName || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格" min-width="100" align="center">
|
||||
<el-table-column label="规格" min-width="100" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.spec" placeholder="规格" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.spec"
|
||||
placeholder="规格"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.spec || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标准用量" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.standardUsage"
|
||||
placeholder="用量"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.standardUsage ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -180,16 +203,19 @@
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.actualUsage"
|
||||
placeholder="实际用量"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveReagents"
|
||||
/>
|
||||
<span v-else>{{ row.actualUsage ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="120" align="center">
|
||||
<el-table-column label="单位" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
@@ -208,19 +234,29 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.unit || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关键物料" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch v-model="row.isKeyMaterial" />
|
||||
<el-switch v-if="!readonly" v-model="row.isKeyMaterial" />
|
||||
<el-tag v-else :type="row.isKeyMaterial ? 'success' : 'info'" size="small">
|
||||
{{ row.isKeyMaterial ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<el-table-column label="备注" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.remark" placeholder="备注" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.remark || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button
|
||||
link
|
||||
@@ -240,7 +276,7 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="耗材" name="consumable">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<div v-if="!readonly" class="route-config-tab-toolbar">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
@@ -253,8 +289,8 @@
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.consumables"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
class="list-table inner-table"
|
||||
max-height="400"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
@@ -263,18 +299,21 @@
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="耗材编码" min-width="110" align="center">
|
||||
<el-table-column label="耗材编码" min-width="110" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.consumableCode"
|
||||
placeholder="编码"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.consumableCode || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="耗材名称" min-width="160" align="center">
|
||||
<el-table-column label="耗材名称" min-width="160" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.consumableId"
|
||||
placeholder="请选择耗材"
|
||||
clearable
|
||||
@@ -294,24 +333,29 @@
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.consumableName || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格型号" min-width="110" align="center">
|
||||
<el-table-column label="规格型号" min-width="110" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.specModel"
|
||||
placeholder="规格型号"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.specModel || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标准用量" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.standardUsage"
|
||||
placeholder="用量"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.standardUsage ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -322,16 +366,19 @@
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.actualUsage"
|
||||
placeholder="实际用量"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveConsumables"
|
||||
/>
|
||||
<span v-else>{{ row.actualUsage ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="120" align="center">
|
||||
<el-table-column label="单位" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
@@ -350,19 +397,29 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.unit || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="必需" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch v-model="row.isRequired" />
|
||||
<el-switch v-if="!readonly" v-model="row.isRequired" />
|
||||
<el-tag v-else :type="row.isRequired ? 'success' : 'info'" size="small">
|
||||
{{ row.isRequired ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<el-table-column label="备注" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.remark" placeholder="备注" class="inner-cell-input" />
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
/>
|
||||
<span v-else>{{ row.remark || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button
|
||||
link
|
||||
@@ -382,7 +439,7 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="产出物" name="output">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<div v-if="!readonly" class="route-config-tab-toolbar">
|
||||
<el-button class="btn-main" type="primary" size="small" @click="editor.addOutputRow">
|
||||
<el-icon><Plus /></el-icon>添加产出物
|
||||
</el-button>
|
||||
@@ -390,8 +447,8 @@
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.outputs"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
class="list-table inner-table"
|
||||
max-height="400"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
@@ -400,9 +457,10 @@
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="产出类型" min-width="150" align="center">
|
||||
<el-table-column label="产出类型" min-width="150" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.outputTypeId"
|
||||
placeholder="选择类型"
|
||||
filterable
|
||||
@@ -416,39 +474,46 @@
|
||||
:value="ot.id"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ outputTypeLabel(row.outputTypeId) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" min-width="150" align="center">
|
||||
<el-table-column label="名称" min-width="150" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.outputName"
|
||||
placeholder="名称"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveOutputs"
|
||||
/>
|
||||
<span v-else>{{ row.outputName || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="条码" min-width="150" align="center">
|
||||
<el-table-column label="条码" min-width="150" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.outputBarcode"
|
||||
placeholder="条码"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveOutputs"
|
||||
/>
|
||||
<span v-else>{{ row.outputBarcode || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<el-table-column label="备注" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveOutputs"
|
||||
/>
|
||||
<span v-else>{{ row.remark || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" size="small" @click="editor.removeOutputRow($index)"
|
||||
>删除</el-button
|
||||
@@ -464,7 +529,7 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="数据采集" name="dataCollection">
|
||||
<div class="route-config-tab-toolbar">
|
||||
<div v-if="!readonly" class="route-config-tab-toolbar">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
@@ -477,8 +542,8 @@
|
||||
<el-table
|
||||
:data="editor.selectedStep.value.dataCollections"
|
||||
stripe
|
||||
class="inner-table"
|
||||
max-height="300"
|
||||
class="list-table inner-table"
|
||||
max-height="400"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
@@ -487,19 +552,22 @@
|
||||
align="center"
|
||||
:index="(i: number) => i + 1"
|
||||
/>
|
||||
<el-table-column label="采集名称" min-width="180" align="center">
|
||||
<el-table-column label="采集名称" min-width="180" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.collectionName"
|
||||
placeholder="采集项名称"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveDataCollections"
|
||||
/>
|
||||
<span v-else>{{ row.collectionName || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" min-width="120" align="center">
|
||||
<el-table-column label="单位" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-if="!readonly"
|
||||
v-model="row.unit"
|
||||
placeholder="请选择单位"
|
||||
clearable
|
||||
@@ -519,6 +587,7 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<span v-else>{{ row.unit || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -529,24 +598,28 @@
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.collectionValue"
|
||||
placeholder="输入采集值"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveDataCollections"
|
||||
/>
|
||||
<span v-else>{{ row.collectionValue ?? '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="120" align="center">
|
||||
<el-table-column label="备注" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-if="!readonly"
|
||||
v-model="row.remark"
|
||||
placeholder="备注"
|
||||
class="inner-cell-input"
|
||||
@change="editor.saveDataCollections"
|
||||
/>
|
||||
<span v-else>{{ row.remark || '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<el-table-column v-if="!readonly" label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button
|
||||
link
|
||||
@@ -588,6 +661,11 @@
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
function outputTypeLabel(outputTypeId: string) {
|
||||
const item = props.editor.outputTypeOptions.value.find((ot) => ot.id === outputTypeId)
|
||||
return item?.typeName || item?.name || outputTypeId || '—'
|
||||
}
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
function sopAttachmentFullUrl(url: string) {
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
<el-tag v-if="editor.virtualOutputStatus.value === 'filled'" type="success" size="small"
|
||||
>已填写 {{ editor.taskOutputs.value.length }} 项</el-tag
|
||||
>
|
||||
<el-tag v-else type="danger" size="small">未填写</el-tag>
|
||||
<el-tag v-else type="info" size="small">暂无</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 分割线 -->
|
||||
@@ -266,7 +266,7 @@
|
||||
<!-- 任务输出详情 -->
|
||||
<template v-else-if="editor.selectedVirtualStep.value === 'output'">
|
||||
<div class="execution-detail-header">
|
||||
<h3>📤 产出(细胞产品)<span class="virtual-step-required">必填</span></h3>
|
||||
<h3>📤 产出(细胞产品)</h3>
|
||||
<div v-if="!readonly" class="execution-detail-actions">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
|
||||
@@ -16,7 +16,15 @@
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table :data="items" size="small" stripe border v-loading="loading" empty-text="暂无原材料">
|
||||
<el-table
|
||||
:data="items"
|
||||
class="list-table"
|
||||
stripe
|
||||
border
|
||||
height="420"
|
||||
v-loading="loading"
|
||||
empty-text="暂无原材料"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="56" align="center" />
|
||||
<el-table-column label="类型" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
@@ -88,7 +96,7 @@
|
||||
<el-dialog
|
||||
v-model="selectDialogVisible"
|
||||
title="选择细胞产品"
|
||||
width="1160px"
|
||||
width="1400px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
destroy-on-close
|
||||
@@ -157,6 +165,28 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="批次号"
|
||||
prop="batchNo"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="规格" prop="specName" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column
|
||||
label="预计细胞数量(亿)"
|
||||
prop="expectedCellCount"
|
||||
min-width="140"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ row.expectedCellCount ?? '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活性" prop="viability" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.viability != null ? row.viability : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -192,6 +222,10 @@
|
||||
passage?: string
|
||||
storageSlotName?: string
|
||||
productStatus?: string
|
||||
batchNo?: string
|
||||
specName?: string
|
||||
expectedCellCount?: number
|
||||
viability?: number
|
||||
}[]
|
||||
cellProductOptionsLoading: boolean
|
||||
}>(),
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
<!-- 列表 -->
|
||||
<el-table
|
||||
:data="items"
|
||||
size="small"
|
||||
class="list-table"
|
||||
stripe
|
||||
border
|
||||
height="420"
|
||||
v-loading="loading"
|
||||
empty-text="暂无产出产品"
|
||||
>
|
||||
@@ -38,6 +39,26 @@
|
||||
{{ row.storageSlotName || '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计细胞数量(亿)" min-width="130" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.expectedCellCount ?? '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="活性" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.viability != null ? row.viability : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="冻存日期" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatDateOnly(row.freezeDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="有效期" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatDateOnly(row.expiryDate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.quantity ?? '—' }}
|
||||
@@ -149,17 +170,42 @@
|
||||
emit('update:items', newItems)
|
||||
}
|
||||
|
||||
function handleDialogConfirm(data: Record<string, any>) {
|
||||
function formatDateOnly(val: unknown) {
|
||||
if (!val) return '—'
|
||||
const s = String(val)
|
||||
return s.length >= 10 ? s.slice(0, 10) : s
|
||||
}
|
||||
|
||||
function enrichOutputRow(row: Record<string, any>) {
|
||||
const productType = props.productTypeOptions.find((o) => o.id === row.productTypeId)
|
||||
const spec = props.specOptions.find((o) => o.id === row.specId)
|
||||
const slot = props.storageSlotOptions.find((o) => o.id === row.storageSlotId)
|
||||
const productCode = row.productCode || row.outputBarcode || ''
|
||||
const productName = row.productName || row.outputName || ''
|
||||
return {
|
||||
...row,
|
||||
productCode,
|
||||
productName,
|
||||
outputBarcode: productCode,
|
||||
outputName: productName,
|
||||
productTypeName: row.productTypeName || productType?.productTypeName || '',
|
||||
specName: row.specName || spec?.specName || '',
|
||||
storageSlotName: row.storageSlotName || slot?.name || ''
|
||||
}
|
||||
}
|
||||
|
||||
function handleDialogConfirm(data: Record<string, any> | Record<string, any>[]) {
|
||||
const rows = (Array.isArray(data) ? data : [data]).map(enrichOutputRow)
|
||||
const newItems = [...props.items]
|
||||
if (editingIndex.value >= 0) {
|
||||
// 编辑:替换原有项,保留 cellProductId
|
||||
const row = rows[0]
|
||||
if (!row) return
|
||||
newItems[editingIndex.value] = {
|
||||
...newItems[editingIndex.value],
|
||||
...data
|
||||
...row
|
||||
}
|
||||
} else {
|
||||
// 新增:添加到列表
|
||||
newItems.push(data)
|
||||
newItems.push(...rows)
|
||||
}
|
||||
emit('update:items', newItems)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
:title="isEdit ? '编辑产出产品' : '新增产出产品'"
|
||||
width="1000px"
|
||||
width="1080px"
|
||||
class="dialog-form-layout task-output-product-dialog"
|
||||
align-center
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
@opened="onDialogOpened"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
@@ -21,7 +22,12 @@
|
||||
<div class="form-grid-3">
|
||||
<!-- 产品编码 -->
|
||||
<el-form-item label="产品编码" prop="productCode">
|
||||
<el-input v-model="formData.productCode" placeholder="请输入产品编码" />
|
||||
<el-input
|
||||
v-model="formData.productCode"
|
||||
placeholder="请输入产品编码"
|
||||
:readonly="isEdit"
|
||||
@input="onBaseProductCodeInput"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 产品名称 -->
|
||||
<el-form-item label="产品名称" prop="productName">
|
||||
@@ -74,8 +80,8 @@
|
||||
<el-form-item label="批次号" prop="batchNo">
|
||||
<el-input v-model="formData.batchNo" placeholder="请输入批次号" />
|
||||
</el-form-item>
|
||||
<!-- 储物格位置 -->
|
||||
<el-form-item label="储物格位置">
|
||||
<!-- 储物格位置(编辑单条) -->
|
||||
<el-form-item v-if="isEdit" label="储物格位置">
|
||||
<el-select
|
||||
v-model="formData.storageSlotId"
|
||||
placeholder="请选择储物格"
|
||||
@@ -122,14 +128,16 @@
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 数量 -->
|
||||
<el-form-item label="数量">
|
||||
<!-- 入库份数 / 数量 -->
|
||||
<el-form-item :label="isEdit ? '数量' : '入库份数'" prop="quantity">
|
||||
<el-input-number
|
||||
v-model="formData.quantity"
|
||||
:min="0"
|
||||
:min="isEdit ? 0 : 1"
|
||||
:max="isEdit ? undefined : 50"
|
||||
:precision="0"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
@change="onCreateCountChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 单位 -->
|
||||
@@ -164,6 +172,21 @@
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 状态(编辑单条,对齐细胞产品库) -->
|
||||
<el-form-item v-if="isEdit" label="状态">
|
||||
<el-select
|
||||
v-model="formData.productStatus"
|
||||
style="width: 100%"
|
||||
:disabled="formData.productStatus !== 'frozen'"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in editStatusOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 产品图片 (full width) -->
|
||||
<el-form-item label="产品图片" class="form-col-span-2">
|
||||
<div class="attachment-upload">
|
||||
@@ -228,6 +251,45 @@
|
||||
<el-form-item label="备注" class="form-col-span-2">
|
||||
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="可选" />
|
||||
</el-form-item>
|
||||
<!-- 入库明细(新增多份) -->
|
||||
<div v-if="!isEdit" class="product-lines-section">
|
||||
<div class="product-lines-head">
|
||||
<span class="product-lines-title">入库明细</span>
|
||||
<span class="form-label-tip"
|
||||
>每份填写编码与储物格;多份时编码为「产品编码-01、-02…」</span
|
||||
>
|
||||
</div>
|
||||
<el-table :data="productLines" size="small" class="product-lines-table">
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column label="产品编码" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<el-input
|
||||
v-model="row.productCode"
|
||||
placeholder="产品编码"
|
||||
@input="onLineProductCodeInput"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="储物格位置" min-width="280">
|
||||
<template #default="{ row }">
|
||||
<el-select
|
||||
v-model="row.storageSlotId"
|
||||
placeholder="请选择储物格"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in storageSlotOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -258,10 +320,13 @@
|
||||
import { Plus, Close, Check, Delete, Download, Loading } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import { fetchNextCellProductCode } from '@/api/cell-product'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
|
||||
defineOptions({ name: 'TaskOutputProductDialog' })
|
||||
|
||||
type ProductLine = { productCode: string; storageSlotId: string }
|
||||
|
||||
type ProductImageFile = {
|
||||
id?: string
|
||||
fileName: string
|
||||
@@ -282,12 +347,21 @@
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
confirm: [data: Record<string, any>]
|
||||
confirm: [data: Record<string, any> | Record<string, any>[]]
|
||||
searchUnit: [query: string]
|
||||
}>()
|
||||
|
||||
const passageOptions = ['P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7', 'P8']
|
||||
|
||||
const editStatusOptions = [
|
||||
{ label: '已冻存', value: 'frozen' },
|
||||
{ label: '生产中', value: 'in_production' },
|
||||
{ label: '已售卖', value: 'sold' },
|
||||
{ label: '已使用', value: 'used' },
|
||||
{ label: '已作废', value: 'voided' },
|
||||
{ label: '已复苏', value: 'thawed' }
|
||||
]
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
function fullFileUrl(suffix: string) {
|
||||
if (!suffix) return ''
|
||||
@@ -296,13 +370,14 @@
|
||||
return `${minioBaseUrl}/${suffix}`
|
||||
}
|
||||
|
||||
const isEdit = computed(() => !!props.editItem?.cellProductId)
|
||||
const isEdit = computed(() => !!props.editItem)
|
||||
const formRef = ref()
|
||||
const submitLoading = ref(false)
|
||||
const productImageUploadRef = ref()
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const productImageFiles = ref<ProductImageFile[]>([])
|
||||
const productLines = ref<ProductLine[]>([{ productCode: '', storageSlotId: '' }])
|
||||
|
||||
const formData = reactive({
|
||||
cellProductId: '',
|
||||
@@ -319,44 +394,181 @@
|
||||
quantity: 1 as number,
|
||||
unit: '',
|
||||
expiryDate: '',
|
||||
productStatus: 'frozen',
|
||||
productImages: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
productCode: [{ required: true, message: '请输入产品编码', trigger: 'blur' }],
|
||||
productName: [{ required: true, message: '请输入产品名称', trigger: 'blur' }]
|
||||
productName: [{ required: true, message: '请输入产品名称', trigger: 'blur' }],
|
||||
batchNo: [{ required: true, message: '请输入批次号', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// Watch editItem to populate form
|
||||
watch(
|
||||
() => props.editItem,
|
||||
(item) => {
|
||||
if (item) {
|
||||
formData.cellProductId = item.cellProductId || ''
|
||||
formData.productCode = item.productCode || ''
|
||||
formData.productName = item.productName || ''
|
||||
formData.productTypeId = item.productTypeId || ''
|
||||
formData.specId = item.specId || ''
|
||||
formData.passage = item.passage || ''
|
||||
formData.batchNo = item.batchNo || ''
|
||||
formData.storageSlotId = item.storageSlotId || ''
|
||||
formData.expectedCellCount = item.expectedCellCount ?? undefined
|
||||
formData.viability = item.viability ?? undefined
|
||||
formData.freezeDate = item.freezeDate || ''
|
||||
formData.quantity = item.quantity ?? 1
|
||||
formData.unit = item.unit || ''
|
||||
formData.expiryDate = item.expiryDate || ''
|
||||
formData.productImages = item.productImages || ''
|
||||
formData.remark = item.remark || ''
|
||||
syncFormToImages(item.productImages || '')
|
||||
} else {
|
||||
resetForm()
|
||||
function normalizeDateValue(val: unknown): string {
|
||||
if (!val) return ''
|
||||
const s = String(val)
|
||||
return s.length >= 10 ? s.slice(0, 10) : s
|
||||
}
|
||||
|
||||
function populateFormFromEditItem(item: Record<string, any>) {
|
||||
formData.cellProductId = item.cellProductId || ''
|
||||
formData.productCode = item.productCode || item.outputBarcode || ''
|
||||
formData.productName = item.productName || item.outputName || ''
|
||||
formData.productTypeId = item.productTypeId || ''
|
||||
formData.specId = item.specId || ''
|
||||
formData.passage = item.passage || ''
|
||||
formData.batchNo = item.batchNo || ''
|
||||
formData.storageSlotId = item.storageSlotId || ''
|
||||
formData.expectedCellCount = item.expectedCellCount ?? undefined
|
||||
formData.viability = item.viability ?? undefined
|
||||
formData.freezeDate = normalizeDateValue(item.freezeDate)
|
||||
formData.quantity = item.quantity ?? 1
|
||||
formData.unit = item.unit || ''
|
||||
formData.expiryDate = normalizeDateValue(item.expiryDate)
|
||||
formData.productStatus = item.productStatus || 'frozen'
|
||||
formData.productImages = item.productImages || ''
|
||||
formData.remark = item.remark || ''
|
||||
syncFormToImages(item.productImages || '')
|
||||
}
|
||||
|
||||
async function initAddForm() {
|
||||
resetForm()
|
||||
formData.quantity = 1
|
||||
emit('searchUnit', '')
|
||||
await nextTick()
|
||||
formRef.value?.clearValidate?.()
|
||||
try {
|
||||
const res = await fetchNextCellProductCode()
|
||||
if (res?.code) {
|
||||
formData.productCode = res.code
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
} catch {
|
||||
/* 自动生成失败时用户可手动输入 */
|
||||
}
|
||||
applyProductCodeToLines()
|
||||
}
|
||||
|
||||
/** 按入库份数将主表单产品编码同步到入库明细(每份均带 -01、-02…,单份时为 -01) */
|
||||
function applyProductCodeToLines() {
|
||||
syncProductLines()
|
||||
syncLineCodesFromBase()
|
||||
}
|
||||
|
||||
function onDialogOpened() {
|
||||
if (props.editItem) {
|
||||
populateFormFromEditItem(props.editItem)
|
||||
emit('searchUnit', props.editItem.unit || '')
|
||||
nextTick(() => formRef.value?.clearValidate?.())
|
||||
} else {
|
||||
void initAddForm()
|
||||
}
|
||||
}
|
||||
|
||||
function createQuantityValue() {
|
||||
const n = Math.floor(Number(formData.quantity) || 0)
|
||||
if (n < 1) return 1
|
||||
return Math.min(n, 50)
|
||||
}
|
||||
|
||||
function stripProductCodeSuffix(code: string) {
|
||||
const c = (code || '').trim()
|
||||
const m = c.match(/^(.+)-(\d{2})$/)
|
||||
return m ? m[1].trim() : c
|
||||
}
|
||||
|
||||
function buildLineProductCode(base: string, index: number) {
|
||||
const b = stripProductCodeSuffix(base)
|
||||
if (!b) return ''
|
||||
return `${b}-${String(index + 1).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
function syncLineCodesFromBase() {
|
||||
const base = formData.productCode
|
||||
productLines.value.forEach((line, index) => {
|
||||
line.productCode = buildLineProductCode(base, index)
|
||||
})
|
||||
}
|
||||
|
||||
function syncProductLines() {
|
||||
const count = createQuantityValue()
|
||||
const prev = productLines.value.map((l) => ({ ...l }))
|
||||
const lines: ProductLine[] = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
lines.push({
|
||||
productCode: buildLineProductCode(formData.productCode, i),
|
||||
storageSlotId: prev[i]?.storageSlotId || ''
|
||||
})
|
||||
}
|
||||
productLines.value = lines.length ? lines : [{ productCode: '', storageSlotId: '' }]
|
||||
}
|
||||
|
||||
function onCreateCountChange() {
|
||||
if (!isEdit.value) {
|
||||
applyProductCodeToLines()
|
||||
}
|
||||
}
|
||||
|
||||
function onBaseProductCodeInput() {
|
||||
if (!isEdit.value) {
|
||||
applyProductCodeToLines()
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => formData.quantity,
|
||||
() => {
|
||||
if (!isEdit.value && props.modelValue) {
|
||||
applyProductCodeToLines()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function onLineProductCodeInput() {
|
||||
if (!isEdit.value && createQuantityValue() <= 1) {
|
||||
formData.productCode = stripProductCodeSuffix(productLines.value[0]?.productCode || '')
|
||||
}
|
||||
}
|
||||
|
||||
async function validateProductLines(): Promise<boolean> {
|
||||
if (isEdit.value) return true
|
||||
const count = createQuantityValue()
|
||||
for (let i = 0; i < productLines.value.length; i++) {
|
||||
const line = productLines.value[i]
|
||||
if (!line.productCode?.trim()) {
|
||||
ElMessage.warning(`请填写第 ${i + 1} 份产品编码`)
|
||||
return false
|
||||
}
|
||||
if (!line.storageSlotId) {
|
||||
ElMessage.warning(`请选择第 ${i + 1} 份产品的储物格位置`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (productLines.value.length !== count) {
|
||||
ElMessage.warning('入库明细行数与入库份数不一致,请调整入库份数')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function buildSharedResult(): Record<string, any> {
|
||||
return {
|
||||
productName: formData.productName.trim(),
|
||||
productTypeId: formData.productTypeId || undefined,
|
||||
specId: formData.specId || undefined,
|
||||
passage: formData.passage || undefined,
|
||||
batchNo: formData.batchNo.trim(),
|
||||
expectedCellCount: formData.expectedCellCount ?? undefined,
|
||||
viability: formData.viability ?? undefined,
|
||||
freezeDate: formData.freezeDate || undefined,
|
||||
unit: formData.unit?.trim() || undefined,
|
||||
expiryDate: formData.expiryDate || undefined,
|
||||
productImages: formData.productImages?.trim() || undefined,
|
||||
productStatus: formData.productStatus || 'frozen',
|
||||
remark: formData.remark?.trim() || undefined
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formData.cellProductId = ''
|
||||
formData.productCode = ''
|
||||
@@ -372,9 +584,11 @@
|
||||
formData.quantity = 1
|
||||
formData.unit = ''
|
||||
formData.expiryDate = ''
|
||||
formData.productStatus = 'frozen'
|
||||
formData.productImages = ''
|
||||
formData.remark = ''
|
||||
productImageFiles.value = []
|
||||
productLines.value = [{ productCode: '', storageSlotId: '' }]
|
||||
}
|
||||
|
||||
function fileNameFromUrl(url: string, index: number) {
|
||||
@@ -464,30 +678,29 @@
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!(await validateProductLines())) return
|
||||
await formRef.value?.validate?.()
|
||||
syncImagesToForm()
|
||||
submitLoading.value = true
|
||||
try {
|
||||
const result: Record<string, any> = {
|
||||
cellProductId: formData.cellProductId || undefined,
|
||||
productCode: formData.productCode.trim(),
|
||||
productName: formData.productName.trim(),
|
||||
productTypeId: formData.productTypeId || undefined,
|
||||
specId: formData.specId || undefined,
|
||||
passage: formData.passage || undefined,
|
||||
batchNo: formData.batchNo.trim() || undefined,
|
||||
storageSlotId: formData.storageSlotId || undefined,
|
||||
expectedCellCount: formData.expectedCellCount ?? undefined,
|
||||
viability: formData.viability ?? undefined,
|
||||
freezeDate: formData.freezeDate || undefined,
|
||||
quantity: formData.quantity,
|
||||
unit: formData.unit?.trim() || undefined,
|
||||
expiryDate: formData.expiryDate || undefined,
|
||||
productImages: formData.productImages?.trim() || undefined,
|
||||
productStatus: 'frozen',
|
||||
remark: formData.remark?.trim() || undefined
|
||||
const shared = buildSharedResult()
|
||||
if (isEdit.value) {
|
||||
emit('confirm', {
|
||||
...shared,
|
||||
cellProductId: formData.cellProductId || undefined,
|
||||
productCode: formData.productCode.trim(),
|
||||
storageSlotId: formData.storageSlotId || undefined,
|
||||
quantity: formData.quantity
|
||||
})
|
||||
} else {
|
||||
const results = productLines.value.map((line) => ({
|
||||
...shared,
|
||||
productCode: line.productCode.trim(),
|
||||
storageSlotId: line.storageSlotId,
|
||||
quantity: 1
|
||||
}))
|
||||
emit('confirm', results)
|
||||
}
|
||||
emit('confirm', result)
|
||||
emit('update:modelValue', false)
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
@@ -651,4 +864,27 @@
|
||||
.preview-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.product-lines-section {
|
||||
grid-column: 1 / -1;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.product-lines-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.product-lines-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--mint-text, #334155);
|
||||
}
|
||||
|
||||
.product-lines-table {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="page-container">
|
||||
<header class="page-head">
|
||||
<h1>入库生产工单</h1>
|
||||
<p class="page-desc">入库生产工单管理,查看工单详情、派发生产任务并完成待配置工单。</p>
|
||||
<p class="page-desc">入库生产工单管理,查看工单详情、派发生产任务并完成工单。</p>
|
||||
</header>
|
||||
|
||||
<section class="panel">
|
||||
@@ -102,7 +102,7 @@
|
||||
><el-icon><EditPen /></el-icon>编辑任务</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="row.workOrderStatus === 'dpz'"
|
||||
v-if="row.workOrderStatus === 'dpz' || row.workOrderStatus === 'jxz'"
|
||||
class="btn-op-item primary"
|
||||
@click="handleOpenDispatch(row)"
|
||||
>
|
||||
|
||||
@@ -107,6 +107,10 @@ export function useTaskStepEditor() {
|
||||
passage?: string
|
||||
storageSlotName?: string
|
||||
productStatus?: string
|
||||
batchNo?: string
|
||||
specName?: string
|
||||
expectedCellCount?: number
|
||||
viability?: number
|
||||
}>
|
||||
>([])
|
||||
const cellProductOptionsLoading = ref(false)
|
||||
@@ -391,7 +395,11 @@ export function useTaskStepEditor() {
|
||||
productTypeName: p.productTypeName || '',
|
||||
passage: p.passage || '',
|
||||
storageSlotName: p.storageSlotName || '',
|
||||
productStatus: p.productStatus || ''
|
||||
productStatus: p.productStatus || '',
|
||||
batchNo: p.batchNo || '',
|
||||
specName: p.specName || '',
|
||||
expectedCellCount: p.expectedCellCount ?? undefined,
|
||||
viability: p.viability ?? undefined
|
||||
}))
|
||||
for (const row of taskInputs.value) {
|
||||
if (
|
||||
@@ -570,6 +578,25 @@ export function useTaskStepEditor() {
|
||||
loadStorageSlotOptions()
|
||||
])
|
||||
}
|
||||
|
||||
function enrichTaskOutputRow(row: Record<string, any>) {
|
||||
const productType = productTypeOptions.value.find((o) => o.id === row.productTypeId)
|
||||
const spec = specOptions.value.find((o) => o.id === row.specId)
|
||||
const slot = storageSlotOptions.value.find((o) => o.id === row.storageSlotId)
|
||||
const productCode = row.productCode || row.outputBarcode || ''
|
||||
const productName = row.productName || row.outputName || ''
|
||||
return {
|
||||
...row,
|
||||
productCode,
|
||||
productName,
|
||||
outputBarcode: productCode,
|
||||
outputName: productName,
|
||||
productTypeName: row.productTypeName || productType?.productTypeName || '',
|
||||
specName: row.specName || spec?.specName || '',
|
||||
storageSlotName: row.storageSlotName || slot?.name || ''
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTaskDetail(overrideId?: string) {
|
||||
const id = overrideId || taskId.value
|
||||
if (!id) return
|
||||
@@ -586,7 +613,9 @@ export function useTaskStepEditor() {
|
||||
if (mode.value === 'execution') {
|
||||
taskDetail.value = detail
|
||||
taskInputs.value = (detail.inputs || []).map((i: Record<string, any>) => ({ ...i }))
|
||||
taskOutputs.value = (detail.outputs || []).map((o: Record<string, any>) => ({ ...o }))
|
||||
taskOutputs.value = (detail.outputs || []).map((o: Record<string, any>) =>
|
||||
enrichTaskOutputRow(o)
|
||||
)
|
||||
for (const row of taskInputs.value) {
|
||||
if (
|
||||
row.inputType === 'cell_product' &&
|
||||
@@ -1007,10 +1036,6 @@ export function useTaskStepEditor() {
|
||||
}
|
||||
async function saveTaskOutputs() {
|
||||
if (!taskDetail.value?.id) return
|
||||
if (!taskOutputs.value.length) {
|
||||
ElMessage.warning('请至少添加一项产出(细胞产品)')
|
||||
return
|
||||
}
|
||||
outputSaving.value = true
|
||||
try {
|
||||
await fetchSetTaskOutputs(taskDetail.value.id, taskOutputs.value)
|
||||
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
<!-- 入库放行审核 — 审核弹窗(支持单条和批量) -->
|
||||
<!-- 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.review?.protocolNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="review-summary-row">
|
||||
<span class="review-summary-label">样本编码</span>
|
||||
<span class="review-summary-value">{{ singleDetail.review?.sampleCode || '—' }}</span>
|
||||
</div>
|
||||
<div class="review-summary-row">
|
||||
<span class="review-summary-label">样本名称</span>
|
||||
<span class="review-summary-value">{{ singleDetail.review?.sampleName || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else-if="reviewIds.length > 1" class="audit-target-hint">
|
||||
将对已选
|
||||
<strong>{{ reviewIds.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="附件">
|
||||
<div class="attachment-upload-area">
|
||||
<div class="attachment-list">
|
||||
<el-upload
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:multiple="true"
|
||||
accept="image/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.txt,.zip"
|
||||
@change="handleAttachmentChange"
|
||||
>
|
||||
<div class="upload-card">
|
||||
<el-icon :size="20" 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">
|
||||
<template v-if="isImageFile(file) && file.fileUrl">
|
||||
<img :src="fullFileUrl(file.fileUrl)" :alt="file.fileName" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="attachment-preview__icon attachment-preview__icon--doc">
|
||||
<el-icon :size="22"><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="removeAttachment(fIdx)">
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="upload-hint">支持图片、文档等,单文件不超过 20MB。</p>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Check, Close, Plus, Document, Delete } from '@element-plus/icons-vue'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import {
|
||||
fetchStorageReleaseReviewDetail,
|
||||
submitStorageReleaseReview,
|
||||
batchReviewStorageRelease,
|
||||
type StorageReleaseReviewDetail
|
||||
} from '@/api/storage-release-review'
|
||||
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
|
||||
|
||||
type AttachmentItem = {
|
||||
id: string
|
||||
fileName: string
|
||||
fileUrl: string
|
||||
fileType: string
|
||||
_uploading?: boolean
|
||||
}
|
||||
|
||||
defineOptions({ name: 'StorageReleaseReviewDialog' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
reviewIds: string[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', val: boolean): void
|
||||
(e: 'submitted'): void
|
||||
}>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const submitting = ref(false)
|
||||
const singleDetail = ref<StorageReleaseReviewDetail | null>(null)
|
||||
const attachmentFiles = ref<AttachmentItem[]>([])
|
||||
|
||||
const isSingleMode = computed(() => props.reviewIds.length === 1)
|
||||
|
||||
const form = reactive({
|
||||
reviewConclusion: 'tg',
|
||||
reviewRemark: ''
|
||||
})
|
||||
|
||||
const rules: FormRules = {
|
||||
reviewConclusion: [{ required: true, message: '请选择审核结论', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const dialogTitle = computed(() =>
|
||||
props.reviewIds.length > 1 ? `批量审核(${props.reviewIds.length} 条)` : '入库放行审核'
|
||||
)
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
function fullFileUrl(url: string) {
|
||||
if (!url) return ''
|
||||
if (/^https?:\/\//i.test(url)) return url
|
||||
return minioBaseUrl ? `${minioBaseUrl}/${url.replace(/^\//, '')}` : url
|
||||
}
|
||||
|
||||
function isImageFile(file: AttachmentItem) {
|
||||
const t = file.fileType || ''
|
||||
const n = file.fileName || ''
|
||||
return t.startsWith('image/') || /\.(png|jpe?g|gif|webp|bmp|svg)$/i.test(n)
|
||||
}
|
||||
|
||||
function removeAttachment(index: number) {
|
||||
attachmentFiles.value.splice(index, 1)
|
||||
}
|
||||
|
||||
let _pendingList: AttachmentItem[] | null = null
|
||||
|
||||
function _getList(): AttachmentItem[] {
|
||||
if (_pendingList) return _pendingList
|
||||
return attachmentFiles.value
|
||||
}
|
||||
|
||||
async function handleAttachmentChange(uploadFile: { raw?: File }) {
|
||||
const file = uploadFile.raw
|
||||
if (!file) return
|
||||
const maxSizeMb = 20
|
||||
if (file.size / 1024 / 1024 > maxSizeMb) {
|
||||
ElMessage.error(`文件大小不能超过 ${maxSizeMb}MB`)
|
||||
return
|
||||
}
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
||||
const uploading: AttachmentItem = {
|
||||
id: tempId,
|
||||
fileName: file.name,
|
||||
fileUrl: '',
|
||||
fileType: file.type || '',
|
||||
_uploading: true
|
||||
}
|
||||
const current = _getList()
|
||||
const listWithTemp = [...current, uploading]
|
||||
_pendingList = listWithTemp
|
||||
attachmentFiles.value = listWithTemp
|
||||
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('folder', 'storage-release-review')
|
||||
fd.append('file', file)
|
||||
const res = await fetchUploadAttachment(fd)
|
||||
const latest = _pendingList || attachmentFiles.value
|
||||
const updated = latest.map((a) =>
|
||||
a.id === tempId
|
||||
? {
|
||||
id: res.id || tempId,
|
||||
fileName: file.name || res.fileName,
|
||||
fileUrl: res.fileUrl,
|
||||
fileType: res.fileType || file.type || '',
|
||||
_uploading: false
|
||||
}
|
||||
: a
|
||||
)
|
||||
_pendingList = updated
|
||||
attachmentFiles.value = updated
|
||||
} catch {
|
||||
const latest = _pendingList || attachmentFiles.value
|
||||
const cleaned = latest.filter((a) => a.id !== tempId)
|
||||
_pendingList = cleaned
|
||||
attachmentFiles.value = cleaned
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.modelValue, props.reviewIds] as const,
|
||||
async ([vis, ids]) => {
|
||||
if (!vis || !ids.length) {
|
||||
singleDetail.value = null
|
||||
return
|
||||
}
|
||||
if (ids.length === 1) {
|
||||
try {
|
||||
singleDetail.value = await fetchStorageReleaseReviewDetail(ids[0])
|
||||
} catch {
|
||||
singleDetail.value = null
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function resetForm() {
|
||||
form.reviewConclusion = 'tg'
|
||||
form.reviewRemark = ''
|
||||
attachmentFiles.value = []
|
||||
_pendingList = null
|
||||
singleDetail.value = null
|
||||
formRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
const valid = await formRef.value?.validate().catch(() => false)
|
||||
if (!valid || !props.reviewIds.length) return
|
||||
|
||||
const attachmentUrls = attachmentFiles.value
|
||||
.filter((f) => f.fileUrl)
|
||||
.map((f) => f.fileUrl)
|
||||
.join(',')
|
||||
|
||||
const payload = {
|
||||
reviewConclusion: form.reviewConclusion,
|
||||
reviewRemark: form.reviewRemark.trim() || undefined,
|
||||
reviewAttachments: attachmentUrls || undefined
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
if (props.reviewIds.length === 1) {
|
||||
await submitStorageReleaseReview(props.reviewIds[0], payload)
|
||||
ElMessage.success('审核成功')
|
||||
} else {
|
||||
await withFullscreenLoading(async () => {
|
||||
await batchReviewStorageRelease({
|
||||
ids: props.reviewIds,
|
||||
...payload
|
||||
})
|
||||
ElMessage.success(`批量审核成功,共 ${props.reviewIds.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;
|
||||
}
|
||||
|
||||
.attachment-upload-area {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.attachment-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.upload-card {
|
||||
width: 120px;
|
||||
height: 88px;
|
||||
border: 1px dashed #a7f3d0;
|
||||
border-radius: 8px;
|
||||
background: #f0fdf4;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s;
|
||||
|
||||
&:hover {
|
||||
border-color: #10b981;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-card-text {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.attachment-item {
|
||||
width: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
|
||||
&.is-uploading {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8fafc;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-preview__icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&--doc {
|
||||
background: #e0f2fe;
|
||||
color: #0284c7;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.attachment-name {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 11px;
|
||||
color: #334155;
|
||||
line-height: 1.4;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.attachment-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
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: #ef4444;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
margin: 8px 0 0;
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
</style>
|
||||
+403
@@ -0,0 +1,403 @@
|
||||
<!-- 入库放行审核 — 只读查看 -->
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<SystemViewDialog
|
||||
v-model:visible="innerVisible"
|
||||
title="入库放行审核详情"
|
||||
width="960px"
|
||||
:grid-columns="2"
|
||||
:icon="CircleCheck"
|
||||
:meta="detail?.review?.protocolNo || ''"
|
||||
:sections="viewSections"
|
||||
:loading="loading"
|
||||
append-to-body
|
||||
>
|
||||
<template #qc-tasks>
|
||||
<div v-if="detail?.qcInfo?.tasks?.length" class="view-section-slot">
|
||||
<div class="qc-task-cards">
|
||||
<div v-for="task in detail.qcInfo.tasks" :key="task.id" class="qc-task-card">
|
||||
<div class="qc-task-card__header">
|
||||
<span class="qc-task-card__type">
|
||||
<el-tag type="info" size="small">{{
|
||||
task.taskTypeLabel || task.taskType || '—'
|
||||
}}</el-tag>
|
||||
</span>
|
||||
<span class="qc-task-card__conclusion">
|
||||
<el-tag
|
||||
v-if="task.conclusion"
|
||||
:type="task.conclusion === 'tg' ? 'success' : 'danger'"
|
||||
size="small"
|
||||
>{{ task.conclusionLabel || task.conclusion }}</el-tag
|
||||
>
|
||||
<span v-else class="qc-kv__muted">—</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="qc-task-card__body">
|
||||
<div class="qc-kv">
|
||||
<span class="qc-kv__label">任务状态</span>
|
||||
<span class="qc-kv__value">
|
||||
<el-tag
|
||||
:type="
|
||||
task.taskStatus === 'ywc'
|
||||
? 'success'
|
||||
: task.taskStatus === 'jxz'
|
||||
? 'warning'
|
||||
: 'info'
|
||||
"
|
||||
size="small"
|
||||
>{{ task.taskStatusLabel || task.taskStatus || '—' }}</el-tag
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="view-section-slot"><p class="view-empty-hint">暂无质检任务</p></div>
|
||||
</template>
|
||||
|
||||
<template #production-tasks>
|
||||
<div v-if="detail?.productionInfo?.tasks?.length" class="view-section-slot">
|
||||
<div class="qc-task-cards">
|
||||
<div v-for="task in detail.productionInfo.tasks" :key="task.id" class="qc-task-card">
|
||||
<div class="qc-task-card__header">
|
||||
<span class="qc-task-card__name">{{ task.taskName || task.taskNo || '—' }}</span>
|
||||
<span class="qc-task-card__conclusion">
|
||||
<el-tag
|
||||
:type="
|
||||
task.taskStatus === 'ywc'
|
||||
? 'success'
|
||||
: task.taskStatus === 'jxz'
|
||||
? 'warning'
|
||||
: 'info'
|
||||
"
|
||||
size="small"
|
||||
>{{ task.taskStatusLabel || task.taskStatus || '—' }}</el-tag
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<div class="qc-task-card__body">
|
||||
<div class="qc-kv">
|
||||
<span class="qc-kv__label">任务号</span>
|
||||
<span class="qc-kv__value">{{ task.taskNo || '—' }}</span>
|
||||
</div>
|
||||
<div class="qc-kv">
|
||||
<span class="qc-kv__label">开始时间</span>
|
||||
<span class="qc-kv__value">{{ task.startedTime || '—' }}</span>
|
||||
</div>
|
||||
<div class="qc-kv">
|
||||
<span class="qc-kv__label">完成时间</span>
|
||||
<span class="qc-kv__value">{{ task.completedTime || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="view-section-slot"><p class="view-empty-hint">暂无生产任务</p></div>
|
||||
</template>
|
||||
|
||||
<template #review-attachments>
|
||||
<div class="view-section-slot">
|
||||
<ProtocolAttachmentReadonly
|
||||
:files="reviewAttachmentFiles"
|
||||
@preview="handleAttachmentPreview"
|
||||
@download="handleAttachmentDownload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</SystemViewDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CircleCheck } 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 ProtocolAttachmentReadonly from '@/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue'
|
||||
import {
|
||||
fetchStorageReleaseReviewDetail,
|
||||
type StorageReleaseReview,
|
||||
type StorageReleaseReviewDetail,
|
||||
type ProtocolInfo,
|
||||
type TransportInfo,
|
||||
type ReceptionInfo,
|
||||
type QcInfo,
|
||||
type ProductionInfo
|
||||
} from '@/api/storage-release-review'
|
||||
import {
|
||||
receptionSourceLabel,
|
||||
receptionSourceTagType,
|
||||
storageReleaseConclusionLabel,
|
||||
storageReleaseConclusionTagType,
|
||||
acceptConclusionLabel,
|
||||
acceptConclusionTagType,
|
||||
transportTypeLabel
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { downloadAttachmentFile } from '@/utils/download-attachment'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
reviewId: 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<StorageReleaseReviewDetail | null>(null)
|
||||
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
watch(
|
||||
() => [props.visible, props.reviewId] as const,
|
||||
async ([vis, id]) => {
|
||||
if (!vis || !id) {
|
||||
detail.value = null
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
detail.value = await fetchStorageReleaseReviewDetail(id)
|
||||
} catch {
|
||||
detail.value = null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function formatVal(v: unknown) {
|
||||
if (v === null || v === undefined || v === '') return '—'
|
||||
return String(v)
|
||||
}
|
||||
|
||||
const reviewAttachmentFiles = computed(() => {
|
||||
const urls = detail.value?.review?.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 viewSections = computed<ViewSection[]>(() => {
|
||||
const d = detail.value
|
||||
if (!d) return []
|
||||
const rev: Partial<StorageReleaseReview> = d.review || {}
|
||||
const proto: Partial<ProtocolInfo> = d.protocolInfo || {}
|
||||
const transport: Partial<TransportInfo> = d.transportInfo || {}
|
||||
const reception: Partial<ReceptionInfo> = d.receptionInfo || {}
|
||||
const qc: Partial<QcInfo> = d.qcInfo || {}
|
||||
const production: Partial<ProductionInfo> = d.productionInfo || {}
|
||||
|
||||
const reviewConclusionValue = rev.reviewConclusion || ''
|
||||
const reviewConclusionDisplay = reviewConclusionValue
|
||||
? storageReleaseConclusionLabel(reviewConclusionValue)
|
||||
: '待审核'
|
||||
const reviewConclusionTagType = reviewConclusionValue
|
||||
? storageReleaseConclusionTagType(reviewConclusionValue)
|
||||
: 'info'
|
||||
|
||||
return [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{ label: '协议编号', value: proto.protocolNo || rev.protocolNo || '—' },
|
||||
{
|
||||
label: '协议类别',
|
||||
value: receptionSourceLabel(rev.receptionSource || ''),
|
||||
tagType: receptionSourceTagType(rev.receptionSource || '')
|
||||
},
|
||||
{ label: '样本编码', value: proto.sampleCode || rev.sampleCode || '—' },
|
||||
{ label: '样本名称', value: rev.sampleName || '—' },
|
||||
{ label: '样本类型', value: proto.sampleTypeName || '—' },
|
||||
{ label: '母亲姓名', value: proto.motherName || '—' },
|
||||
{ label: '母亲电话', value: proto.motherPhone || '—' },
|
||||
{ label: '新生儿姓名', value: proto.newbornName || '—' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '运输信息',
|
||||
fields: transport.transportType
|
||||
? [
|
||||
{ label: '运输方式', value: transportTypeLabel(transport.transportType) },
|
||||
...(transport.transportType === 'kdys'
|
||||
? [{ label: '快递单号', value: transport.waybillNo || '—' }]
|
||||
: [
|
||||
{ label: '携带人', value: transport.carrierName || '—' },
|
||||
{ label: '携带人电话', value: transport.carrierPhone || '—' }
|
||||
]),
|
||||
{ label: '预计到达', value: transport.etaTime || '—' }
|
||||
]
|
||||
: []
|
||||
},
|
||||
{
|
||||
title: '接收信息',
|
||||
fields: reception.receptionTime
|
||||
? [
|
||||
{ label: '接收时间', value: reception.receptionTime || '—' },
|
||||
{ label: '接收经手人', value: reception.handlerName || '—' },
|
||||
{
|
||||
label: '接收结论',
|
||||
value: acceptConclusionLabel(reception.acceptConclusion || ''),
|
||||
tagType: acceptConclusionTagType(reception.acceptConclusion || '')
|
||||
},
|
||||
{ label: '接收温度(℃)', value: formatVal(reception.receiveTemperature) },
|
||||
{ label: '体积(ml)', value: formatVal(reception.volumeMl) }
|
||||
]
|
||||
: []
|
||||
},
|
||||
{
|
||||
title: '质检信息',
|
||||
slot: 'qc-tasks',
|
||||
fields: [
|
||||
{ label: '工单号', value: qc.workOrderNo || '—' },
|
||||
{
|
||||
label: '工单状态',
|
||||
value:
|
||||
qc.workOrderStatus === 'ywc'
|
||||
? '已完成'
|
||||
: qc.workOrderStatus === 'ypz'
|
||||
? '已配置'
|
||||
: qc.workOrderStatus || '—',
|
||||
tagType:
|
||||
qc.workOrderStatus === 'ywc'
|
||||
? 'success'
|
||||
: qc.workOrderStatus === 'ypz'
|
||||
? 'warning'
|
||||
: 'info'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '生产信息',
|
||||
slot: 'production-tasks',
|
||||
fields: [
|
||||
{ label: '工单号', value: production.workOrderNo || '—' },
|
||||
{
|
||||
label: '工单状态',
|
||||
value:
|
||||
production.workOrderStatus === 'ywc'
|
||||
? '已完成'
|
||||
: production.workOrderStatus === 'jxz'
|
||||
? '执行中'
|
||||
: production.workOrderStatus || '—',
|
||||
tagType:
|
||||
production.workOrderStatus === 'ywc'
|
||||
? 'success'
|
||||
: production.workOrderStatus === 'jxz'
|
||||
? 'warning'
|
||||
: 'info'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '放行审核',
|
||||
slot: 'review-attachments',
|
||||
fields: [
|
||||
{
|
||||
label: '审核结论',
|
||||
value: reviewConclusionDisplay,
|
||||
tagType: reviewConclusionTagType
|
||||
},
|
||||
{ label: '审核人', value: rev.reviewByName || '—' },
|
||||
{ label: '审核时间', value: rev.reviewTime || '—' },
|
||||
{ label: '审核备注', value: rev.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-empty-hint {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.qc-task-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.qc-task-card {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.qc-task-card__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
|
||||
.qc-task-card__name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.qc-task-card__body {
|
||||
padding: 10px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.qc-kv {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.qc-kv__label {
|
||||
color: #94a3b8;
|
||||
flex-shrink: 0;
|
||||
min-width: 56px;
|
||||
}
|
||||
|
||||
.qc-kv__value {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.qc-kv__muted {
|
||||
color: #94a3b8;
|
||||
font-weight: 400;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,398 @@
|
||||
<!-- 入库放行审核 -->
|
||||
<!-- 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.receptionSource"
|
||||
clearable
|
||||
placeholder="全部"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in receptionSourceOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</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>
|
||||
<el-form-item label="入库状态">
|
||||
<el-select
|
||||
v-model="searchForm.storageStatus"
|
||||
clearable
|
||||
placeholder="全部"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in storageStatusOptions"
|
||||
: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>
|
||||
<el-button class="btn-ghost" :disabled="!canBatchStore" @click="handleBatchStore">
|
||||
<el-icon><Box /></el-icon>
|
||||
批量入库
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-body list-wrap">
|
||||
<el-table
|
||||
class="list-table"
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
@row-dblclick="handleView"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" header-align="center" />
|
||||
<el-table-column
|
||||
prop="protocolNo"
|
||||
label="协议编号"
|
||||
width="160"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="sampleCode"
|
||||
label="样本编码"
|
||||
width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="sampleName"
|
||||
label="样本名称"
|
||||
width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="receptionSource"
|
||||
label="协议类别"
|
||||
width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="receptionSourceTagType(row.receptionSource || '')" size="small">
|
||||
{{ receptionSourceLabel(row.receptionSource || '') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="reviewConclusion"
|
||||
label="审核结论"
|
||||
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="storageStatus"
|
||||
label="入库状态"
|
||||
width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="storageStatusTagType(row.storageStatus || 'drk')" size="small">
|
||||
{{ storageStatusLabel(row.storageStatus || 'drk') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="reviewByName"
|
||||
label="审核人"
|
||||
width="100"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="reviewTime"
|
||||
label="审核时间"
|
||||
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="openReviewDialog(row)">
|
||||
<el-icon><CircleCheck /></el-icon>审核
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.reviewConclusion === 'tg' && row.storageStatus === 'drk'"
|
||||
class="btn-op-item"
|
||||
@click="handleStore(row)"
|
||||
>
|
||||
<el-icon><Box /></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>
|
||||
|
||||
<StorageReleaseViewDialog v-model:visible="viewVisible" :review-id="viewReviewId" />
|
||||
|
||||
<StorageReleaseReviewDialog
|
||||
v-model="reviewDialogVisible"
|
||||
:review-ids="reviewDialogIds"
|
||||
@submitted="onReviewSubmitted"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { View, Search, RefreshLeft, CircleCheck, Box } from '@element-plus/icons-vue'
|
||||
import StorageReleaseViewDialog from './StorageReleaseViewDialog.vue'
|
||||
import StorageReleaseReviewDialog from './StorageReleaseReviewDialog.vue'
|
||||
import {
|
||||
fetchStorageReleaseReviewList,
|
||||
storeSample,
|
||||
batchStoreSample,
|
||||
type StorageReleaseReview
|
||||
} from '@/api/storage-release-review'
|
||||
import {
|
||||
receptionSourceLabel,
|
||||
receptionSourceTagType,
|
||||
storageReleaseConclusionLabel,
|
||||
storageReleaseConclusionTagType,
|
||||
storageStatusLabel,
|
||||
storageStatusTagType
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
|
||||
|
||||
defineOptions({ name: 'QualityInspectionStorageReleaseReview' })
|
||||
|
||||
const receptionSourceOptions = [
|
||||
{ value: 'cell_storage', label: '细胞存储' },
|
||||
{ value: 'research_donation', label: '科研捐赠' }
|
||||
]
|
||||
|
||||
const reviewConclusionOptions = [
|
||||
{ value: 'tg', label: '通过' },
|
||||
{ value: 'btg', label: '不通过' }
|
||||
]
|
||||
|
||||
const storageStatusOptions = [
|
||||
{ value: 'drk', label: '待入库' },
|
||||
{ value: 'yrk', label: '已入库' }
|
||||
]
|
||||
|
||||
const searchForm = reactive({
|
||||
receptionSource: '',
|
||||
reviewConclusion: '',
|
||||
storageStatus: '',
|
||||
keyword: ''
|
||||
})
|
||||
const tableData = ref<StorageReleaseReview[]>([])
|
||||
const selectedRows = ref<StorageReleaseReview[]>([])
|
||||
const loading = ref(false)
|
||||
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
|
||||
|
||||
const handleSelectionChange = (rows: StorageReleaseReview[]) => {
|
||||
selectedRows.value = rows
|
||||
}
|
||||
|
||||
const canBatchReview = computed(() => selectedRows.value.length > 0)
|
||||
|
||||
const canBatchStore = computed(
|
||||
() =>
|
||||
selectedRows.value.length > 0 &&
|
||||
selectedRows.value.every((r) => r.reviewConclusion === 'tg' && r.storageStatus === 'drk')
|
||||
)
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await fetchStorageReleaseReviewList({
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize,
|
||||
receptionSource: searchForm.receptionSource || undefined,
|
||||
reviewConclusion: searchForm.reviewConclusion || undefined,
|
||||
storageStatus: searchForm.storageStatus || 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.receptionSource = ''
|
||||
searchForm.reviewConclusion = ''
|
||||
searchForm.storageStatus = ''
|
||||
searchForm.keyword = ''
|
||||
pagination.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
const viewVisible = ref(false)
|
||||
const viewReviewId = ref('')
|
||||
|
||||
function handleView(row: StorageReleaseReview) {
|
||||
viewReviewId.value = String(row.id)
|
||||
viewVisible.value = true
|
||||
}
|
||||
|
||||
const reviewDialogVisible = ref(false)
|
||||
const reviewDialogIds = ref<string[]>([])
|
||||
|
||||
function openReviewDialog(row?: StorageReleaseReview) {
|
||||
const rows = row ? [row] : selectedRows.value
|
||||
if (!rows.length) {
|
||||
ElMessage.warning('请先选择要审核的记录')
|
||||
return
|
||||
}
|
||||
reviewDialogIds.value = rows.map((r) => String(r.id))
|
||||
reviewDialogVisible.value = true
|
||||
}
|
||||
|
||||
function onReviewSubmitted() {
|
||||
selectedRows.value = []
|
||||
loadData()
|
||||
}
|
||||
|
||||
async function handleStore(row: StorageReleaseReview) {
|
||||
await ElMessageBox.confirm(`确认将样本「${row.sampleCode || ''}」执行入库操作?`, '入库确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
await storeSample(String(row.id))
|
||||
ElMessage.success('入库成功')
|
||||
loadData()
|
||||
}
|
||||
|
||||
async function handleBatchStore() {
|
||||
if (!selectedRows.value.length) return
|
||||
await ElMessageBox.confirm(
|
||||
`确认将选中的 ${selectedRows.value.length} 条记录执行入库操作?`,
|
||||
'批量入库',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
await withFullscreenLoading(async () => {
|
||||
const ids = selectedRows.value.map((r) => String(r.id))
|
||||
await batchStoreSample({ ids })
|
||||
ElMessage.success(`批量入库成功,共 ${ids.length} 条`)
|
||||
selectedRows.value = []
|
||||
loadData()
|
||||
}, fullscreenLoadingText.save)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
Reference in new issue
Block a user