This commit is contained in:
zlwu committed 2026-05-21 11:09:29 +08:00
1 parent ee73f69759
commit 74f751d0d1
70 files changed
+9340 -6930

No files matched your search

+15
View File
@@ -15,6 +15,20 @@ export function fetchQcTaskResults(taskId: string) {
})
}
/** 任务已保存的物料使用(用于填写结果弹窗回显) */
export type QcTaskMaterialUsageItem = {
materialId: string
materialName: string
changeQty: number
remark?: string
}
export function fetchQcTaskMaterialUsages(taskId: string) {
return request.get<QcTaskMaterialUsageItem[]>({
url: `/web/admin/qc-tasks/${taskId}/material-usages`
})
}
export function fetchCompleteTaskWithResult(
taskId: string,
data: {
@@ -22,6 +36,7 @@ export function fetchCompleteTaskWithResult(
results: QcTaskResultItem[]
remark?: string
thirdPartyAttachments?: string
materialUsages?: Array<{ materialId: string; qty: number; remark?: string }>
}
) {
return request.post({
@@ -28,6 +28,49 @@
</div>
</template>
<template #append>
<div
v-if="viewDetail?.taskType !== 'dsfjc'"
class="view-section qc-task-view-material-usages"
>
<h3>物料使用</h3>
<el-table
v-if="viewMaterialUsages.length"
:data="viewMaterialUsages"
stripe
class="list-table inner-table qc-task-view-material-table"
>
<el-table-column
type="index"
label="序号"
width="56"
align="center"
header-align="center"
:index="(index) => index + 1"
/>
<el-table-column
prop="materialName"
label="物料名称"
min-width="160"
align="center"
header-align="center"
/>
<el-table-column label="规格型号" min-width="120" align="center" header-align="center">
<template #default="{ row }">{{ row.specModel || '—' }}</template>
</el-table-column>
<el-table-column label="用量" min-width="100" align="center" header-align="center">
<template #default="{ row }">
{{ row.qty != null && row.qty !== '' ? row.qty : '—' }}
</template>
</el-table-column>
<el-table-column label="单位" width="72" align="center" header-align="center">
<template #default="{ row }">{{ row.unit || '—' }}</template>
</el-table-column>
<el-table-column label="备注" min-width="140" align="center" header-align="center">
<template #default="{ row }">{{ row.remark || '—' }}</template>
</el-table-column>
</el-table>
<p v-else class="view-material-empty">暂无物料使用记录</p>
</div>
<div
v-if="viewDetail?.attachmentFiles?.length"
class="view-section qc-task-view-attachments"
@@ -50,7 +93,12 @@
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
import type { ViewSection } from '@/components/business/system-view-dialog/index.vue'
import { fetchQcTaskDetail, type QcTaskDetail } from '@/api/qc-task'
import { fetchQcTaskResults } from '@/api/qc-task-result'
import {
fetchQcTaskResults,
fetchQcTaskMaterialUsages,
type QcTaskMaterialUsageItem
} from '@/api/qc-task-result'
import { fetchQcMaterialOptions, type QcMaterialOption } from '@/api/qc-material'
import {
taskTypeLabel,
taskStatusLabel,
@@ -77,15 +125,60 @@
set: (val) => emit('update:visible', val)
})
type ViewMaterialUsageRow = {
materialId: string
materialName: string
specModel: string
unit: string
qty: number | undefined
remark: string
}
const loading = ref(false)
const viewDetail = ref<QcTaskDetail | null>(null)
const viewMaterialUsages = ref<ViewMaterialUsageRow[]>([])
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
function materialRowsFromApi(items: QcTaskMaterialUsageItem[]): ViewMaterialUsageRow[] {
const merged = new Map<string, ViewMaterialUsageRow>()
for (const u of items || []) {
const mid = (u.materialId || '').trim()
if (!mid) continue
const qtyRaw = Math.abs(Number(u.changeQty) || 0)
const prev = merged.get(mid)
if (prev) {
prev.qty = (Number(prev.qty) || 0) + qtyRaw
} else {
merged.set(mid, {
materialId: mid,
materialName: u.materialName || '',
specModel: '',
unit: '',
qty: qtyRaw > 0 ? qtyRaw : undefined,
remark: u.remark || ''
})
}
}
return Array.from(merged.values())
}
function enrichMaterialUsageRows(rows: ViewMaterialUsageRow[], options: QcMaterialOption[]) {
for (const row of rows) {
const opt = options.find((o) => o.id === row.materialId)
if (opt) {
row.materialName = opt.materialName || row.materialName
row.specModel = opt.specModel || ''
row.unit = opt.unit || ''
}
}
}
watch(
() => [props.visible, props.taskId] as const,
async ([vis, id]) => {
if (!vis || !id) {
viewDetail.value = null
viewMaterialUsages.value = []
return
}
loading.value = true
@@ -97,9 +190,24 @@
} catch {
;(detail as QcTaskDetail).results = []
}
viewMaterialUsages.value = []
if (detail.taskType !== 'dsfjc') {
try {
const [savedUsages, opts] = await Promise.all([
fetchQcTaskMaterialUsages(id).catch(() => [] as QcTaskMaterialUsageItem[]),
fetchQcMaterialOptions().catch(() => [] as QcMaterialOption[])
])
const rows = materialRowsFromApi(savedUsages || [])
enrichMaterialUsageRows(rows, opts || [])
viewMaterialUsages.value = rows
} catch {
viewMaterialUsages.value = []
}
}
viewDetail.value = detail
} catch {
viewDetail.value = null
viewMaterialUsages.value = []
} finally {
loading.value = false
}
@@ -286,6 +394,7 @@
}
}
.qc-task-view-material-usages,
.qc-task-view-attachments {
padding: 12px 18px;
border-bottom: none;
@@ -300,4 +409,25 @@
line-height: 1;
}
}
.qc-task-view-material-table {
width: 100%;
:deep(.el-table__header .cell),
:deep(.el-table__body .cell) {
font-size: 12px;
line-height: 1.4;
}
:deep(.el-table__header th.el-table__cell),
:deep(.el-table__body td.el-table__cell) {
padding: 6px 0;
}
}
.view-material-empty {
margin: 0;
font-size: 12px;
color: #64748b;
}
</style>
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "label": "ProtocolFormDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1"}, {"id": "protocol_protocolformdialog_createemptytransport", "label": "createEmptyTransport()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1283"}, {"id": "protocol_protocolformdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1303"}, {"id": "protocol_protocolformdialog_loadethnicoptions", "label": "loadEthnicOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1310"}, {"id": "protocol_protocolformdialog_ensureethnicoptioninlist", "label": "ensureEthnicOptionInList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1321"}, {"id": "protocol_protocolformdialog_expressname", "label": "expressName()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1349"}, {"id": "protocol_protocolformdialog_for", "label": "for()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L2017"}, {"id": "protocol_protocolformdialog_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L2553"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1178", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_collection_protocol", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1190", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_business", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1196", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_download_attachment", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1197", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1198", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_ethnic", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1211", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolFormDialog.vue", "source_location": "L1212", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_protocol_payment_insLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L1"}, {"id": "qc_task_index_canfillresult", "label": "canFillResult()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L339"}, {"id": "qc_task_index_onmaterialselect", "label": "onMaterialSelect()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L361"}, {"id": "qc_task_index_handlereceptionattachmentdownload", "label": "handleReceptionAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L422"}, {"id": "qc_task_index_normalizeresultparams", "label": "normalizeResultParams()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L426"}, {"id": "qc_task_index_toresultnumber", "label": "toResultNumber()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L434"}, {"id": "qc_task_index_setresultnumber", "label": "setResultNumber()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L440"}, {"id": "qc_task_index_hasresultvalue", "label": "hasResultValue()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L444"}, {"id": "qc_task_index_formatmetric", "label": "formatMetric()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L448"}, {"id": "qc_task_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L454"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L289", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L290", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_protocol_finance_confirm_fields_index_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L291", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolattachmentreadonly_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L292", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_system_view_dialog_index_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L293", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_system_view_dialog_index_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L294", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_protocol_finance_confirm_fields_finance_attachments", "relation": "imports_from", "confidence": "EXTRACTED", "Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "label": "ProtocolViewDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1"}, {"id": "protocol_protocolviewdialog_dash", "label": "dash()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L310"}, {"id": "protocol_protocolviewdialog_handleclosed", "label": "handleClosed()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1627"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "target": "protocol_protocolviewdialog_dash", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L310", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolviewdialog_vue", "target": "protocol_protocolviewdialog_handleclosed", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L1627", "weight": 1.0}], "raw_calls": [{"caller_nid": "protocol_protocolviewdialog_dash", "callee": "unitName", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue", "source_location": "L310"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin_ts", "label": "admin.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L1"}, {"id": "api_admin_fetchgetmenulist", "label": "fetchGetMenuList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L10"}, {"id": "api_admin_fetchgetrolelist", "label": "fetchGetRoleList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L19"}, {"id": "api_admin_fetchuseroptions", "label": "fetchUserOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L26"}, {"id": "api_admin_fetchroleoptions", "label": "fetchRoleOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L33"}, {"id": "api_admin_fetchtenantlist", "label": "fetchTenantList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L43"}, {"id": "api_admin_fetchcreatetenant", "label": "fetchCreateTenant()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L56"}, {"id": "api_admin_fetchupdatetenant", "label": "fetchUpdateTenant()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L71"}, {"id": "api_admin_fetchdeletetenant", "label": "fetchDeleteTenant()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L89"}, {"id": "api_admin_fetchuserlist", "label": "fetchUserList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L100"}, {"id": "api_admin_fetchuserdetail", "label": "fetchUserDetail()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L119"}, {"id": "api_admin_fetchcreateuser", "label": "fetchCreateUser()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L125"}, {"id": "api_admin_fetchupdateuser", "label": "fetchUpdateUser()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L147"}, {"id": "api_admin_fetchdeleteuser", "label": "fetchDeleteUser()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L170"}, {"id": "api_admin_fetchresetpassword", "label": "fetchResetPassword()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L177"}, {"id": "api_admin_fetchuploaduseravatar", "label": "fetchUploadUserAvatar()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L185"}, {"id": "api_admin_fetchregionlist", "label": "fetchRegionList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L196"}, {"id": "api_admin_fetchactiveregions", "label": "fetchActiveRegions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L214"}, {"id": "api_admin_fetchnotdeletedregions", "label": "fetchNotDeletedRegions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L231"}, {"id": "api_admin_fetchcategorylist", "label": "fetchCategoryList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L259"}, {"id": "api_admin_fetchcreatecategory", "label": "fetchCreateCategory()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L277"}, {"id": "api_admin_fetchupdatecategory", "label": "fetchUpdateCategory()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L290"}, {"id": "api_admin_fetchdeletecategory", "label": "fetchDeleteCategory()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L307"}, {"id": "api_admin_fetchuploadcategoryimage", "label": "fetchUploadCategoryImage()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L313"}, {"id": "api_admin_fetchunitlist", "label": "fetchUnitList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/admin.ts", "source_location": "L324"}, {"id": "api_admin_fetchcreateunit", "label": "fetchCrLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_lab_test_item_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/lab-test-item/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_third_party_company_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/third-party-company/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_payment_info_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/payment-info/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_research_donation_list_donationformdialog_vue", "label": "DonationFormDialog.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1"}, {"id": "list_donationformdialog_createemptytransport", "label": "createEmptyTransport()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1015"}, {"id": "list_donationformdialog_fullfileurl", "label": "fullFileUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1035"}, {"id": "list_donationformdialog_loadethnicoptions", "label": "loadEthnicOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1042"}, {"id": "list_donationformdialog_ensureethnicoptioninlist", "label": "ensureEthnicOptionInList()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1053"}, {"id": "list_donationformdialog_samplerowlabel", "label": "sampleRowLabel()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1081"}, {"id": "list_donationformdialog_resetform", "label": "resetForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1165"}, {"id": "list_donationformdialog_scrolltosection", "label": "scrollToSection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1220"}, {"id": "list_donationformdialog_normalizesamplerow", "label": "normalizeSampleRow()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1228"}, {"id": "list_donationformdialog_openbatchtransportdialog", "label": "openBatchTransportDialog()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1251"}, {"id": "list_donationformdialog_confirmbatchtransportapply", "label": "confirmBatchTransportApply()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1259"}, {"id": "list_donationformdialog_addsample", "label": "addSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1273"}, {"id": "list_donationformdialog_removesample", "label": "removeSample()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1287"}, {"id": "list_donationformdialog_handleopenforadd", "label": "handleOpenForAdd()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1291"}, {"id": "list_donationformdialog_applydonationbundletoform", "label": "applyDonationBundleToForm()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1339"}, {"id": "list_donationformdialog_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L1974"}, {"id": "list_donationformdialog_ensuredonationprotocolnoinoptions", "label": "ensureDonationProtocolNoInOptions()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L2047"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_research_donation_list_donationformdialog_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/research-donation/list/DonationFormDialog.vue", "source_location": "L932", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_research_donation_list_donationformdialog_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_research_donation", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/wLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_checkup_institution_checkuppackagepanel_vue", "label": "CheckupPackagePanel.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/checkup-institution/CheckupPackagePanel.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -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": "L250"}, {"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": "L260"}, {"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": "L267"}, {"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": "L273"}, {"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": "L280"}, {"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": "L294"}, {"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": "L299"}, {"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": "L318"}, {"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": "L331"}, {"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": "L355"}, {"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": "L366"}, {"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": "L372"}, {"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": "L389"}, {"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": "L394"}], "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
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_audit_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -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_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
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_qc_task_view_dialog_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L1"}, {"id": "qc_task_view_dialog_index_attachmentfullurl", "label": "attachmentFullUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L349"}, {"id": "qc_task_view_dialog_index_handleattachmentpreview", "label": "handleAttachmentPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L356"}, {"id": "qc_task_view_dialog_index_handleattachmentdownload", "label": "handleAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L361"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_qc_task_view_dialog_index_vue", "target": "qc_task_view_dialog_index_attachmentfullurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L349", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_qc_task_view_dialog_index_vue", "target": "qc_task_view_dialog_index_handleattachmentpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L356", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_qc_task_view_dialog_index_vue", "target": "qc_task_view_dialog_index_handleattachmentdownload", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L361", "weight": 1.0}, {"source": "qc_task_view_dialog_index_handleattachmentpreview", "target": "qc_task_view_dialog_index_attachmentfullurl", "relation": "calls", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L357", "weight": 1.0}], "raw_calls": [{"caller_nid": "qc_task_view_dialog_index_attachmentfullurl", "callee": "trim", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L350"}, {"caller_nid": "qc_task_view_dialog_index_attachmentfullurl", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L350"}, {"caller_nid": "qc_task_view_dialog_index_attachmentfullurl", "callee": "test", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L352"}, {"caller_nid": "qc_task_view_dialog_index_attachmentfullurl", "callee": "replace", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L353"}, {"caller_nid": "qc_task_view_dialog_index_handleattachmentpreview", "callee": "String", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L357"}, {"caller_nid": "qc_task_view_dialog_index_handleattachmentpreview", "callee": "open", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L358"}, {"caller_nid": "qc_task_view_dialog_index_handleattachmentdownload", "callee": "downloadAttachmentFile", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/components/business/qc-task-view-dialog/index.vue", "source_location": "L362"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L1"}, {"id": "qc_task_index_canfillresult", "label": "canFillResult()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L391"}, {"id": "qc_task_index_onmaterialselect", "label": "onMaterialSelect()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L413"}, {"id": "qc_task_index_addmaterialrow", "label": "addMaterialRow()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L423"}, {"id": "qc_task_index_handlereceptionattachmentdownload", "label": "handleReceptionAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L490"}, {"id": "qc_task_index_normalizeresultparams", "label": "normalizeResultParams()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L494"}, {"id": "qc_task_index_toresultnumber", "label": "toResultNumber()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L502"}, {"id": "qc_task_index_setresultnumber", "label": "setResultNumber()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L508"}, {"id": "qc_task_index_hasresultvalue", "label": "hasResultValue()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L512"}, {"id": "qc_task_index_formatmetric", "label": "formatMetric()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L516"}, {"id": "qc_task_index_if", "label": "if()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L522"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "element_plus", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L328", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "icons_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L329", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_protocol_finance_confirm_fields_index_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L330", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_protocolattachmentreadonly_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L331", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_system_view_dialog_index_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L332", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_components_business_system_view_dialog_index_vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L333", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_qLine truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task_result_ts", "label": "qc-task-result.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L1"}, {"id": "api_qc_task_result_fetchqctaskresults", "label": "fetchQcTaskResults()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L12"}, {"id": "api_qc_task_result_fetchqctaskmaterialusages", "label": "fetchQcTaskMaterialUsages()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L26"}, {"id": "api_qc_task_result_fetchcompletetaskwithresult", "label": "fetchCompleteTaskWithResult()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L32"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task_result_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task_result_ts", "target": "api_qc_task_result_fetchqctaskresults", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task_result_ts", "target": "api_qc_task_result_fetchqctaskmaterialusages", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task_result_ts", "target": "api_qc_task_result_fetchcompletetaskwithresult", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L32", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_qc_task_result_fetchqctaskresults", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L13"}, {"caller_nid": "api_qc_task_result_fetchqctaskmaterialusages", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L27"}, {"caller_nid": "api_qc_task_result_fetchcompletetaskwithresult", "callee": "post", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L42"}]}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L1"}, {"id": "qc_task_index_canfillresult", "label": "canFillResult()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L451"}, {"id": "qc_task_index_clearmaterialrow", "label": "clearMaterialRow()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L473"}, {"id": "qc_task_index_ismaterialoptiondisabled", "label": "isMaterialOptionDisabled()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L480"}, {"id": "qc_task_index_isduplicatematerial", "label": "isDuplicateMaterial()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L485"}, {"id": "qc_task_index_handlereceptionattachmentdownload", "label": "handleReceptionAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L632"}, {"id": "qc_task_index_normalizeresultparams", "label": "normalizeResultParams()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L636"}, {"id": "qc_task_index_toresultnumber", "label": "toResultNumber()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L644"}, {"id": "qc_task_index_setresultnumber", "label": "setResultNumber()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L650"}, {"id": "qc_task_index_hasresultvalue", "label": "hasResultValue()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L654"}, {"id": "qc_task_index_formatmetric", "label": "formatMetric()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L658"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "qc_task_index_canfillresult", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L451", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "qc_task_index_clearmaterialrow", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L473", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "qc_task_index_ismaterialoptiondisabled", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L480", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "qc_task_index_isduplicatematerial", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L485", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "qc_task_index_handlereceptionattachmentdownload", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L632", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "qc_task_index_normalizeresultparams", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue", "source_location": "L636", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_task_index_vue", "target": "qc_task_index_toresultnumber", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/Line truncated
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "label": "user.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L1"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "pinia", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L15", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "vue", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_enums_appenum", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L17", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_router", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L18", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_worktab", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L19", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_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/store/modules/user.ts", "source_location": "L20", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_router", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L21", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_router_guards_beforeeach", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L22", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_menu", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L23", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_storage_storage_config", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_store_modules_user_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_auth", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/store/modules/user.ts", "source_location": "L25", "weight": 1.0}], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_protocol_audit_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/protocol-audit/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_cell_storage_storage_cert_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/cell-storage/storage-cert/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_pilot_service_checkup_institution_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/pilot-service/checkup-institution/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task_result_ts", "label": "qc-task-result.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L1"}, {"id": "api_qc_task_result_fetchqctaskresults", "label": "fetchQcTaskResults()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L12"}, {"id": "api_qc_task_result_fetchcompletetaskwithresult", "label": "fetchCompleteTaskWithResult()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L18"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task_result_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_http", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task_result_ts", "target": "api_qc_task_result_fetchqctaskresults", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L12", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_api_qc_task_result_ts", "target": "api_qc_task_result_fetchcompletetaskwithresult", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L18", "weight": 1.0}], "raw_calls": [{"caller_nid": "api_qc_task_result_fetchqctaskresults", "callee": "get", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L13"}, {"caller_nid": "api_qc_task_result_fetchcompletetaskwithresult", "callee": "post", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/api/qc-task-result.ts", "source_location": "L28"}]}
+8 -2
View File
@@ -22,6 +22,7 @@ import { setPageTitle } from '@/utils/router'
import { resetRouterState } from '@/router/guards/beforeEach'
import { useMenuStore } from './menu'
import { StorageConfig } from '@/utils/storage/storage-config'
import { fetchLogout } from '@/api/auth'
/**
* 用户状态管理
@@ -101,9 +102,14 @@ export const useUserStore = defineStore(
/**
* 退出登录
* 清空所有用户相关状态并跳转到登录页
* 通知后端撤销 Redis 会话,然后清空所有用户相关状态并跳转到登录页
*/
const logOut = () => {
const logOut = async () => {
// 先通知后端撤销 Redis 会话(不等待结果,失败也不阻塞退出)
if (accessToken.value) {
fetchLogout().catch(() => {})
}
// 保存当前用户 ID,用于下次登录时判断是否为同一用户
const currentUserId = info.value.userId
if (currentUserId) {
+93 -16
View File
@@ -5,7 +5,7 @@
* ## 主要功能
*
* - 请求/响应拦截器(自动添加 Token、统一错误处理)
* - 401 未授权自动登出(带防抖机制)
* - 401 未授权自动刷新 Token(并发安全,刷新失败后登出)
* - 请求失败自动重试(可配置)
* - 统一的成功/错误消息提示
* - 支持 GET/POST/PUT/DELETE 等常用方法
@@ -21,6 +21,7 @@ import { HttpError, handleError, showError, showSuccess } from './error'
import { $t } from '@/locales'
import { BaseResponse } from '@/types'
import { decryptJson, deriveKey, encryptJson, randomNonce } from './crypto'
import { fetchRefreshToken } from '@/api/auth'
/** 请求配置常量 */
const REQUEST_TIMEOUT = 60000
@@ -34,10 +35,32 @@ const UNAUTHORIZED_DEBOUNCE_TIME = 3000
let isUnauthorizedErrorShown = false
let unauthorizedTimer: NodeJS.Timeout | null = null
/** Token 刷新状态(并发安全) */
let isRefreshing = false
let pendingRequests: Array<{
resolve: (token: string) => void
reject: (error: unknown) => void
}> = []
function subscribeTokenRefresh(resolve: (token: string) => void, reject: (error: unknown) => void) {
pendingRequests.push({ resolve, reject })
}
function onRefreshed(newToken: string) {
pendingRequests.forEach(({ resolve }) => resolve(newToken))
pendingRequests = []
}
function onRefreshFailed(error: unknown) {
pendingRequests.forEach(({ reject }) => reject(error))
pendingRequests = []
}
/** 扩展 AxiosRequestConfig */
interface ExtendedAxiosRequestConfig extends AxiosRequestConfig {
showErrorMessage?: boolean
showSuccessMessage?: boolean
_retry?: boolean
}
const { VITE_API_URL, VITE_WITH_CREDENTIALS } = import.meta.env
@@ -148,11 +171,15 @@ axiosInstance.interceptors.response.use(
const code = resData?.code
const msg = resData?.msg || resData?.message
if (code === ApiStatus.success) return response
if (code === ApiStatus.unauthorized) handleUnauthorizedError(msg)
if (code === ApiStatus.unauthorized) {
return handleUnauthorizedResponse(response.config, msg)
}
throw createHttpError(msg || $t('httpMsg.requestFailed'), code)
},
(error) => {
if (error.response?.status === ApiStatus.unauthorized) handleUnauthorizedError()
async (error) => {
if (error.response?.status === ApiStatus.unauthorized) {
return handleUnauthorizedResponse(error.response.config, undefined)
}
return Promise.reject(handleError(error))
}
)
@@ -162,21 +189,71 @@ function createHttpError(message: string, code: number) {
return new HttpError(message, code)
}
/** 处理401错误(带防抖) */
function handleUnauthorizedError(message?: string): never {
const error = createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
/**
* 处理 401 响应:尝试刷新 token,成功则重试原请求,失败则登出
*/
async function handleUnauthorizedResponse(
config: InternalAxiosRequestConfig,
message?: string
): Promise<AxiosResponse> {
const userStore = useUserStore()
const storedRefreshToken = userStore.refreshToken
if (!isUnauthorizedErrorShown) {
isUnauthorizedErrorShown = true
logOut()
unauthorizedTimer = setTimeout(resetUnauthorizedError, UNAUTHORIZED_DEBOUNCE_TIME)
showError(error, true)
throw error
if (!storedRefreshToken) {
triggerLogoutWithDebounce(message)
throw createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
}
throw error
if ((config as ExtendedAxiosRequestConfig)._retry) {
triggerLogoutWithDebounce(message)
throw createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
}
if (isRefreshing) {
return new Promise<AxiosResponse>((resolve, reject) => {
subscribeTokenRefresh(
(newToken: string) => {
config.headers.set('Authorization', `Bearer ${newToken}`)
;(config as ExtendedAxiosRequestConfig)._retry = true
resolve(axiosInstance.request(config))
},
(error: unknown) => {
reject(createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized))
}
)
})
}
isRefreshing = true
;(config as ExtendedAxiosRequestConfig)._retry = true
try {
const result = await fetchRefreshToken(storedRefreshToken)
const newAccessToken = result.token
const newRefreshToken = result.refreshToken
userStore.setToken(newAccessToken, newRefreshToken)
onRefreshed(newAccessToken)
config.headers.set('Authorization', `Bearer ${newAccessToken}`)
return await axiosInstance.request(config)
} catch (refreshError) {
onRefreshFailed(refreshError)
triggerLogoutWithDebounce(message)
throw createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
} finally {
isRefreshing = false
}
}
/** 带 3 秒防抖的登出提示 */
function triggerLogoutWithDebounce(message?: string) {
if (isUnauthorizedErrorShown) return
isUnauthorizedErrorShown = true
const error = createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
logOut()
unauthorizedTimer = setTimeout(resetUnauthorizedError, UNAUTHORIZED_DEBOUNCE_TIME)
showError(error, true)
}
/** 重置401防抖状态 */
@@ -404,6 +404,7 @@
:list-row="viewRow"
show-audit-history
show-storage-cert
show-fullmoon-insurance
/>
</section>
</div>
@@ -16,7 +16,7 @@
<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-input v-model="searchForm.keyword" placeholder="协议号/姓名/身份证号/电话" clearable />
</el-form-item>
<el-form-item label="协议状态">
<el-select
@@ -290,6 +290,7 @@
:list-row="viewRow"
show-audit-history
show-storage-cert
show-fullmoon-insurance
/>
</section>
</div>
@@ -3,7 +3,7 @@
<el-dialog
:model-value="modelValue"
:title="dialogTitle"
width="1200px"
width="1380px"
class="dialog-form-layout"
align-center
destroy-on-close
@@ -366,6 +366,10 @@
}}
</div>
</div>
<div class="data-item">
<div class="data-label">体积(ml)</div>
<div class="data-value">{{ formatMetric(row.sampleReception.volumeMl) }}</div>
</div>
<div class="data-item">
<div class="data-label">净重(g)</div>
<div class="data-value">{{ formatMetric(row.sampleReception.netWeightG) }}</div>
@@ -16,7 +16,7 @@
<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-input v-model="searchForm.keyword" placeholder="协议号/姓名/身份证号/电话" clearable />
</el-form-item>
<el-form-item label="协议状态">
<el-select
@@ -255,6 +255,8 @@
:protocol-id="viewProtocolId"
:list-row="viewRow"
show-audit-history
show-storage-cert
show-fullmoon-insurance
/>
</section>
</div>
@@ -245,13 +245,14 @@
</div>
</el-dialog>
<!-- 查看弹窗(与采集协议单录入一致) -->
<!-- 查看弹窗(与满月待办管理等一致:审核+储存证+保险) -->
<ProtocolViewDialog
v-model="viewVisible"
:protocol-id="viewProtocolId"
:list-row="viewRow"
show-audit-history
show-storage-cert
show-fullmoon-insurance
/>
</section>
</div>
@@ -327,7 +328,7 @@
const previewType = ref<'image' | 'video'>('image')
// ---------------------------------------------------------------------------
// 查看弹窗(与采集协议单录入一致)
// 查看弹窗(与满月待办管理等一致)
// ---------------------------------------------------------------------------
const viewVisible = ref(false)
const viewProtocolId = ref<string | null>(null)
@@ -82,7 +82,7 @@
</div>
</div>
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="640px" class="dialog-form-layout" align-center destroy-on-close :close-on-click-modal="false">
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="840px" class="dialog-form-layout" align-center destroy-on-close :close-on-click-modal="false">
<el-form ref="formRef" :model="formData" :rules="formRules" label-position="top" class="biz-dialog-form">
<div class="form-grid-2">
<el-form-item label="项目编码" prop="itemCode">
@@ -145,8 +145,8 @@
<el-dialog
v-model="resultVisible"
title="填写检测结果"
width="720px"
class="dialog-form-layout"
:width="resultDialogWidth"
class="dialog-form-layout qc-task-result-dialog"
align-center
destroy-on-close
:close-on-click-modal="false"
@@ -160,7 +160,7 @@
>
该项目暂无配置参数,请先在实验室/第三方项目中维护检测参数
</div>
<div class="form-grid-2">
<div class="form-grid-3">
<el-form-item
v-for="(param, idx) in resultParams"
:key="param.paramId || param.paramCode || idx"
@@ -178,42 +178,85 @@
</el-form-item>
</div>
</div>
<div class="form-section material-usage-section">
<div class="section-title">物料使用(选填)</div>
<div v-for="(mu, idx) in materialUsages" :key="idx" class="material-usage-row">
<el-form-item label="物料" class="mu-field-material">
<el-select
v-model="mu.materialId"
filterable
placeholder="选择物料"
style="width: 100%"
@change="(v: string) => onMaterialSelect(mu, v)"
>
<el-option
v-for="opt in materialOptions"
:key="opt.id"
:label="opt.label"
:value="opt.id"
/>
</el-select>
</el-form-item>
<el-form-item label="使用数量" class="mu-field-qty">
<el-input-number
v-model="mu.qty"
:min="0.0001"
:precision="4"
:step="1"
style="width: 100%"
placeholder="数量"
/>
</el-form-item>
<el-button class="btn-ghost mu-remove-btn" @click="materialUsages.splice(idx, 1)">
<el-icon><Delete /></el-icon>
<div v-if="resultTaskType !== 'dsfjc'" class="form-section material-usage-section">
<div class="section-title material-usage-head">
<div class="section-title-left"><h3>物料使用(选填)</h3></div>
<el-button class="btn-main" type="primary" size="small" @click="addMaterialRow">
<el-icon><Plus /></el-icon>添加物料
</el-button>
</div>
<el-button class="btn-ghost" size="small" @click="materialUsages.push({ materialId: '', materialName: '', qty: undefined })">
<el-icon><Plus /></el-icon>添加物料
</el-button>
<el-table
v-if="materialUsages.length"
:data="materialUsages"
:row-key="(row) => row._rowKey"
stripe
class="list-table inner-table material-usage-table"
max-height="280"
>
<el-table-column
type="index"
label="序号"
width="56"
align="center"
header-align="center"
:index="(index) => index + 1"
/>
<el-table-column label="物料名称" min-width="160" align="center" header-align="center">
<template #default="{ row, $index }">
<el-select
v-model="row.materialId"
filterable
placeholder="搜索并选择物料"
class="inner-cell-input"
@change="(v) => onMaterialSelect($index, v)"
>
<el-option
v-for="opt in materialOptions"
:key="opt.id"
:label="opt.label"
:value="opt.id"
:disabled="isMaterialOptionDisabled(opt.id, $index)"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="规格型号" min-width="120" align="center" header-align="center">
<template #default="{ row }">{{ row.specModel || '—' }}</template>
</el-table-column>
<el-table-column label="用量" min-width="120" align="center" header-align="center">
<template #default="{ row }">
<el-input-number
v-model="row.qty"
:min="0.0001"
:precision="4"
:step="1"
controls-position="right"
placeholder="用量"
class="inner-cell-input"
/>
</template>
</el-table-column>
<el-table-column label="单位" width="72" align="center" header-align="center">
<template #default="{ row }">{{ row.unit || '—' }}</template>
</el-table-column>
<el-table-column label="备注" min-width="140" align="center" header-align="center">
<template #default="{ row }">
<el-input v-model="row.remark" placeholder="选填" clearable class="inner-cell-input" />
</template>
</el-table-column>
<el-table-column label="操作" width="96" align="center" header-align="center" fixed="right">
<template #default="{ $index }">
<el-button link type="danger" size="small" @click="removeMaterialRow($index)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-empty
v-else
description="暂无物料记录,可点击上方按钮添加"
:image-size="56"
/>
</div>
<div class="form-section result-conclusion-section">
<div class="section-title">检测结论</div>
@@ -267,6 +310,49 @@
</div>
</template>
<template #append>
<div
v-if="viewDetail?.taskType !== 'dsfjc'"
class="view-section qc-task-view-material-usages"
>
<h3>物料使用</h3>
<el-table
v-if="viewMaterialUsages.length"
:data="viewMaterialUsages"
stripe
class="list-table inner-table qc-task-view-material-table"
>
<el-table-column
type="index"
label="序号"
width="56"
align="center"
header-align="center"
:index="(index) => index + 1"
/>
<el-table-column
prop="materialName"
label="物料名称"
min-width="160"
align="center"
header-align="center"
/>
<el-table-column label="规格型号" min-width="120" align="center" header-align="center">
<template #default="{ row }">{{ row.specModel || '—' }}</template>
</el-table-column>
<el-table-column label="用量" min-width="100" align="center" header-align="center">
<template #default="{ row }">
{{ row.qty != null && row.qty !== '' ? row.qty : '—' }}
</template>
</el-table-column>
<el-table-column label="单位" width="72" align="center" header-align="center">
<template #default="{ row }">{{ row.unit || '—' }}</template>
</el-table-column>
<el-table-column label="备注" min-width="140" align="center" header-align="center">
<template #default="{ row }">{{ row.remark || '—' }}</template>
</el-table-column>
</el-table>
<p v-else class="view-material-empty">暂无物料使用记录</p>
</div>
<div
v-if="viewDetail?.attachmentFiles?.length"
class="view-section qc-task-view-attachments"
@@ -287,7 +373,7 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Close, Check, Search, RefreshLeft, VideoPlay, View, Tickets, EditPen } from '@element-plus/icons-vue'
import { Close, Check, Search, RefreshLeft, VideoPlay, View, Tickets, EditPen, Plus } from '@element-plus/icons-vue'
import ProtocolFinanceConfirmFields from '@/components/business/protocol-finance-confirm-fields/index.vue'
import ProtocolAttachmentReadonly from '@/views/cell-storage/protocol/ProtocolAttachmentReadonly.vue'
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
@@ -303,7 +389,13 @@
type QcTask,
type QcTaskDetail
} from '@/api/qc-task'
import { fetchQcTaskResults, fetchCompleteTaskWithResult, type QcTaskResultItem } from '@/api/qc-task-result'
import {
fetchQcTaskResults,
fetchQcTaskMaterialUsages,
fetchCompleteTaskWithResult,
type QcTaskResultItem,
type QcTaskMaterialUsageItem
} from '@/api/qc-task-result'
import { fetchQcMaterialOptions, type QcMaterialOption } from '@/api/qc-material'
import {
taskTypeLabel,
@@ -333,8 +425,28 @@
const resultRemark = ref('')
const resultConclusion = ref('')
const resultAttachments = ref<FinanceAttachmentItem[]>([])
type MaterialUsageRow = {
_rowKey: string
materialId: string
materialName: string
specModel: string
unit: string
qty: number | undefined
remark: string
}
let materialUsageRowSeq = 0
function nextMaterialRowKey() {
materialUsageRowSeq += 1
return `mu_${Date.now()}_${materialUsageRowSeq}`
}
const materialOptions = ref<QcMaterialOption[]>([])
const materialUsages = ref<Array<{ materialId: string; materialName: string; qty: number | undefined }>>([])
const materialUsages = ref<MaterialUsageRow[]>([])
const resultDialogWidth = computed(() =>
resultTaskType.value === 'sysz' ? '970px' : '720px'
)
function canFillResult(row: QcTask) {
if (row.taskType === 'sysz') {
@@ -358,13 +470,111 @@
resultTaskType.value === 'dsfjc' ? 'qc-third-party' : 'qc-lab'
)
function onMaterialSelect(mu: { materialId: string; materialName: string; qty: number | undefined }, id: string) {
function clearMaterialRow(row: MaterialUsageRow) {
row.materialId = ''
row.materialName = ''
row.specModel = ''
row.unit = ''
}
function isMaterialOptionDisabled(materialId: string, currentIndex: number) {
if (!materialId) return false
return materialUsages.value.some((r, i) => i !== currentIndex && r.materialId === materialId)
}
function isDuplicateMaterial(materialId: string, excludeIndex: number) {
if (!materialId) return false
return materialUsages.value.some((r, i) => i !== excludeIndex && r.materialId === materialId)
}
function parseMaterialQty(qty: number | string | undefined): number | null {
if (qty === undefined || qty === null || qty === '') return null
const n = typeof qty === 'number' ? qty : Number(String(qty).trim())
if (!Number.isFinite(n) || n <= 0) return null
return n
}
function onMaterialSelect(index: number, id: string) {
const row = materialUsages.value[index]
if (!row) return
if (!id) {
if (parseMaterialQty(row.qty) == null) {
removeMaterialRow(index)
} else {
clearMaterialRow(row)
}
return
}
if (isDuplicateMaterial(id, index)) {
ElMessage.warning('该物料已在列表中,请勿重复添加')
clearMaterialRow(row)
return
}
const opt = materialOptions.value.find((o) => o.id === id)
mu.materialName = opt ? opt.materialName : ''
row.materialId = id
row.materialName = opt?.materialName || ''
row.specModel = opt?.specModel || ''
row.unit = opt?.unit || ''
}
function addMaterialRow() {
materialUsages.value.push({
_rowKey: nextMaterialRowKey(),
materialId: '',
materialName: '',
specModel: '',
unit: '',
qty: undefined,
remark: ''
})
}
function removeMaterialRow(index: number) {
materialUsages.value = materialUsages.value.filter((_, i) => i !== index)
}
/** 合并同物料多条变动记录(历史重复扣减时),并生成稳定行 key */
function materialRowsFromApi(items: QcTaskMaterialUsageItem[]): MaterialUsageRow[] {
const merged = new Map<string, MaterialUsageRow>()
for (const u of items || []) {
const mid = (u.materialId || '').trim()
if (!mid) continue
const qtyRaw = Math.abs(Number(u.changeQty) || 0)
const prev = merged.get(mid)
if (prev) {
prev.qty = (Number(prev.qty) || 0) + qtyRaw
} else {
merged.set(mid, {
_rowKey: nextMaterialRowKey(),
materialId: mid,
materialName: u.materialName || '',
specModel: '',
unit: '',
qty: qtyRaw > 0 ? qtyRaw : undefined,
remark: u.remark || ''
})
}
}
return Array.from(merged.values())
}
function enrichMaterialUsageRows(
rows: MaterialUsageRow[],
options: QcMaterialOption[] = materialOptions.value
) {
for (const row of rows) {
const opt = options.find((o) => o.id === row.materialId)
if (opt) {
row.materialName = opt.materialName || row.materialName
row.specModel = opt.specModel || ''
row.unit = opt.unit || ''
}
}
}
const viewVisible = ref(false)
const viewDetail = ref<QcTaskDetail | null>(null)
const viewMaterialUsages = ref<MaterialUsageRow[]>([])
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
function apiFilesToFinanceItems(
@@ -603,6 +813,20 @@
} catch {
;(detail as any).results = []
}
viewMaterialUsages.value = []
if (row.taskType !== 'dsfjc') {
try {
const [savedUsages, opts] = await Promise.all([
fetchQcTaskMaterialUsages(row.id).catch(() => [] as QcTaskMaterialUsageItem[]),
fetchQcMaterialOptions().catch(() => [] as QcMaterialOption[])
])
const rows = materialRowsFromApi(savedUsages || [])
enrichMaterialUsageRows(rows, opts || [])
viewMaterialUsages.value = rows
} catch {
viewMaterialUsages.value = []
}
}
viewDetail.value = detail
viewVisible.value = true
}
@@ -623,6 +847,7 @@
resultConclusion.value = ''
resultAttachments.value = apiFilesToFinanceItems(row.attachmentFiles)
materialUsages.value = []
materialOptions.value = []
resultLoading.value = true
try {
const detail = await fetchQcTaskDetail(row.id)
@@ -632,16 +857,21 @@
resultAttachments.value = apiFilesToFinanceItems(detail.attachmentFiles)
const results = await fetchQcTaskResults(row.id)
resultParams.value = normalizeResultParams(results || [])
if (row.taskType !== 'dsfjc') {
const [opts, savedUsages] = await Promise.all([
fetchQcMaterialOptions().catch(() => [] as QcMaterialOption[]),
fetchQcTaskMaterialUsages(row.id).catch(() => [] as QcTaskMaterialUsageItem[])
])
materialOptions.value = opts || []
materialUsages.value = materialRowsFromApi(savedUsages || [])
enrichMaterialUsageRows(materialUsages.value)
}
} catch {
resultParams.value = []
materialUsages.value = []
} finally {
resultLoading.value = false
}
try {
materialOptions.value = await fetchQcMaterialOptions()
} catch {
materialOptions.value = []
}
resultVisible.value = true
}
@@ -658,17 +888,33 @@
ElMessage.warning('请上传第三方检测材料')
return
}
if (resultTaskType.value !== 'dsfjc') {
materialUsages.value = materialUsages.value.filter((mu) => !!mu.materialId)
const seenMaterialIds = new Set<string>()
for (const mu of materialUsages.value) {
if (!mu.materialId) continue
if (parseMaterialQty(mu.qty) == null) {
ElMessage.warning('请为已选物料填写有效用量')
return
}
if (seenMaterialIds.has(mu.materialId)) {
ElMessage.warning('物料列表中存在重复物料,请检查')
return
}
seenMaterialIds.add(mu.materialId)
}
}
resultLoading.value = true
try {
const validMaterialUsages = materialUsages.value.filter(
(mu) => mu.materialId && mu.qty && mu.qty > 0
)
const validMaterialUsages = materialUsages.value
.map((mu) => ({ ...mu, qtyNum: parseMaterialQty(mu.qty) }))
.filter((mu) => mu.materialId && mu.qtyNum != null)
const data: {
conclusion: string
results: QcTaskResultItem[]
remark?: string
thirdPartyAttachments?: string
materialUsages?: Array<{ materialId: string; qty: number }>
materialUsages?: Array<{ materialId: string; qty: number; remark?: string }>
} = {
conclusion: resultConclusion.value,
remark: resultRemark.value,
@@ -680,10 +926,11 @@
}))
}
data.thirdPartyAttachments = financeAttachmentsToUrlString(resultAttachments.value)
if (validMaterialUsages.length > 0) {
if (resultTaskType.value !== 'dsfjc') {
data.materialUsages = validMaterialUsages.map((mu) => ({
materialId: mu.materialId,
qty: mu.qty!,
qty: mu.qtyNum!,
remark: mu.remark?.trim() || undefined
}))
}
await fetchCompleteTaskWithResult(resultTaskId.value, data)
@@ -719,24 +966,50 @@
.material-usage-section {
margin-top: 12px;
.material-usage-row {
.material-usage-head {
display: flex;
align-items: flex-start;
gap: 12px;
margin-bottom: 12px;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
border-left: none;
padding-left: 0;
}
.mu-field-material {
flex: 3;
.section-title-left {
display: flex;
align-items: center;
min-width: 0;
&::before {
content: '';
width: 3px;
height: 18px;
background: #10b981;
border-radius: 2px;
margin-right: 10px;
flex-shrink: 0;
}
.mu-field-qty {
flex: 1.2;
h3 {
margin: 0;
font-size: 15px;
font-weight: 600;
color: #1e293b;
}
}
.mu-remove-btn {
margin-top: 28px;
padding: 8px;
}
.material-usage-table {
width: 100%;
}
.inner-cell-input {
width: 100%;
}
:deep(.inner-cell-input.el-input-number) {
width: 100%;
}
}
@@ -779,6 +1052,7 @@
}
}
.qc-task-view-material-usages,
.qc-task-view-attachments {
padding: 18px 22px;
border-bottom: none;
@@ -794,6 +1068,16 @@
}
}
.qc-task-view-material-table {
width: 100%;
}
.view-material-empty {
margin: 0;
font-size: 13px;
color: #64748b;
}
.view-reception-attachments {
margin-top: 4px;
@@ -89,6 +89,7 @@
v-loading="loading"
stripe
row-key="protocolSampleId"
:row-class-name="sampleReceptionRowClassName"
@row-dblclick="handleRowDblClick"
@selection-change="handlePendingSelectionChange"
>
@@ -679,6 +680,35 @@
return Number.isFinite(n) ? String(n) : '-'
}
function parseEtaLocalDate(eta: unknown): Date | null {
const s = String(eta || '').trim()
if (!s) return null
const normalized = s.replace('T', ' ').slice(0, 19)
const m = normalized.match(/^(\d{4})-(\d{2})-(\d{2})/)
if (!m) return null
const y = Number(m[1])
const mo = Number(m[2]) - 1
const d = Number(m[3])
const dt = new Date(y, mo, d)
if (dt.getFullYear() !== y || dt.getMonth() !== mo || dt.getDate() !== d) return null
return dt
}
function startOfLocalDay(d: Date) {
return new Date(d.getFullYear(), d.getMonth(), d.getDate())
}
function isEtaTodayOrEarlier(etaTime: unknown) {
const etaDay = parseEtaLocalDate(etaTime)
if (!etaDay) return false
return etaDay.getTime() <= startOfLocalDay(new Date()).getTime()
}
function sampleReceptionRowClassName({ row }: { row: Record<string, unknown> }) {
if (activeTab.value !== 'pending') return ''
return isEtaTodayOrEarlier(row.etaTime) ? 'row-eta-overdue' : ''
}
function apiFilesToFinanceItems(
files?: Array<{ id?: string; fileUrl?: string; fileName?: string; fileType?: string }>
): FinanceAttachmentItem[] {
@@ -1236,6 +1266,15 @@
}
}
.list-table :deep(tr.row-eta-overdue > td.el-table__cell) {
background-color: #fee2e2 !important;
color: #991b1b;
}
.list-table :deep(tr.row-eta-overdue:hover > td.el-table__cell) {
background-color: #fecaca !important;
}
.sample-reception-dialog .check-list-view,
.batch-receive-dialog .check-list-view {
pointer-events: none;
@@ -126,7 +126,9 @@
</el-form-item>
</div>
<div class="form-section">
<div class="section-title">开票信息</div>
<div class="section-title section-title--static">
<div class="section-title-left"><h3>开票信息</h3></div>
</div>
<div class="form-grid-2">
<el-form-item label="发票抬头">
<el-input v-model="companyForm.invoiceTitle" placeholder="与营业执照一致" />
@@ -167,8 +169,8 @@
<el-dialog
v-model="itemDialogVisible"
:title="itemDialogTitle"
width="880px"
class="dialog-form-layout"
width="980px"
class="dialog-form-layout third-party-item-config-dialog"
align-center
destroy-on-close
:close-on-click-modal="false"
@@ -194,7 +196,7 @@
v-loading="itemLoading"
stripe
row-key="id"
max-height="360"
max-height="460"
@selection-change="onItemSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
@@ -233,77 +235,130 @@
<el-dialog
v-model="itemFormVisible"
:title="itemFormTitle"
width="640px"
width="840px"
class="dialog-form-layout"
align-center
append-to-body
destroy-on-close
:close-on-click-modal="false"
>
<el-form ref="itemFormRef" :model="itemForm" :rules="itemFormRules" label-position="top" class="biz-dialog-form">
<el-form
ref="itemFormRef"
:model="itemForm"
:rules="itemFormRules"
label-position="top"
class="biz-dialog-form item-dialog-form"
scroll-to-error
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
>
<div class="form-section">
<div class="section-title">
<div class="section-title-left"><h3>基本信息</h3></div>
</div>
<div class="form-grid-2">
<el-form-item label="项目编码" prop="itemCode">
<el-input v-model="itemForm.itemCode" :disabled="itemIsEdit" />
</el-form-item>
<el-form-item label="项目名称" prop="itemName">
<el-input v-model="itemForm.itemName" />
</el-form-item>
<el-form-item label="费用(元)" prop="feeAmount">
<el-input-number v-model="itemForm.feeAmount" :min="0" :precision="2" style="width: 100%" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="itemForm.status">
<el-radio value="1">启用</el-radio>
<el-radio value="0">停用</el-radio>
</el-radio-group>
</el-form-item>
</div>
</div>
<div class="form-section param-config-section">
<div class="section-title">
<div class="section-title-left"><h3>参数配置</h3></div>
<el-button class="btn-main" type="primary" size="small" @click="addItemParam">
<el-icon><Plus /></el-icon>添加参数
</el-button>
</div>
<el-table
v-if="itemParams.length > 0"
:data="itemParams"
stripe
class="list-table inner-table"
max-height="280"
>
<el-table-column
type="index"
label="序号"
width="56"
align="center"
header-align="center"
:index="(i) => i + 1"
/>
<el-table-column label="参数名称" min-width="140" align="center" header-align="center">
<template #default="{ row }">
<el-input v-model="row.paramName" placeholder="参数名称" class="inner-cell-input" />
</template>
</el-table-column>
<el-table-column label="参数编码" min-width="120" align="center" header-align="center">
<template #default="{ row }">
<el-input v-model="row.paramCode" placeholder="参数编码" class="inner-cell-input" />
</template>
</el-table-column>
<el-table-column label="单位" min-width="120" align="center" header-align="center">
<template #default="{ row }">
<el-select
v-model="row.paramUnit"
placeholder="单位"
clearable
filterable
remote
reserve-keyword
class="inner-cell-select"
:loading="unitOptionsLoading"
:remote-method="searchUnitOptions"
>
<el-option
v-for="item in unitOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="类型" width="110" align="center" header-align="center">
<template #default="{ row }">
<el-select v-model="row.paramType" class="inner-cell-select">
<el-option label="文本" value="text" />
<el-option label="数字" value="number" />
</el-select>
</template>
</el-table-column>
<el-table-column label="必填" width="72" align="center" header-align="center">
<template #default="{ row }">
<el-switch v-model="row.requiredFlag" active-value="1" inactive-value="0" />
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center" header-align="center">
<template #default="{ $index }">
<el-button link type="danger" size="small" @click="removeItemParam($index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-empty
v-else
description="暂无参数,点击上方按钮添加"
:image-size="48"
/>
</div>
<div class="form-grid-2">
<el-form-item label="项目编码" prop="itemCode">
<el-input v-model="itemForm.itemCode" :disabled="itemIsEdit" />
</el-form-item>
<el-form-item label="项目名称" prop="itemName">
<el-input v-model="itemForm.itemName" />
</el-form-item>
<el-form-item label="费用(元)" prop="feeAmount">
<el-input-number v-model="itemForm.feeAmount" :min="0" :precision="2" style="width: 100%" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="itemForm.status">
<el-radio value="1">启用</el-radio>
<el-radio value="0">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" class="form-col-span-2">
<el-input v-model="itemForm.remark" type="textarea" :rows="2" />
</el-form-item>
</div>
<div class="form-section" style="margin-top: 8px">
<div class="section-title">参数配置</div>
<div class="param-row param-row-header">
<span style="flex: 2">参数名称</span>
<span style="flex: 1.5">参数编码</span>
<span style="flex: 1">单位</span>
<span style="flex: 1">类型</span>
<span style="width: 40px; text-align: center">必填</span>
<span style="width: 32px"></span>
</div>
<div v-if="itemParams.length === 0" style="text-align: center; padding: 8px 0; color: #94a3b8; font-size: 13px">
暂无参数,点击下方按钮添加
</div>
<div v-for="(param, idx) in itemParams" :key="idx" class="param-row">
<el-input v-model="param.paramName" placeholder="参数名称" style="flex: 2" />
<el-input v-model="param.paramCode" placeholder="参数编码" style="flex: 1.5" />
<el-select
v-model="param.paramUnit"
placeholder="单位"
clearable
filterable
remote
reserve-keyword
:loading="unitOptionsLoading"
:remote-method="searchUnitOptions"
style="flex: 1"
>
<el-option
v-for="item in unitOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-select v-model="param.paramType" style="flex: 1">
<el-option label="文本" value="text" />
<el-option label="数字" value="number" />
</el-select>
<el-switch v-model="param.requiredFlag" active-value="1" inactive-value="0" />
<el-button class="btn-op-item" @click="removeItemParam(idx)"><el-icon><Delete /></el-icon></el-button>
</div>
<el-button type="primary" plain style="margin-top: 8px" @click="addItemParam"><el-icon><Plus /></el-icon>添加参数</el-button>
</div>
</el-form>
<template #footer>
<el-button class="btn-ghost" @click="itemFormVisible = false">
@@ -747,37 +802,86 @@
padding-top: 0;
}
.form-section {
.biz-dialog-form .form-section {
background: #fafafa;
border-radius: 8px;
padding: 20px;
margin: 4px 0;
padding: 16px 20px;
margin: 4px 0 16px;
}
.form-section .section-title {
font-size: 15px;
font-weight: 500;
color: #303133;
margin-bottom: 16px;
padding-bottom: 12px;
padding-left: 10px;
border-bottom: 1px solid #ebeef5;
border-left: 3px solid #10b981;
}
.param-row {
.biz-dialog-form .section-title {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
justify-content: space-between;
margin-bottom: 14px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
}
.param-row-header {
font-size: 12px;
color: #64748b;
font-weight: 500;
padding-bottom: 4px;
border-bottom: 1px solid #e2e8f0;
margin-bottom: 10px;
.biz-dialog-form .section-title--static {
justify-content: flex-start;
}
.biz-dialog-form .section-title-left {
display: flex;
align-items: center;
min-width: 0;
&::before {
content: '';
width: 3px;
height: 18px;
background: #10b981;
border-radius: 2px;
margin-right: 10px;
flex-shrink: 0;
}
h3 {
margin: 0;
font-size: 15px;
font-weight: 600;
color: #1e293b;
}
}
.item-dialog-form {
.form-section:last-of-type {
margin-bottom: 0;
}
.inner-table {
width: 100%;
:deep(td.el-table__cell) {
padding-top: 8px;
padding-bottom: 8px;
}
:deep(.inner-cell-input) {
.el-input__wrapper {
min-height: 36px;
padding-top: 2px;
padding-bottom: 2px;
}
.el-input__inner {
height: 32px;
line-height: 32px;
}
}
}
.inner-cell-select {
width: 100%;
}
}
</style>
<!-- 检测项目配置弹窗 teleport 到 body,尺寸类名需非 scoped -->
<style lang="scss">
.el-dialog.third-party-item-config-dialog.dialog-form-layout {
max-height: calc(100vh);
min-height: min(740px, calc(100vh - 80px));
}
</style>
@@ -3,7 +3,7 @@
<el-dialog
:model-value="modelValue"
:title="dialogTitle"
width="1200px"
width="1380px"
class="dialog-form-layout"
align-center
destroy-on-close