This commit is contained in:
zlwu committed 2026-05-24 10:21:14 +08:00
1 parent 9a93140391
commit 2eb1807fe6
7 files changed
+326 -76

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
# Graph Report - src (2026-05-24) # Graph Report - src (2026-05-24)
## Corpus Check ## Corpus Check
- Large corpus: 595 files · ~325,287 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only. - Large corpus: 595 files · ~325,295 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
## Summary ## Summary
- 1515 nodes · 1895 edges · 34 communities detected - 1515 nodes · 1895 edges · 34 communities detected
+3 -3
View File
@@ -17,8 +17,8 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1778582068.0254738, "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/common/index.ts": 1778582068.0254738,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1778582068.026978, "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/directive/directive.d.ts": 1778582068.026978,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1778582068.0246184, "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/api/api.d.ts": 1778582068.0246184,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1779586894.4437778, "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts": 1779587829.0283062,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1779586887.6455336, "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/auto-imports.d.ts": 1779587821.900029,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1778582068.0293276, "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/store/index.ts": 1778582068.0293276,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1778775532.895603, "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/router/index.ts": 1778775532.895603,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1778582068.004279, "/Users/wuzilong/work/00code/zkxw/web-admin/src/locales/index.ts": 1778582068.004279,
@@ -282,7 +282,7 @@
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-task/index.vue": 1779586846.3205786, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-task/index.vue": 1779586846.3205786,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/reagent/index.vue": 1779543540.724915, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/reagent/index.vue": 1779543540.724915,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue": 1779586813.5815248, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/index.vue": 1779586813.5815248,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1779506613.3205173, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts": 1779587781.963615,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1779525781.678735, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/StepDetailPanel.vue": 1779525781.678735,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue": 1779525781.679505, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskInputEnhancedTable.vue": 1779525781.679505,
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue": 1779525781.680895, "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/TaskExecutionDialog.vue": 1779525781.680895,
+2 -1
View File
@@ -85,7 +85,8 @@
[/(工单)/, 'auto-title-receipt'], [/(工单)/, 'auto-title-receipt'],
[/(流程|待办|已办|申请|请假|执行)/, 'auto-title-operation'], [/(流程|待办|已办|申请|请假|执行)/, 'auto-title-operation'],
[/(耗材|试剂)/, 'auto-title-box'], [/(耗材|试剂)/, 'auto-title-box'],
[/(工艺|路线)/, 'auto-title-settings'], [/(路线)/, 'auto-title-route'],
[/(工艺)/, 'auto-title-settings'],
[/(规格|价格|体系|渠道|项目)/, 'auto-title-category'], [/(规格|价格|体系|渠道|项目)/, 'auto-title-category'],
[/(类型)/, 'auto-title-category'], [/(类型)/, 'auto-title-category'],
[/(任务|调度)/, 'auto-title-operation'], [/(任务|调度)/, 'auto-title-operation'],
@@ -499,6 +499,10 @@ body {
.console-container .panel-top h2.auto-title-user { .console-container .panel-top h2.auto-title-user {
--panel-title-icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='12' cy='7' r='4'/%3E%3C/svg%3E"); --panel-title-icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='12' cy='7' r='4'/%3E%3C/svg%3E");
} }
.page-container :is(.panel-top h2, .panel-top h3, .storage-content-header h3, .storage-aside__head h2, .user-dept-aside__head h2).auto-title-route,
.console-container .panel-top h2.auto-title-route {
--panel-title-icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='6' cy='19' r='3'/%3E%3Cpath d='M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15'/%3E%3Ccircle cx='18' cy='5' r='3'/%3E%3C/svg%3E");
}
.page-container :is(.panel-top h2, .panel-top h3, .storage-content-header h3, .storage-aside__head h2, .user-dept-aside__head h2).auto-title-settings, .page-container :is(.panel-top h2, .panel-top h3, .storage-content-header h3, .storage-aside__head h2, .user-dept-aside__head h2).auto-title-settings,
.console-container .panel-top h2.auto-title-settings { .console-container .panel-top h2.auto-title-settings {
--panel-title-icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z'/%3E%3Cpath d='M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-1.5 2.6-0.2-.1a2 2 0 0 0-2.1.4 1.8 1.8 0 0 0-.6 2v.2H9.6v-.2a1.8 1.8 0 0 0-.6-2 2 2 0 0 0-2.1-.4l-.2.1-1.5-2.6.1-.1A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-1.6-1.1H3v-3h.0a1.7 1.7 0 0 0 1.6-1.1 1.7 1.7 0 0 0-.3-1.9l-.1-.1 1.5-2.6.2.1a2 2 0 0 0 2.1-.4 1.8 1.8 0 0 0 .6-2V2h4.8v.2a1.8 1.8 0 0 0 .6 2 2 2 0 0 0 2.1.4l.2-.1 1.5 2.6-.1.1a1.7 1.7 0 0 0-.3 1.9A1.7 1.7 0 0 0 21 10.9h.0v3H21a1.7 1.7 0 0 0-1.6 1.1z'/%3E%3C/svg%3E"); --panel-title-icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z'/%3E%3Cpath d='M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-1.5 2.6-0.2-.1a2 2 0 0 0-2.1.4 1.8 1.8 0 0 0-.6 2v.2H9.6v-.2a1.8 1.8 0 0 0-.6-2 2 2 0 0 0-2.1-.4l-.2.1-1.5-2.6.1-.1A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-1.6-1.1H3v-3h.0a1.7 1.7 0 0 0 1.6-1.1 1.7 1.7 0 0 0-.3-1.9l-.1-.1 1.5-2.6.2.1a2 2 0 0 0 2.1-.4 1.8 1.8 0 0 0 .6-2V2h4.8v.2a1.8 1.8 0 0 0 .6 2 2 2 0 0 0 2.1.4l.2-.1 1.5 2.6-.1.1a1.7 1.7 0 0 0-.3 1.9A1.7 1.7 0 0 0 21 10.9h.0v3H21a1.7 1.7 0 0 0-1.6 1.1z'/%3E%3C/svg%3E");
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "label": "useTaskStepEditor.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1"}, {"id": "production_work_order_usetaskstepeditor_toapiflag", "label": "toApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35"}, {"id": "production_work_order_usetaskstepeditor_fromapiflag", "label": "fromApiFlag()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39"}, {"id": "production_work_order_usetaskstepeditor_normalizestepsubtables", "label": "normalizeStepSubTables()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L42"}, {"id": "production_work_order_usetaskstepeditor_parseordernum", "label": "parseOrderNum()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L57"}, {"id": "production_work_order_usetaskstepeditor_usetaskstepeditor", "label": "useTaskStepEditor()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L64"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production_task", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_production", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L24", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_cell_product", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L25", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_api_admin", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L26", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "users_wuzilong_work_00code_zkxw_web_admin_src_utils_tree_path", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L32", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_toapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L35", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_production_production_work_order_usetaskstepeditor_ts", "target": "production_work_order_usetaskstepeditor_fromapiflag", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/production-work-order/useTaskStepEditor.ts", "source_location": "L39", "weight": 1.0}, {"source": "users_wuLine truncated
@@ -146,6 +146,7 @@
<el-select v-model="logSearchForm.changeType" placeholder="全部" clearable> <el-select v-model="logSearchForm.changeType" placeholder="全部" clearable>
<el-option label="入库" value="in" /> <el-option label="入库" value="in" />
<el-option label="出库" value="out" /> <el-option label="出库" value="out" />
<el-option label="生产扣减" value="use" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="时间范围"> <el-form-item label="时间范围">
@@ -162,7 +163,7 @@
</el-form-item> </el-form-item>
</div> </div>
<div class="filter-actions-row"> <div class="filter-actions-row">
<span class="filter-hint">按耗材、变动类型和时间范围查询变动记录。</span> <span class="filter-hint">按耗材编码/名称、变动类型和时间范围查询变动记录。</span>
<div class="filter-actions-end"> <div class="filter-actions-end">
<el-button class="btn-ghost" @click="handleLogReset"><el-icon><RefreshLeft /></el-icon>重置</el-button> <el-button class="btn-ghost" @click="handleLogReset"><el-icon><RefreshLeft /></el-icon>重置</el-button>
<el-button class="btn-main" type="primary" @click="handleLogSearch"><el-icon><Search /></el-icon>搜索</el-button> <el-button class="btn-main" type="primary" @click="handleLogSearch"><el-icon><Search /></el-icon>搜索</el-button>
@@ -178,6 +179,7 @@
v-loading="logLoading" v-loading="logLoading"
stripe stripe
row-key="id" row-key="id"
@row-dblclick="handleLogView"
> >
<el-table-column <el-table-column
type="index" type="index"
@@ -191,8 +193,8 @@
<el-table-column prop="materialName" label="耗材名称" min-width="140" align="center" header-align="center" /> <el-table-column prop="materialName" label="耗材名称" min-width="140" align="center" header-align="center" />
<el-table-column label="变动类型" min-width="100" align="center" header-align="center"> <el-table-column label="变动类型" min-width="100" align="center" header-align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-tag :type="getChangeTypeTagType(row.changeType)" size="small"> <el-tag :type="getChangeTypeTagType(row)" size="small">
{{ getChangeTypeLabel(row.changeType) }} {{ getChangeTypeLabel(row) }}
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
@@ -205,6 +207,12 @@
</el-table-column> </el-table-column>
<el-table-column prop="stockBefore" label="变动前库存" min-width="120" align="center" header-align="center" /> <el-table-column prop="stockBefore" label="变动前库存" min-width="120" align="center" header-align="center" />
<el-table-column prop="stockAfter" label="变动后库存" min-width="120" align="center" header-align="center" /> <el-table-column prop="stockAfter" label="变动后库存" min-width="120" align="center" header-align="center" />
<el-table-column label="工单号" min-width="140" align="center" header-align="center">
<template #default="{ row }">{{ row.taskNo || '-' }}</template>
</el-table-column>
<el-table-column label="步骤名称" min-width="140" align="center" header-align="center">
<template #default="{ row }">{{ row.stepName || '-' }}</template>
</el-table-column>
<el-table-column label="来源类型" min-width="120" align="center" header-align="center"> <el-table-column label="来源类型" min-width="120" align="center" header-align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-tag :type="getSourceTagType(row.sourceType)" size="small"> <el-tag :type="getSourceTagType(row.sourceType)" size="small">
@@ -212,23 +220,27 @@
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="工单号" min-width="140" align="center" header-align="center">
<template #default="{ row }">{{ row.taskNo || '-' }}</template>
</el-table-column>
<el-table-column label="步骤名称" min-width="140" align="center" header-align="center">
<template #default="{ row }">{{ row.stepName || '-' }}</template>
</el-table-column>
<el-table-column label="领用人" min-width="100" align="center" header-align="center"> <el-table-column label="领用人" min-width="100" align="center" header-align="center">
<template #default="{ row }">{{ row.receiver || '-' }}</template> <template #default="{ row }">{{ displayLogReceiver(row) }}</template>
</el-table-column> </el-table-column>
<el-table-column label="用途" min-width="120" align="center" header-align="center" show-overflow-tooltip> <el-table-column label="用途" min-width="120" align="center" header-align="center" show-overflow-tooltip>
<template #default="{ row }">{{ row.purpose || '-' }}</template> <template #default="{ row }">{{ displayLogPurpose(row) }}</template>
</el-table-column>
<el-table-column label="领用时间" min-width="170" align="center" header-align="center">
<template #default="{ row }">{{ displayLogUseTime(row) }}</template>
</el-table-column> </el-table-column>
<el-table-column prop="remark" label="备注" min-width="140" align="center" header-align="center" show-overflow-tooltip /> <el-table-column prop="remark" label="备注" min-width="140" align="center" header-align="center" show-overflow-tooltip />
<el-table-column label="操作人" min-width="100" align="center" header-align="center"> <el-table-column label="操作人" min-width="100" align="center" header-align="center">
<template #default="{ row }">{{ row.createByName || row.createBy || '-' }}</template> <template #default="{ row }">{{ row.createByName || row.createBy || '-' }}</template>
</el-table-column> </el-table-column>
<el-table-column prop="createTime" label="变动时间" min-width="170" align="center" header-align="center" /> <el-table-column prop="createTime" label="变动时间" min-width="170" align="center" header-align="center" />
<el-table-column label="操作" min-width="160" fixed="right" align="center" header-align="center">
<template #default="{ row }">
<el-button class="btn-op-item" @click="handleLogView(row)">
<el-icon><View /></el-icon>查看
</el-button>
</template>
</el-table-column>
</el-table> </el-table>
<div class="pagination list-pager-bar"> <div class="pagination list-pager-bar">
<el-pagination <el-pagination
@@ -770,8 +782,8 @@
/> />
<el-table-column label="变动类型" min-width="100" align="center" header-align="center"> <el-table-column label="变动类型" min-width="100" align="center" header-align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-tag :type="getChangeTypeTagType(row.changeType)" size="small"> <el-tag :type="getChangeTypeTagType(row)" size="small">
{{ getChangeTypeLabel(row.changeType) }} {{ getChangeTypeLabel(row) }}
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
@@ -832,6 +844,15 @@
code-label="耗材编码" code-label="耗材编码"
:sections="viewSections" :sections="viewSections"
/> />
<!-- 变动记录查看弹窗 -->
<SystemViewDialog
v-model:visible="logViewVisible"
:title="logViewRow?.materialName || '变动记录详情'"
:icon="Document"
:meta="logViewRow?.createTime || ''"
:sections="logViewSections"
/>
</section> </section>
</div> </div>
</template> </template>
@@ -1015,20 +1036,101 @@
const logTableData = ref<any[]>([]) const logTableData = ref<any[]>([])
const logLoading = ref(false) const logLoading = ref(false)
const logPagination = reactive({ page: 1, pageSize: 10, total: 0 }) const logPagination = reactive({ page: 1, pageSize: 10, total: 0 })
const logViewVisible = ref(false)
const logViewRow = ref<any>(null)
// 变动类型/来源映射 // 变动类型/来源映射
function getChangeTypeLabel(changeType: string) { function isManualOutLogRow(row: { changeType?: string; sourceType?: string }) {
if (changeType === 'in') return '入库' return row.sourceType === 'manual_out'
if (changeType === 'use') return '出库'
return changeType || '-'
} }
function getChangeTypeTagType(changeType: string) { function isStepUseLogRow(row: { changeType?: string; sourceType?: string }) {
if (changeType === 'in') return 'success' return row.changeType === 'use' && row.sourceType === 'step_save'
if (changeType === 'use') return 'warning' }
function getChangeTypeLabel(row: { changeType?: string; sourceType?: string }) {
if (row.changeType === 'in') return '入库'
if (isManualOutLogRow(row)) return '出库'
if (isStepUseLogRow(row)) return '生产扣减'
if (row.changeType === 'use') return '出库'
return row.changeType || '-'
}
function getChangeTypeTagType(row: { changeType?: string; sourceType?: string }) {
if (row.changeType === 'in') return 'success'
if (isManualOutLogRow(row)) return 'warning'
if (isStepUseLogRow(row)) return 'primary'
if (row.changeType === 'use') return 'warning'
return 'info' return 'info'
} }
function displayLogReceiver(row: any) {
if (!isManualOutLogRow(row)) return '-'
return (row.receiver || '').trim() || '-'
}
function displayLogPurpose(row: any) {
if (!isManualOutLogRow(row)) return '-'
return (row.purpose || '').trim() || '-'
}
function displayLogUseTime(row: any) {
if (!isManualOutLogRow(row)) return '-'
return (row.useTime || row.createTime || '').trim() || '-'
}
const logViewSections = computed(() => {
if (!logViewRow.value) return []
const row = logViewRow.value
return [
{
title: '变动信息',
fields: [
{ label: '耗材编码', value: row.materialCode },
{ label: '耗材名称', value: row.materialName },
{
label: '变动类型',
value: getChangeTypeLabel(row),
tagType: getChangeTypeTagType(row) as 'success' | 'warning' | 'primary' | 'info'
},
{
label: '变动数量',
value: row.changeQty < 0 ? String(row.changeQty) : `+${row.changeQty}`
},
{ label: '变动前库存', value: String(row.stockBefore ?? 0) },
{ label: '变动后库存', value: String(row.stockAfter ?? 0) },
{ label: '工单号', value: row.taskNo || '-' },
{ label: '步骤名称', value: row.stepName || '-' },
{
label: '来源类型',
value: getSourceLabel(row.sourceType),
tagType: getSourceTagType(row.sourceType) as 'success' | 'warning' | 'primary' | 'info'
},
{
label: '领用人',
value: displayLogReceiver(row) === '-' ? '—' : displayLogReceiver(row)
},
{
label: '用途',
value: displayLogPurpose(row) === '-' ? '—' : displayLogPurpose(row)
},
{
label: '领用时间',
value: displayLogUseTime(row) === '-' ? '—' : displayLogUseTime(row)
},
{ label: '备注', value: row.remark || '—', span: true },
{ label: '操作人', value: row.createByName || row.createBy || '-' },
{ label: '变动时间', value: row.createTime || '—' }
]
}
]
})
function handleLogView(row: any) {
logViewRow.value = row
logViewVisible.value = true
}
function getSourceLabel(sourceType: string) { function getSourceLabel(sourceType: string) {
if (sourceType === 'manual_in') return '手动入库' if (sourceType === 'manual_in') return '手动入库'
if (sourceType === 'manual_out') return '手动出库' if (sourceType === 'manual_out') return '手动出库'
@@ -1540,9 +1642,27 @@
watch(activeTab, (val) => { watch(activeTab, (val) => {
if (val === 'logs') { if (val === 'logs') {
logPagination.page = 1
loadLogData() loadLogData()
} }
}) })
watchDebounced(
logSearchForm,
() => {
if (activeTab.value !== 'logs') return
logPagination.page = 1
loadLogData()
},
{ debounce: 300, deep: true }
)
watch(logDateRange, () => {
if (activeTab.value !== 'logs') return
logPagination.page = 1
loadLogData()
})
// 输入检索条件时自动搜索(防抖 300ms) // 输入检索条件时自动搜索(防抖 300ms)
watchDebounced( watchDebounced(
searchForm, searchForm,
+175 -51
View File
@@ -219,8 +219,9 @@
</el-form-item> </el-form-item>
<el-form-item label="变动类型"> <el-form-item label="变动类型">
<el-select v-model="allLogFilter.changeType" placeholder="全部" clearable> <el-select v-model="allLogFilter.changeType" placeholder="全部" clearable>
<el-option label="入库" value="in" /> <el-option label="入库" value="in" />
<el-option label="出库" value="out" /> <el-option label="出库" value="out" />
<el-option label="生产扣减" value="use" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="时间范围"> <el-form-item label="时间范围">
@@ -231,12 +232,13 @@
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
clearable
style="width: 100%" style="width: 100%"
/> />
</el-form-item> </el-form-item>
</div> </div>
<div class="filter-actions-row"> <div class="filter-actions-row">
<span class="filter-hint">按试剂编码/名称、变动类型、时间范围筛选。</span> <span class="filter-hint">按试剂编码/名称、变动类型和时间范围查询变动记录。</span>
<div class="filter-actions-end"> <div class="filter-actions-end">
<el-button class="btn-ghost" @click="handleAllLogReset" <el-button class="btn-ghost" @click="handleAllLogReset"
><el-icon><RefreshLeft /></el-icon>重置</el-button ><el-icon><RefreshLeft /></el-icon>重置</el-button
@@ -255,6 +257,8 @@
:data="allLogData" :data="allLogData"
v-loading="allLogLoading" v-loading="allLogLoading"
stripe stripe
row-key="id"
@row-dblclick="handleAllLogView"
> >
<el-table-column <el-table-column
type="index" type="index"
@@ -280,95 +284,86 @@
/> />
<el-table-column label="变动类型" min-width="100" align="center" header-align="center"> <el-table-column label="变动类型" min-width="100" align="center" header-align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-tag <el-tag :type="getChangeTypeTagType(row)" size="small">
:type="row.changeType === 'in' ? 'success' : 'warning'" {{ getChangeTypeLabel(row) }}
size="small"
>
{{ row.changeType === 'in' ? '入库' : '出库' }}
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="变动数量" min-width="100" align="center" header-align="center"> <el-table-column label="变动数量" min-width="120" align="center" header-align="center">
<template #default="{ row }"> <template #default="{ row }">
<span :style="{ color: row.changeQty >= 0 ? '#10b981' : '#ef4444' }"> <span :class="row.changeQty < 0 ? 'qty-negative' : 'qty-positive'">
{{ row.changeQty >= 0 ? '+' : '' }}{{ row.changeQty }} {{ row.changeQty < 0 ? row.changeQty : `+${row.changeQty}` }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="stockBefore" prop="stockBefore"
label="变动前库存" label="变动前库存"
min-width="110" min-width="120"
align="center" align="center"
header-align="center" header-align="center"
/> />
<el-table-column <el-table-column
prop="stockAfter" prop="stockAfter"
label="变动后库存" label="变动后库存"
min-width="110" min-width="120"
align="center" align="center"
header-align="center" header-align="center"
/> />
<el-table-column label="来源" min-width="100" align="center" header-align="center">
<template #default="{ row }">
<el-tag
:type="sourceTypeTagType(row.sourceType)"
size="small"
>
{{ sourceTypeLabel(row.sourceType) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="工单号" min-width="140" align="center" header-align="center"> <el-table-column label="工单号" min-width="140" align="center" header-align="center">
<template #default="{ row }">{{ row.taskNo || '-' }}</template> <template #default="{ row }">{{ row.taskNo || '-' }}</template>
</el-table-column> </el-table-column>
<el-table-column label="步骤名称" min-width="140" align="center" header-align="center"> <el-table-column label="步骤名称" min-width="140" align="center" header-align="center">
<template #default="{ row }">{{ row.stepName || '-' }}</template> <template #default="{ row }">{{ row.stepName || '-' }}</template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="来源类型" min-width="120" align="center" header-align="center">
prop="receiver" <template #default="{ row }">
label="领用人" <el-tag :type="sourceTypeTagType(row.sourceType)" size="small">
min-width="100" {{ sourceTypeLabel(row.sourceType) }}
align="center" </el-tag>
header-align="center" </template>
/> </el-table-column>
<el-table-column <el-table-column label="领用人" min-width="100" align="center" header-align="center">
prop="purpose" <template #default="{ row }">{{ displayLogReceiver(row) }}</template>
label="用途" </el-table-column>
min-width="120" <el-table-column label="用途" min-width="120" align="center" header-align="center" show-overflow-tooltip>
align="center" <template #default="{ row }">{{ displayLogPurpose(row) }}</template>
header-align="center" </el-table-column>
show-overflow-tooltip <el-table-column label="领用时间" min-width="170" align="center" header-align="center">
/> <template #default="{ row }">{{ displayLogUseTime(row) }}</template>
</el-table-column>
<el-table-column <el-table-column
prop="remark" prop="remark"
label="备注" label="备注"
min-width="120" min-width="140"
align="center" align="center"
header-align="center" header-align="center"
show-overflow-tooltip show-overflow-tooltip
/> />
<el-table-column <el-table-column label="操作人" min-width="100" align="center" header-align="center">
prop="createByName" <template #default="{ row }">{{ row.createByName || row.createBy || '-' }}</template>
label="操作人" </el-table-column>
min-width="100"
align="center"
header-align="center"
/>
<el-table-column <el-table-column
prop="createTime" prop="createTime"
label="操作时间" label="变动时间"
min-width="180" min-width="170"
align="center" align="center"
header-align="center" header-align="center"
/> />
<el-table-column label="操作" min-width="160" fixed="right" align="center" header-align="center">
<template #default="{ row }">
<el-button class="btn-op-item" @click="handleAllLogView(row)">
<el-icon><View /></el-icon>查看
</el-button>
</template>
</el-table-column>
</el-table> </el-table>
<div class="pagination list-pager-bar"> <div class="pagination list-pager-bar">
<el-pagination <el-pagination
:current-page="allLogPagination.page" :current-page="allLogPagination.page"
:page-size="allLogPagination.pageSize" :page-size="allLogPagination.pageSize"
:total="allLogPagination.total" :total="allLogPagination.total"
:page-sizes="[10, 20, 50]" :page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next" layout="total, sizes, prev, pager, next"
@size-change=" @size-change="
(val) => ((allLogPagination.page = 1), (allLogPagination.pageSize = val), loadAllLogs()) (val) => ((allLogPagination.page = 1), (allLogPagination.pageSize = val), loadAllLogs())
@@ -1059,6 +1054,15 @@
code-label="试剂编码" code-label="试剂编码"
:sections="viewSections" :sections="viewSections"
/> />
<!-- 变动记录查看弹窗 -->
<SystemViewDialog
v-model:visible="allLogViewVisible"
:title="allLogViewRow?.materialName || '变动记录详情'"
:icon="Document"
:meta="allLogViewRow?.createTime || ''"
:sections="allLogViewSections"
/>
</section> </section>
</div> </div>
</template> </template>
@@ -1120,9 +1124,48 @@
const map: Record<string, string> = { const map: Record<string, string> = {
manual_in: 'success', manual_in: 'success',
manual_out: 'warning', manual_out: 'warning',
step_save: 'info' step_save: 'primary'
} }
return (map[sourceType] || 'info') as 'success' | 'warning' | 'info' return (map[sourceType] || 'info') as 'success' | 'warning' | 'primary' | 'info'
}
function isManualOutLogRow(row: { changeType?: string; sourceType?: string }) {
return row.sourceType === 'manual_out'
}
function isStepUseLogRow(row: { changeType?: string; sourceType?: string }) {
return row.changeType === 'use' && row.sourceType === 'step_save'
}
function getChangeTypeLabel(row: { changeType?: string; sourceType?: string }) {
if (row.changeType === 'in') return '入库'
if (isManualOutLogRow(row)) return '出库'
if (isStepUseLogRow(row)) return '生产扣减'
if (row.changeType === 'use') return '出库'
return row.changeType || '-'
}
function getChangeTypeTagType(row: { changeType?: string; sourceType?: string }) {
if (row.changeType === 'in') return 'success'
if (isManualOutLogRow(row)) return 'warning'
if (isStepUseLogRow(row)) return 'primary'
if (row.changeType === 'use') return 'warning'
return 'info'
}
function displayLogReceiver(row: any) {
if (!isManualOutLogRow(row)) return '-'
return (row.receiver || '').trim() || '-'
}
function displayLogPurpose(row: any) {
if (!isManualOutLogRow(row)) return '-'
return (row.purpose || '').trim() || '-'
}
function displayLogUseTime(row: any) {
if (!isManualOutLogRow(row)) return '-'
return (row.useTime || row.createTime || '').trim() || '-'
} }
const activeTab = ref('list') const activeTab = ref('list')
@@ -1278,6 +1321,60 @@
const allLogFilter = reactive({ keyword: '', changeType: '' }) const allLogFilter = reactive({ keyword: '', changeType: '' })
const allLogDateRange = ref<string[] | null>(null) const allLogDateRange = ref<string[] | null>(null)
const allLogPagination = reactive({ page: 1, pageSize: 10, total: 0 }) const allLogPagination = reactive({ page: 1, pageSize: 10, total: 0 })
const allLogViewVisible = ref(false)
const allLogViewRow = ref<any>(null)
const allLogViewSections = computed(() => {
if (!allLogViewRow.value) return []
const row = allLogViewRow.value
return [
{
title: '变动信息',
fields: [
{ label: '试剂编码', value: row.materialCode },
{ label: '试剂名称', value: row.materialName },
{
label: '变动类型',
value: getChangeTypeLabel(row),
tagType: getChangeTypeTagType(row) as 'success' | 'warning' | 'primary' | 'info'
},
{
label: '变动数量',
value: row.changeQty < 0 ? String(row.changeQty) : `+${row.changeQty}`
},
{ label: '变动前库存', value: String(row.stockBefore ?? 0) },
{ label: '变动后库存', value: String(row.stockAfter ?? 0) },
{ label: '工单号', value: row.taskNo || '-' },
{ label: '步骤名称', value: row.stepName || '-' },
{
label: '来源类型',
value: sourceTypeLabel(row.sourceType),
tagType: sourceTypeTagType(row.sourceType)
},
{
label: '领用人',
value: displayLogReceiver(row) === '-' ? '—' : displayLogReceiver(row)
},
{
label: '用途',
value: displayLogPurpose(row) === '-' ? '—' : displayLogPurpose(row)
},
{
label: '领用时间',
value: displayLogUseTime(row) === '-' ? '—' : displayLogUseTime(row)
},
{ label: '备注', value: row.remark || '—', span: true },
{ label: '操作人', value: row.createByName || row.createBy || '-' },
{ label: '变动时间', value: row.createTime || '—' }
]
}
]
})
function handleAllLogView(row: any) {
allLogViewRow.value = row
allLogViewVisible.value = true
}
const handleSelectionChange = (rows: any[]) => { const handleSelectionChange = (rows: any[]) => {
selectedRows.value = rows selectedRows.value = rows
@@ -1805,6 +1902,23 @@
loadAllLogs() loadAllLogs()
} }
}) })
watchDebounced(
allLogFilter,
() => {
if (activeTab.value !== 'stockLogs') return
allLogPagination.page = 1
loadAllLogs()
},
{ debounce: 300, deep: true }
)
watch(allLogDateRange, () => {
if (activeTab.value !== 'stockLogs') return
allLogPagination.page = 1
loadAllLogs()
})
// 输入检索条件时自动搜索(防抖 300ms) // 输入检索条件时自动搜索(防抖 300ms)
watchDebounced( watchDebounced(
searchForm, searchForm,
@@ -1829,6 +1943,16 @@
} }
} }
.qty-positive {
color: var(--el-color-success);
font-weight: 500;
}
.qty-negative {
color: var(--el-color-danger);
font-weight: 500;
}
.reagent-batch-stock-dialog { .reagent-batch-stock-dialog {
.batch-stock-move-head { .batch-stock-move-head {
margin-bottom: 16px; margin-bottom: 16px;