This commit is contained in:
zlwu committed 2026-05-21 12:53:37 +08:00
1 parent ad0e849f6d
commit 14f1a84bb3
32 files changed
+8200 -5194

No files matched your search

+6
View File
@@ -4,6 +4,8 @@ export function fetchCellProductList(params?: {
page?: number
pageSize?: number
keyword?: string
productStatus?: string
passage?: string
}) {
return request.get<{
list: any[]
@@ -29,3 +31,7 @@ export function fetchUpdateCellProduct(id: string, data: Record<string, any>) {
export function fetchDeleteCellProduct(id: string) {
return request.del({ url: `/web/admin/cell-products/${id}` })
}
export function fetchNextCellProductCode() {
return request.get<{ code: string }>({ url: '/web/admin/cell-products/next-code' })
}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1"}, {"id": "process_route_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1024"}, {"id": "process_route_index_get", "label": "get()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1089"}, {"id": "process_route_index_set", "label": "set()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1099"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "process_route_index_if", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1024", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "process_route_index_get", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1089", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_process_route_index_vue", "target": "process_route_index_set", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1099", "weight": 1.0}], "raw_calls": [{"caller_nid": "process_route_index_if", "callee": "mapReagentOptionLabel", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1026"}, {"caller_nid": "process_route_index_get", "callee": "isArray", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1093"}, {"caller_nid": "process_route_index_get", "callee": "filter", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1093"}, {"caller_nid": "process_route_index_get", "callee": "filter", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1094"}, {"caller_nid": "process_route_index_get", "callee": "map", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1094"}, {"caller_nid": "process_route_index_get", "callee": "split", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1094"}, {"caller_nid": "process_route_index_get", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1094"}, {"caller_nid": "process_route_index_set", "callee": "join", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-route/index.vue", "source_location": "L1101"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product_ts", "label": "cell-product.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L1"}, {"id": "api_cell_product_fetchcellproductlist", "label": "fetchCellProductList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L3"}, {"id": "api_cell_product_fetchcellproductdetail", "label": "fetchCellProductDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L19"}, {"id": "api_cell_product_fetchcreatecellproduct", "label": "fetchCreateCellProduct()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L23"}, {"id": "api_cell_product_fetchupdatecellproduct", "label": "fetchUpdateCellProduct()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L27"}, {"id": "api_cell_product_fetchdeletecellproduct", "label": "fetchDeleteCellProduct()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L31"}, {"id": "api_cell_product_fetchnextcellproductcode", "label": "fetchNextCellProductCode()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L35"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product_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/cell-product.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product_ts", "target": "api_cell_product_fetchcellproductlist", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L3", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product_ts", "target": "api_cell_product_fetchcellproductdetail", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L19", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product_ts", "target": "api_cell_product_fetchcreatecellproduct", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L23", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product_ts", "target": "api_cell_product_fetchupdatecellproduct", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L27", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product_ts", "target": "api_cell_product_fetchdeletecellproduct", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L31", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product_ts", "target": "api_cell_product_fetchnextcellproductcode", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L35", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_cell_product_fetchcellproductlist", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L10"}, {"caller_nid": "api_cell_product_fetchcellproductdetail", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L20"}, {"caller_nid": "api_cell_product_fetchcreatecellproduct", "callee": "post", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L24"}, {"caller_nid": "api_cell_product_fetchupdatecellproduct", "callee": "put", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L28"}, {"caller_nid": "api_cell_product_fetchdeletecellproduct", "callee": "del", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L32"}, {"caller_nid": "api_cell_product_fetchnextcellproductcode", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/cell-product.ts", "source_location": "L36"}]}
@@ -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": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L1"}, {"id": "sample_reception_index_parseetalocaldate", "label": "parseEtaLocalDate()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L683"}, {"id": "sample_reception_index_startoflocalday", "label": "startOfLocalDay()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L697"}, {"id": "sample_reception_index_isetatodayorearlier", "label": "isEtaTodayOrEarlier()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L701"}, {"id": "sample_reception_index_samplereceptionrowclassname", "label": "sampleReceptionRowClassName()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L707"}, {"id": "sample_reception_index_pad", "label": "pad()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L832"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_parseetalocaldate", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L683", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_startoflocalday", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L697", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_isetatodayorearlier", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L701", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_samplereceptionrowclassname", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L707", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_sample_reception_index_vue", "target": "sample_reception_index_pad", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L832", "weight": 1.0}, {"source": "sample_reception_index_isetatodayorearlier", "target": "sample_reception_index_parseetalocaldate", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L702", "weight": 1.0}, {"source": "sample_reception_index_isetatodayorearlier", "target": "sample_reception_index_startoflocalday", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L704", "weight": 1.0}, {"source": "sample_reception_index_samplereceptionrowclassname", "target": "sample_reception_index_isetatodayorearlier", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L709", "weight": 1.0}], "raw_calls": [{"caller_nid": "sample_reception_index_parseetalocaldate", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L684"}, {"caller_nid": "sample_reception_index_parseetalocaldate", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue", "source_location": "L684"}, {"caller_nid": "sample_reception_index_parseetalocaldate", "callee": "slice", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/srLine truncated
File diff suppressed because it is too large. Load diff
@@ -403,9 +403,28 @@
<el-input v-model="row.reagentCode" placeholder="编码" class="inner-cell-input" @change="saveReagents" />
</template>
</el-table-column>
<el-table-column label="试剂名称" min-width="120" align="center" header-align="center">
<el-table-column label="试剂名称" min-width="160" align="center" header-align="center">
<template #default="{ row }">
<el-input v-model="row.reagentName" placeholder="名称" class="inner-cell-input" @change="saveReagents" />
<el-select
v-model="row.reagentId"
placeholder="请选择试剂"
clearable
filterable
remote
reserve-keyword
class="inner-cell-select"
:loading="reagentOptionsLoading"
:remote-method="searchReagentOptions"
@visible-change="(visible) => visible && searchReagentOptions('')"
@change="(val) => onReagentRowSelect(row, val)"
>
<el-option
v-for="item in reagentOptions"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="规格" min-width="100" align="center" header-align="center">
@@ -418,9 +437,28 @@
<el-input v-model="row.standardUsage" placeholder="用量" class="inner-cell-input" @change="saveReagents" />
</template>
</el-table-column>
<el-table-column label="单位" min-width="80" align="center" header-align="center">
<el-table-column label="单位" min-width="120" align="center" header-align="center">
<template #default="{ row }">
<el-input v-model="row.unit" placeholder="单位" class="inner-cell-input" @change="saveReagents" />
<el-select
v-model="row.unit"
placeholder="请选择单位"
clearable
filterable
remote
reserve-keyword
class="inner-cell-select"
:loading="itemUnitOptionsLoading"
:remote-method="searchItemUnitOptions"
@visible-change="(visible) => visible && searchItemUnitOptions('')"
@change="saveReagents"
>
<el-option
v-for="item in itemUnitOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="关键物料" width="90" align="center" header-align="center">
@@ -464,9 +502,28 @@
<el-input v-model="row.consumableCode" placeholder="编码" class="inner-cell-input" @change="saveConsumables" />
</template>
</el-table-column>
<el-table-column label="耗材名称" min-width="120" align="center" header-align="center">
<el-table-column label="耗材名称" min-width="160" align="center" header-align="center">
<template #default="{ row }">
<el-input v-model="row.consumableName" placeholder="名称" class="inner-cell-input" @change="saveConsumables" />
<el-select
v-model="row.consumableId"
placeholder="请选择耗材"
clearable
filterable
remote
reserve-keyword
class="inner-cell-select"
:loading="consumableOptionsLoading"
:remote-method="searchConsumableOptions"
@visible-change="(visible) => visible && searchConsumableOptions('')"
@change="(val) => onConsumableRowSelect(row, val)"
>
<el-option
v-for="item in consumableOptions"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="规格型号" min-width="110" align="center" header-align="center">
@@ -479,9 +536,28 @@
<el-input v-model="row.standardUsage" placeholder="用量" class="inner-cell-input" @change="saveConsumables" />
</template>
</el-table-column>
<el-table-column label="单位" min-width="80" align="center" header-align="center">
<el-table-column label="单位" min-width="120" align="center" header-align="center">
<template #default="{ row }">
<el-input v-model="row.unit" placeholder="单位" class="inner-cell-input" @change="saveConsumables" />
<el-select
v-model="row.unit"
placeholder="请选择单位"
clearable
filterable
remote
reserve-keyword
class="inner-cell-select"
:loading="itemUnitOptionsLoading"
:remote-method="searchItemUnitOptions"
@visible-change="(visible) => visible && searchItemUnitOptions('')"
@change="saveConsumables"
>
<el-option
v-for="item in itemUnitOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="必需" width="80" align="center" header-align="center">
@@ -580,16 +656,33 @@
</el-table-column>
<el-table-column label="单位" min-width="120" align="center" header-align="center">
<template #default="{ row }">
<el-input v-model="row.unit" placeholder="如 °C、mL" class="inner-cell-input" @change="saveDataCollections" />
<el-select
v-model="row.unit"
placeholder="请选择单位"
clearable
filterable
remote
reserve-keyword
class="inner-cell-select"
:loading="itemUnitOptionsLoading"
:remote-method="searchItemUnitOptions"
@visible-change="(visible) => visible && searchItemUnitOptions('')"
@change="saveDataCollections"
>
<el-option
v-for="item in itemUnitOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="排序号" min-width="100" align="center" header-align="center">
<template #default="{ row }">
<el-input-number
<el-input
v-model="row.orderNum"
:min="0"
:max="9999"
controls-position="right"
placeholder="排序号"
class="inner-cell-input"
@change="saveDataCollections"
/>
@@ -643,12 +736,14 @@
fetchUpdateStepConsumables,
fetchUpdateStepOutputs,
fetchUpdateStepDataCollections,
fetchReagentList,
fetchConsumableList,
fetchAllProcesses,
fetchAllProcessCategories,
fetchAllSopDocuments,
fetchAllOutputTypes
} from '@/api/production'
import { fetchCellProductTypeList } from '@/api/admin'
import { fetchCellProductTypeList, fetchItemUnitList } from '@/api/admin'
defineOptions({ name: 'ProductionProcessRoute' })
@@ -870,6 +965,234 @@
const categoryOptions = ref<Record<string, any>[]>([])
const sopOptions = ref<Record<string, any>[]>([])
const outputTypeOptions = ref<Record<string, any>[]>([])
const itemUnitOptions = ref<{ label: string; value: string }[]>([])
const itemUnitOptionsLoading = ref(false)
const reagentOptions = ref<{ id: string; label: string }[]>([])
const reagentOptionsLoading = ref(false)
const reagentMasterById = ref<Record<string, Record<string, any>>>({})
const consumableOptions = ref<{ id: string; label: string }[]>([])
const consumableOptionsLoading = ref(false)
const consumableMasterById = ref<Record<string, Record<string, any>>>({})
function mapItemUnitOption(u: { unitName?: string; unitCode?: string }) {
const name = (u.unitName || '').trim()
const code = (u.unitCode || '').trim()
return {
label: code ? `${name}(${code})` : name,
value: name
}
}
function ensureItemUnitOption(unitName: string) {
const v = unitName?.trim()
if (!v) return
if (!itemUnitOptions.value.some((o) => o.value === v)) {
itemUnitOptions.value = [{ label: v, value: v }, ...itemUnitOptions.value]
}
}
async function searchItemUnitOptions(query: string) {
itemUnitOptionsLoading.value = true
try {
const res = await fetchItemUnitList({
page: 1,
pageSize: 100,
keyword: query?.trim() || undefined
})
itemUnitOptions.value = (res.list || []).map(mapItemUnitOption).filter((o) => o.value)
} catch {
itemUnitOptions.value = []
} finally {
itemUnitOptionsLoading.value = false
}
}
function mapReagentOptionLabel(r: { reagentName?: string; reagentCode?: string }) {
const name = (r.reagentName || '').trim()
const code = (r.reagentCode || '').trim()
return code ? `${name}(${code})` : name
}
function cacheReagentMasters(list: Record<string, any>[]) {
const next = { ...reagentMasterById.value }
for (const r of list) {
if (r?.id) next[r.id] = r
}
reagentMasterById.value = next
}
async function searchReagentOptions(query: string) {
reagentOptionsLoading.value = true
try {
const res = await fetchReagentList({
page: 1,
pageSize: 100,
keyword: query?.trim() || undefined
})
const list = res.list || []
cacheReagentMasters(list)
const options = list
.filter((r) => (r.reagentName || '').trim() || (r.reagentCode || '').trim())
.map((r) => ({ id: r.id, label: mapReagentOptionLabel(r) }))
reagentOptions.value = options
ensureAllStepReagentOptions()
} catch {
reagentOptions.value = []
ensureAllStepReagentOptions()
} finally {
reagentOptionsLoading.value = false
}
}
function ensureReagentOption(row: Record<string, any>) {
if (row.reagentId && !String(row.reagentId).startsWith('legacy:')) {
if (!reagentOptions.value.some((o) => o.id === row.reagentId)) {
reagentOptions.value = [
{ id: row.reagentId, label: mapReagentOptionLabel(row) },
...reagentOptions.value
]
}
return
}
const code = (row.reagentCode || '').trim()
const name = (row.reagentName || '').trim()
if (!code && !name) return
const legacyId = `legacy:${code || name}`
row.reagentId = legacyId
if (!reagentOptions.value.some((o) => o.id === legacyId)) {
reagentOptions.value = [{ id: legacyId, label: mapReagentOptionLabel(row) }, ...reagentOptions.value]
}
}
function ensureAllStepReagentOptions() {
for (const step of stepList.value) {
;(step.reagents || []).forEach((r: Record<string, any>) => ensureReagentOption(r))
}
}
function onReagentRowSelect(row: Record<string, any>, reagentId: string) {
if (!reagentId) {
row.reagentId = ''
saveReagents()
return
}
if (String(reagentId).startsWith('legacy:')) {
saveReagents()
return
}
const master = reagentMasterById.value[reagentId]
if (!master) {
saveReagents()
return
}
row.reagentId = master.id
row.reagentCode = master.reagentCode || ''
row.reagentName = master.reagentName || ''
row.spec = master.spec || ''
row.standardUsage = master.standardUsage ?? ''
row.unit = master.unit || ''
row.remark = master.remark ?? master.description ?? ''
ensureItemUnitOption(row.unit)
ensureReagentOption(row)
saveReagents()
}
function mapConsumableOptionLabel(c: { consumableName?: string; consumableCode?: string }) {
const name = (c.consumableName || '').trim()
const code = (c.consumableCode || '').trim()
return code ? `${name}(${code})` : name
}
function cacheConsumableMasters(list: Record<string, any>[]) {
const next = { ...consumableMasterById.value }
for (const c of list) {
if (c?.id) next[c.id] = c
}
consumableMasterById.value = next
}
async function searchConsumableOptions(query: string) {
consumableOptionsLoading.value = true
try {
const res = await fetchConsumableList({
page: 1,
pageSize: 100,
keyword: query?.trim() || undefined
})
const list = res.list || []
cacheConsumableMasters(list)
consumableOptions.value = list
.filter((c) => (c.consumableName || '').trim() || (c.consumableCode || '').trim())
.map((c) => ({ id: c.id, label: mapConsumableOptionLabel(c) }))
ensureAllStepConsumableOptions()
} catch {
consumableOptions.value = []
ensureAllStepConsumableOptions()
} finally {
consumableOptionsLoading.value = false
}
}
function ensureConsumableOption(row: Record<string, any>) {
if (row.consumableId && !String(row.consumableId).startsWith('legacy:')) {
if (!consumableOptions.value.some((o) => o.id === row.consumableId)) {
consumableOptions.value = [
{ id: row.consumableId, label: mapConsumableOptionLabel(row) },
...consumableOptions.value
]
}
return
}
const code = (row.consumableCode || '').trim()
const name = (row.consumableName || '').trim()
if (!code && !name) return
const legacyId = `legacy:${code || name}`
row.consumableId = legacyId
if (!consumableOptions.value.some((o) => o.id === legacyId)) {
consumableOptions.value = [{ id: legacyId, label: mapConsumableOptionLabel(row) }, ...consumableOptions.value]
}
}
function ensureAllStepConsumableOptions() {
for (const step of stepList.value) {
;(step.consumables || []).forEach((c: Record<string, any>) => ensureConsumableOption(c))
}
}
function onConsumableRowSelect(row: Record<string, any>, consumableId: string) {
if (!consumableId) {
row.consumableId = ''
saveConsumables()
return
}
if (String(consumableId).startsWith('legacy:')) {
saveConsumables()
return
}
const master = consumableMasterById.value[consumableId]
if (!master) {
saveConsumables()
return
}
row.consumableId = master.id
row.consumableCode = master.consumableCode || ''
row.consumableName = master.consumableName || ''
row.specModel = master.specModel || ''
row.standardUsage = master.standardUsage ?? ''
row.unit = master.unit || ''
row.remark = master.remark ?? master.description ?? ''
ensureItemUnitOption(row.unit)
ensureConsumableOption(row)
saveConsumables()
}
function ensureAllStepUnitOptions() {
for (const step of stepList.value) {
;(step.reagents || []).forEach((r: Record<string, any>) => ensureItemUnitOption(r.unit))
;(step.consumables || []).forEach((c: Record<string, any>) => ensureItemUnitOption(c.unit))
;(step.dataCollections || []).forEach((d: Record<string, any>) => ensureItemUnitOption(d.unit))
}
}
const selectedStep = computed(() => {
if (!selectedStepId.value) return null
@@ -956,6 +1279,7 @@
// 展开所有分类
expandedCategories.value = Object.keys(catMap)
processLibrary.value = Object.values(catMap)
await Promise.all([searchItemUnitOptions(''), searchReagentOptions(''), searchConsumableOptions('')])
} catch {
/* ignore */
}
@@ -984,6 +1308,9 @@
try {
const detail = await fetchProcessRouteDetail(configRouteId.value)
stepList.value = (detail.steps || []).map((s: Record<string, any>) => normalizeStepSubTables(s))
ensureAllStepReagentOptions()
ensureAllStepConsumableOptions()
ensureAllStepUnitOptions()
if (stepList.value.length > 0 && !selectedStepId.value) {
selectedStepId.value = stepList.value[0].id
}
@@ -1103,6 +1430,7 @@
if (!selectedStep.value) return
if (!selectedStep.value.reagents) selectedStep.value.reagents = []
selectedStep.value.reagents.push({
reagentId: '',
reagentCode: '',
reagentName: '',
spec: '',
@@ -1144,6 +1472,7 @@
if (!selectedStep.value) return
if (!selectedStep.value.consumables) selectedStep.value.consumables = []
selectedStep.value.consumables.push({
consumableId: '',
consumableCode: '',
consumableName: '',
specModel: '',
@@ -1216,6 +1545,12 @@
}
// --- 数据采集 ---
function parseOrderNum(value: unknown): number {
const n = Number.parseInt(String(value ?? '').trim(), 10)
if (!Number.isFinite(n) || n < 0) return 0
return Math.min(n, 9999)
}
function addDataCollectionRow() {
if (!selectedStep.value) return
if (!selectedStep.value.dataCollections) selectedStep.value.dataCollections = []
@@ -1241,7 +1576,7 @@
items: (selectedStep.value.dataCollections || []).map((d: Record<string, any>) => ({
collectionName: d.collectionName,
unit: d.unit,
orderNum: d.orderNum ?? 0,
orderNum: parseOrderNum(d.orderNum),
remark: d.remark || ''
}))
})
@@ -1573,6 +1908,23 @@
justify-content: flex-end;
}
/* 步骤详情内嵌表格:单元格控件随列宽收缩 */
.inner-table {
width: 100%;
:deep(.inner-cell-input) {
width: 100%;
}
:deep(.inner-cell-select) {
width: 100%;
.el-select__wrapper {
min-height: 36px;
}
}
}
/* 路线图配置弹窗内的对话框滚动区域 */
.route-config-dialog {
:deep(.el-dialog__body) {