更新采购订单、发货、合同查询、盘点、入库和配置文件,新增零改单和文档

This commit is contained in:
Developer
2026-06-26 08:13:30 +08:00
parent 0c8c6587bd
commit bb30ba76d0
21 changed files with 3290 additions and 50 deletions

View File

@@ -0,0 +1,291 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>JY1.0 立库详细界面功能说明书</title>
<style>
@page { size: A4; margin: 16mm 13mm 15mm; }
body { font-family: "Microsoft YaHei", "PingFang SC", Arial, sans-serif; color:#222; line-height:1.62; font-size:13.5px; }
h1,h2,h3,h4 { color:#111; margin: 1.1em 0 0.5em; page-break-after: avoid; }
h1 { font-size: 25px; border-bottom: 2px solid #2f6fed; padding-bottom: 10px; }
h2 { font-size: 19px; border-left: 4px solid #2f6fed; padding-left: 10px; }
h3 { font-size: 16px; }
p { margin: 0.35em 0 0.75em; }
ul,ol { margin: 0.35em 0 0.75em 1.4em; }
li { margin: 0.18em 0; }
code { background:#f5f7fa; padding:2px 6px; border-radius:4px; font-family: Consolas, monospace; font-size: 12px; }
table { width:100%; border-collapse: collapse; margin: 10px 0 16px; font-size: 13px; }
th,td { border:1px solid #dcdfe6; padding:8px 10px; vertical-align: top; }
th { background:#f5f7fa; }
tr:nth-child(even) td { background:#fafafa; }
figure { margin: 10px 0 18px; page-break-inside: avoid; }
img { display:block; width:100%; max-width:100%; border:1px solid #dcdfe6; border-radius:4px; box-shadow:0 2px 8px rgba(0,0,0,.08); }
strong { color:#111; }
</style>
</head>
<body>
<h1>JY1.0 立库详细界面功能说明书</h1>
<h2>1. 文档目的</h2>
<p>本文档用于整理 JY1.0 项目中“立库查询/立库可视化”组件目前已经实现的界面结构、后端数据来源、交互逻辑和现有功能范围,便于后续培训、验收、维护和功能扩展。</p>
<p>本文档依据当前前端实现整理,核心代码位置如下:</p>
<ul><li><code>src/views/WarehouseManagement/WarehouseVisualization/index.vue</code></li><li><code>static/config.js</code></li></ul>
<h2>2. 页面定位</h2>
<p>立库详细界面当前对应的页面名称为:</p>
<ul><li>页面组件名:<code>WarehouseVisualizationSystem</code></li><li>页面标签名:<code>立库可视化</code></li><li>目录位置:<code>src/views/WarehouseManagement/WarehouseVisualization/index.vue</code></li></ul>
<p>该页面用于展示立体仓库库位占用情况,并支持按巷道查看、按行层筛选、单击库位查看库位详情或物料明细。</p>
<h2>3. 页面总体结构</h2>
<p><strong>图 1立库可视化主界面</strong></p>
<figure><img src="assets/warehouse-visualization/main.png" alt="立库可视化主界面" /></figure>
<p>页面整体采用标准的 <code>div.app-container + el-card</code> 布局,主体包含以下区域:</p>
<ol><li>顶部筛选与操作区</li><li>统计信息卡片区</li><li>状态图例区</li><li>立库可视化表格区</li><li>分页区</li><li>库位详情弹窗</li><li>物料信息弹窗</li></ol>
<p>当前页面只有一个主标签页:</p>
<ul><li><code>立库可视化</code></li></ul>
<p>代码中保留了图表分析相关逻辑,但当前模板中未实际启用“分析”页签。</p>
<h2>4. 顶部筛选与操作区说明</h2>
<p><strong>图 2筛选区与显示模式切换示意</strong></p>
<figure><img src="assets/warehouse-visualization/filter.png" alt="筛选区与显示模式切换" /></figure>
<p>顶部区域已经实现以下控件:</p>
<h3>4.1 巷道筛选</h3>
<p>控件类型:<code>el-select</code></p>
<p>可选项固定为:</p>
<ul><li>第一巷道</li><li>第二巷道</li><li>第三巷道</li><li>第四巷道</li></ul>
<p>对应值为:</p>
<ul><li><code>1</code></li><li><code>2</code></li><li><code>3</code></li><li><code>4</code></li></ul>
<p>用途:</p>
<ul><li>用户选择巷道后,点击“搜索”,页面会调用立库接口重新获取该巷道数据。</li></ul>
<h3>4.2 行层筛选</h3>
<p>控件类型:<code>el-select</code></p>
<p>用途:</p>
<ul><li>根据当前已加载的立库数据,自动生成“行-层”下拉选项。</li><li>用户可筛选某一行层,只显示对应行数据。</li></ul>
<p>特点:</p>
<ul><li>选项从接口返回数据动态提取。</li><li>会自动去重。</li><li>按数值倒序排列。</li></ul>
<h3>4.3 搜索按钮</h3>
<p>按钮文字:<code>搜索</code></p>
<p>作用:</p>
<ul><li>重置当前页码为第一页。</li><li>先执行前端筛选。</li><li>再根据当前巷道值调用 <code>GetPosition</code> 获取最新立库数据。</li></ul>
<p>说明:</p>
<ul><li>当前“搜索”动作的核心是按巷道重新取数。</li><li>代码里存在 <code>searchKeyword</code> 变量,但页面上没有对应输入框,因此当前没有“按列关键字搜索”的实际入口。</li></ul>
<h3>4.4 重置按钮</h3>
<p>按钮文字:<code>重置</code></p>
<p>作用:</p>
<ul><li>清空状态筛选值</li><li>清空行层筛选值</li><li>清空搜索关键字</li><li>页码回到第一页</li><li>重新执行前端筛选</li></ul>
<p>页面提示:</p>
<ul><li>执行后弹出“筛选条件已重置”。</li></ul>
<p>注意:</p>
<ul><li>当前重置不会主动重新按巷道取数,只是重置前端筛选状态。</li></ul>
<h3>4.5 刷新数据按钮</h3>
<p>按钮文字:<code>刷新数据</code></p>
<p>作用:</p>
<ul><li>触发加载动画</li><li>500ms 后重新装载当前 <code>defaultWarehouseData</code></li><li>更新时间戳</li><li>弹出“数据刷新成功”</li></ul>
<p>说明:</p>
<ul><li>刷新逻辑最终仍依赖最近一次 <code>GetPosition</code> 返回并写入的 <code>defaultWarehouseData</code></li><li>如果前面已经切换过巷道,刷新展示的是当前缓存的该次结果。</li></ul>
<h3>4.6 显示模式切换开关</h3>
<p>控件类型:<code>el-switch</code></p>
<p>两种模式:</p>
<ul><li>显示文字:<code>text</code></li><li>显示图标:<code>icon</code></li></ul>
<p>作用:</p>
<ul><li>控制库位单元格显示简称文字还是图标。</li></ul>
<p>切换效果:</p>
<ul><li>文字模式显示:<code>物 / 盘 / 空 / 锁</code></li><li>图标模式显示:</li></ul>
<p>- 锁库:<code>el-icon-lock</code> - 有物料有托盘:<code>el-icon-box</code> - 有托盘无物料:<code>el-icon-tickets</code> - 空库位:<code>el-icon-minus</code></p>
<p>页面提示:</p>
<ul><li>切换后会提示当前已切换到文字模式或图标模式。</li></ul>
<h2>5. 统计信息卡片区说明</h2>
<p>页面中部已实现 6 个统计卡片:</p>
<h3>5.1 总库位数</h3>
<p>计算方式:</p>
<ul><li><code>立库行数 * 列数</code></li></ul>
<h3>5.2 有物料有托盘</h3>
<p>统计状态值为 <code>2</code> 的库位数量。</p>
<h3>5.3 有托盘无物料</h3>
<p>统计状态值为 <code>1</code> 的库位数量。</p>
<h3>5.4 空闲库位</h3>
<p>统计状态值为 <code>0</code> 的库位数量。</p>
<h3>5.5 使用率</h3>
<p>计算方式:</p>
<ul><li><code>(有物料有托盘 + 有托盘无物料) / 总库位数 * 100%</code></li></ul>
<p>保留 1 位小数。</p>
<h3>5.6 数据行数</h3>
<p>显示当前 <code>warehouseData.length</code>,即当前已加载的立库行数。</p>
<h2>6. 状态图例说明</h2>
<p>页面已实现状态图例,用于帮助用户理解库位颜色含义:</p>
<ul><li>绿色:有物料有托盘</li><li>橙色:有托盘无物料</li><li>灰色:空库位</li><li>蓝色边框:当前选中库位</li></ul>
<p>另外,代码中还支持:</p>
<ul><li>蓝色底:锁库(状态值 <code>9</code></li></ul>
<p>该状态在单元格样式和状态文字中已实现,但图例区未单独展示“锁库”说明。</p>
<h2>7. 立库可视化表格区说明</h2>
<p>这是页面的核心区域。</p>
<h3>7.1 展示形式</h3>
<p>页面使用原生 <code>table</code> 绘制立库矩阵,不是 <code>el-table</code></p>
<p>结构包含:</p>
<ul><li>表头列号</li><li>左侧行层标识</li><li>中间库位矩阵单元格</li></ul>
<h3>7.2 行列组织方式</h3>
<h4></h4>
<p>每一行对应一个“行-层”值,例如:</p>
<ul><li><code>01-01</code></li><li><code>03-12</code></li></ul>
<p>页面左侧固定显示该行层编号。</p>
<h4></h4>
<p>每列按照数字列号展示,从 <code>1</code><code>columnCount</code></p>
<p><code>columnCount</code> 的计算规则为:</p>
<ul><li>读取首行对象字段</li><li>排除 <code>行-层</code></li><li>其余字段个数即为列数</li></ul>
<h3>7.3 单元格状态值</h3>
<p>当前库位单元格支持以下状态:</p>
<table><thead><tr><th>状态值</th><th>含义</th><th>前端显示简称</th><th>颜色</th></tr></thead><tbody><tr><td><code>0</code></td><td>空库位</td><td></td><td>灰色</td></tr><tr><td><code>1</code></td><td>有托盘无物料</td><td></td><td>橙色</td></tr><tr><td><code>2</code></td><td>有物料有托盘</td><td></td><td>绿色</td></tr><tr><td><code>9</code></td><td>锁库/在存取</td><td></td><td>蓝色</td></tr></tbody></table>
<h3>7.4 单元格交互效果</h3>
<p>当前已实现以下交互:</p>
<ul><li>鼠标悬停单元格时放大</li><li>鼠标悬停显示状态浮层</li><li>点击后高亮选中库位</li><li>点击后高亮整行对应的行层</li><li>点击某列后,对应列表头也会高亮</li></ul>
<h3>7.5 空数据处理</h3>
<p><code>warehouseData.length === 0</code> 时,页面显示空白占位区域。</p>
<p>说明:</p>
<ul><li>代码里曾预留 <code>el-empty</code> 结构,但目前已注释,没有实际“暂无数据”文案和“加载示例数据”按钮。</li></ul>
<h2>8. 分页功能说明</h2>
<p>页面底部已实现分页控件,条件为:</p>
<ul><li><code>totalPages &gt; 1</code> 时显示</li></ul>
<p>分页能力包括:</p>
<ul><li>查看总数</li><li>切换每页条数</li><li>上一页/下一页</li><li>页码跳转</li><li>直接输入页码跳转</li></ul>
<p>每页可选条数:</p>
<ul><li><code>5</code></li><li><code>8</code></li><li><code>10</code></li><li><code>15</code></li></ul>
<p>说明:</p>
<ul><li>分页针对的是“行数据”,不是单元格。</li><li>也就是一次显示若干个“行-层”。</li></ul>
<h2>9. 库位详情弹窗说明</h2>
<p><strong>图 3库位详情弹窗示意</strong></p>
<figure><img src="assets/warehouse-visualization/detail.png" alt="库位详情弹窗" /></figure>
<p>点击非物料状态库位时,会弹出“库位详情”窗口。</p>
<p>触发条件:</p>
<ul><li>单元格状态不等于 <code>2</code></li></ul>
<h3>9.1 弹窗标题</h3>
<p>格式:</p>
<ul><li><code>库位详情 + 库位编号</code></li></ul>
<p>例如:</p>
<ul><li><code>库位详情 03-12-01</code></li></ul>
<h3>9.2 弹窗内容</h3>
<p>当前弹窗内容为库位状态说明文本,即 <code>selectedCellDetail.description</code></p>
<p>不同状态说明如下:</p>
<ul><li><code>0</code>:空闲库位,可存放物料</li><li><code>1</code>:有托盘但无物料,需补充物料</li><li><code>2</code>:有托盘且有物料,正常使用中</li><li><code>9</code>:在存取,库位已锁</li></ul>
<h3>9.3 当前实现特点</h3>
<p>目前该弹窗属于轻量说明弹窗,展示内容较简洁,没有进一步展示:</p>
<ul><li>仓库编号</li><li>创建时间</li><li>最近操作人</li><li>任务锁定来源</li></ul>
<p>这些字段在前端对象构造里有部分预留,但未在弹窗模板中完整展示。</p>
<h2>10. 物料信息弹窗说明</h2>
<p><strong>图 4物料信息弹窗示意</strong></p>
<figure><img src="assets/warehouse-visualization/material.png" alt="物料信息弹窗" /></figure>
<p>点击状态为 <code>2</code> 的库位时,会弹出“库位信息”窗口。</p>
<p>触发条件:</p>
<ul><li>单元格状态等于 <code>2</code></li></ul>
<h3>10.1 弹窗标题</h3>
<p>格式:</p>
<ul><li><code>库位信息 + pno</code></li></ul>
<p>即使用返回数据第一条的库位编号作为标题显示。</p>
<h3>10.2 数据来源</h3>
<p>点击单元格后会调用 <code>StockDetail(rowLayer, column)</code>,查询数据库视图:</p>
<ul><li><code>StockDetailView</code></li></ul>
<p>查询方式为手写 SQL</p>
<ul><li><code>select ... from StockDetailView where pno='xx-xx-xx'</code></li></ul>
<h3>10.3 库位编号拼接规则</h3>
<p>点击单元格时,前端会把界面上的 <code>行-层</code> 和列号转换为后端查询用的 <code>pno</code></p>
<ol><li>将列号补足 2 位</li><li><code>行-层</code><code>-</code> 拆分</li><li><code>行-列-层</code> 重新拼装</li></ol>
<p>例如:</p>
<ul><li>页面行层:<code>03-01</code></li><li>点击列:<code>12</code></li><li>后端查询编码:<code>03-12-01</code></li></ul>
<h3>10.4 弹窗字段</h3>
<p>当前弹窗表格已展示以下字段:</p>
<ul><li>物料编号 <code>mno</code></li><li>物料名称 <code>mname</code></li><li>图号 <code>spec</code></li><li>型号 <code>thao</code></li><li>库存数量 <code>qty</code></li><li>锁定库存 <code>lockqty</code></li><li>批次 <code>batch</code></li></ul>
<p>其中数量字段已实现:</p>
<ul><li>保留三位小数格式化</li><li>大于 0 时显示强调色</li><li>在数量下方显示单位 <code>munit</code></li></ul>
<h3>10.5 返回数据兼容处理</h3>
<p>前端对接口返回做了多种兼容判断,支持以下几类格式:</p>
<ol><li><code>response.data</code> 直接就是数组</li><li><code>response.data.data</code> 是数组</li><li><code>response.data.rows</code> 是数组</li><li><code>response.data</code> 是单对象,且对象里包含 <code>mno</code><code>pno</code></li></ol>
<p>说明:</p>
<ul><li>这意味着当前页面对后端返回结构的容错较强。</li></ul>
<h3>10.6 无数据提示</h3>
<p>如果接口无返回数据,则提示:</p>
<ul><li><code>未查询到数据</code></li></ul>
<h2>11. 后端接口与数据来源说明</h2>
<p>当前页面实际对接了两类立库相关数据。</p>
<h3>11.1 立库布局/状态数据</h3>
<p>请求方法:</p>
<ul><li><code>GetPosition(hang)</code></li></ul>
<p>请求构造:</p>
<ul><li><code>this.CreateData('11', 'GetPositionView', param)</code></li></ul>
<p>参数内容:</p>
<ul><li><code>hang</code>:巷道编号</li><li><code>potvar</code>:空字符串</li><li><code>tt</code>:固定值 <code>1</code></li></ul>
<p>调用方式:</p>
<ul><li><code>this.ExecDatabase1(Data)</code></li></ul>
<p>返回结果用途:</p>
<ul><li>写入 <code>defaultWarehouseData</code></li><li>再触发 <code>refreshData()</code></li><li>最终更新主界面矩阵</li></ul>
<h3>11.2 库位物料明细数据</h3>
<p>请求方法:</p>
<ul><li><code>StockDetail(rowLayer, column)</code></li></ul>
<p>请求方式:</p>
<ul><li>直接构造 SQL 查询 <code>StockDetailView</code></li></ul>
<p>调用方式:</p>
<ul><li><code>this.ExecDatabase1(Data)</code></li></ul>
<p>返回结果用途:</p>
<ul><li>写入 <code>materialInfoList</code></li><li>打开物料信息弹窗</li></ul>
<h3>11.3 接口地址配置</h3>
<p>立库接口使用独立配置地址:</p>
<ul><li><code>static/config.js</code></li><li><code>url1 = http://192.168.1.27:10006</code></li><li><code>request_config1 = url1 + '/submit/MESCommonBase.ashx'</code></li></ul>
<p>说明:</p>
<ul><li>这表明立库数据并未走默认 MES 主接口,而是走单独的立库数据库服务入口。</li></ul>
<h2>12. 已实现功能清单</h2>
<p>基于当前页面代码,已经实现并可确认的功能如下。</p>
<h3>12.1 页面展示类功能</h3>
<ul><li>立库矩阵可视化展示</li><li>行层与列号展示</li><li>库位状态颜色区分</li><li>文字/图标两种展示模式切换</li><li>统计卡片展示</li><li>状态图例展示</li><li>分页展示</li></ul>
<h3>12.2 数据查询类功能</h3>
<ul><li>默认加载立库数据</li><li>按巷道重新查询立库数据</li><li>点击状态为 <code>2</code> 的库位查询物料明细</li><li>点击非 <code>2</code> 状态库位查看库位状态说明</li></ul>
<h3>12.3 筛选与交互类功能</h3>
<ul><li>按行层筛选</li><li>点击搜索按钮重新按巷道加载</li><li>重置筛选条件</li><li>刷新当前缓存数据</li><li>鼠标悬停显示状态提示</li><li>单元格选中高亮</li><li>行高亮</li><li>列高亮</li></ul>
<h3>12.4 数据展示增强功能</h3>
<ul><li>数量保留 3 位小数</li><li>锁定库存单独显示</li><li>单位显示</li><li>物料列表支持多条记录显示</li><li>接口返回结构兼容处理</li></ul>
<h2>13. 代码中存在但当前未完整启用的能力</h2>
<p>以下能力在代码中已有部分实现或预留,但从当前模板和实际入口看,尚未完整开放为可见功能。</p>
<h3>13.1 状态筛选</h3>
<p>页面 <code>data</code> 中存在:</p>
<ul><li><code>statusFilter</code></li></ul>
<p>筛选逻辑中也保留了状态筛选分支,但顶部没有对应状态下拉框,因此用户当前无法直接按状态筛选。</p>
<h3>13.2 关键字搜索</h3>
<p>页面 <code>data</code> 中存在:</p>
<ul><li><code>searchKeyword</code></li></ul>
<p>并保留了按列搜索的逻辑入口,但模板中没有对应输入框,因此当前不构成实际功能。</p>
<h3>13.3 图表分析功能</h3>
<p>代码中已实现:</p>
<ul><li>状态分布饼图</li><li>行列使用率图</li><li>ECharts 初始化与销毁</li></ul>
<p>但模板中没有对应图表容器,也没有实际启用 <code>warehouse-analysis</code> 页签,因此当前属于预留代码。</p>
<h3>13.4 数据导出功能</h3>
<p>代码中已实现 <code>exportWarehouseData()</code>,可以把当前 <code>warehouseData</code> 导出为 JSON 文件。</p>
<p>但当前页面模板没有挂出“导出”按钮,因此用户在界面上无法直接使用。</p>
<h3>13.5 打印报表功能</h3>
<p>代码中存在 <code>printWarehouseReport()</code>,但当前只是提示“功能开发中”,没有实际打印逻辑。</p>
<h3>13.6 物料标签打印</h3>
<p>代码中存在 <code>printMaterialInfo()</code>,当前同样仅提示“功能开发中”。</p>
<h3>13.7 焦点定位功能</h3>
<p>代码中存在 <code>focusOnCell(rowLayer, column)</code>,可自动滚动到指定单元格位置。</p>
<p>但当前页面没有表格明细区或其他入口去调用它,因此属于预留能力。</p>
<h2>14. 现有业务规则总结</h2>
<p>从当前实现可归纳出以下业务规则。</p>
<h3>14.1 单元格状态规则</h3>
<ul><li><code>2</code> 代表该库位已有物料,应进入物料明细查询流程</li><li><code>2</code> 状态不查物料,进入库位说明流程</li><li><code>9</code> 表示库位正在存取或已锁定</li></ul>
<h3>14.2 巷道数据规则</h3>
<ul><li>页面默认在 <code>mounted</code> 时调用 <code>GetPosition(1)</code>,默认先加载第一巷道</li><li>用户再通过顶部选择器切换其他巷道</li></ul>
<h3>14.3 编码转换规则</h3>
<p>界面展示编码与后端查询编码不是同一个结构:</p>
<ul><li>界面主要使用 <code>行-层</code></li><li>后端明细查询使用 <code>行-列-层</code></li></ul>
<p>因此点击库位时必须做一次编号重组。</p>
<h2>15. 使用流程说明</h2>
<p>典型操作流程如下:</p>
<ol><li>打开立库可视化页面</li><li>系统默认加载第一巷道数据</li><li>页面显示统计信息、图例和库位矩阵</li><li>用户可切换巷道并点击“搜索”</li><li>用户可按“行-层”筛选关注区域</li><li>用户可切换文字/图标显示模式</li><li>鼠标悬停可查看库位状态提示</li><li>点击空库位/托盘位/锁定位,查看库位说明</li><li>点击有物料库位,查看该库位物料明细</li><li>需要恢复视图时可点击“重置”或“刷新数据”</li></ol>
<h2>16. 当前界面优点</h2>
<p>从现有实现看,该页面已经具备以下优点:</p>
<ul><li>视觉上能直接反映立库占用状态</li><li>巷道切换清晰</li><li>物料库位点击查询路径明确</li><li>状态颜色和图标表达直观</li><li>对后端返回结构适配较灵活</li><li>前端交互响应比较轻量</li></ul>
<h2>17. 当前界面限制</h2>
<p>从代码现状看,也存在一些限制:</p>
<ul><li>状态筛选未开放界面入口</li><li>关键字搜索未开放界面入口</li><li>导出功能未开放按钮</li><li>图表分析功能未在页面中启用</li><li>空数据时缺少明确提示文案</li><li>锁库状态没有放入图例区</li><li>库位详情弹窗信息量偏少</li><li>刷新逻辑更多是前端缓存刷新,不是强制重新拉取所有条件数据</li></ul>
<h2>18. 维护与扩展建议</h2>
<p>如果后续继续完善该页面,建议优先考虑以下方向:</p>
<ol><li>增加状态筛选下拉框,补齐 <code>statusFilter</code> 实际入口</li><li>增加库位搜索输入框,支持直接定位 <code>行-层-列</code></li><li>在界面上开放“导出 JSON”按钮</li><li>补上锁库状态图例</li><li>完善空数据占位提示</li><li>把图表分析页签正式接入模板</li><li>在库位详情弹窗中增加更多业务字段</li><li>将明细查询 SQL 封装为标准后端接口名,减少前端直接拼 SQL 的耦合</li></ol>
<h2>19. 结论</h2>
<p>当前“立库详细界面”已经不是简单静态展示,而是一个具备实际业务查询能力的立库可视化页面。它已经实现了:</p>
<ul><li>按巷道加载立库状态</li><li>立库矩阵可视化展示</li><li>库位状态识别</li><li>库位详情查看</li><li>库位物料明细查询</li><li>筛选、分页、刷新、显示模式切换等基础交互</li></ul>
<p>同时,代码中还保留了导出、图表分析、状态筛选、定位等扩展能力,说明该页面已经具备进一步升级为完整“立库监控与查询中心”的基础。</p>
</body>
</html>

View File

@@ -0,0 +1,672 @@
# JY1.0 立库详细界面功能说明书
## 1. 文档目的
本文档用于整理 JY1.0 项目中“立库查询/立库可视化”组件目前已经实现的界面结构、后端数据来源、交互逻辑和现有功能范围,便于后续培训、验收、维护和功能扩展。
本文档依据当前前端实现整理,核心代码位置如下:
- `src/views/WarehouseManagement/WarehouseVisualization/index.vue`
- `static/config.js`
## 2. 页面定位
立库详细界面当前对应的页面名称为:
- 页面组件名:`WarehouseVisualizationSystem`
- 页面标签名:`立库可视化`
- 目录位置:`src/views/WarehouseManagement/WarehouseVisualization/index.vue`
该页面用于展示立体仓库库位占用情况,并支持按巷道查看、按行层筛选、单击库位查看库位详情或物料明细。
## 3. 页面总体结构
**图 1立库可视化主界面**
![立库可视化主界面](assets/warehouse-visualization/main.png)
页面整体采用标准的 `div.app-container + el-card` 布局,主体包含以下区域:
1. 顶部筛选与操作区
2. 统计信息卡片区
3. 状态图例区
4. 立库可视化表格区
5. 分页区
6. 库位详情弹窗
7. 物料信息弹窗
当前页面只有一个主标签页:
- `立库可视化`
代码中保留了图表分析相关逻辑,但当前模板中未实际启用“分析”页签。
## 4. 顶部筛选与操作区说明
**图 2筛选区与显示模式切换示意**
![筛选区与显示模式切换](assets/warehouse-visualization/filter.png)
顶部区域已经实现以下控件:
### 4.1 巷道筛选
控件类型:`el-select`
可选项固定为:
- 第一巷道
- 第二巷道
- 第三巷道
- 第四巷道
对应值为:
- `1`
- `2`
- `3`
- `4`
用途:
- 用户选择巷道后,点击“搜索”,页面会调用立库接口重新获取该巷道数据。
### 4.2 行层筛选
控件类型:`el-select`
用途:
- 根据当前已加载的立库数据,自动生成“行-层”下拉选项。
- 用户可筛选某一行层,只显示对应行数据。
特点:
- 选项从接口返回数据动态提取。
- 会自动去重。
- 按数值倒序排列。
### 4.3 搜索按钮
按钮文字:`搜索`
作用:
- 重置当前页码为第一页。
- 先执行前端筛选。
- 再根据当前巷道值调用 `GetPosition` 获取最新立库数据。
说明:
- 当前“搜索”动作的核心是按巷道重新取数。
- 代码里存在 `searchKeyword` 变量,但页面上没有对应输入框,因此当前没有“按列关键字搜索”的实际入口。
### 4.4 重置按钮
按钮文字:`重置`
作用:
- 清空状态筛选值
- 清空行层筛选值
- 清空搜索关键字
- 页码回到第一页
- 重新执行前端筛选
页面提示:
- 执行后弹出“筛选条件已重置”。
注意:
- 当前重置不会主动重新按巷道取数,只是重置前端筛选状态。
### 4.5 刷新数据按钮
按钮文字:`刷新数据`
作用:
- 触发加载动画
- 500ms 后重新装载当前 `defaultWarehouseData`
- 更新时间戳
- 弹出“数据刷新成功”
说明:
- 刷新逻辑最终仍依赖最近一次 `GetPosition` 返回并写入的 `defaultWarehouseData`
- 如果前面已经切换过巷道,刷新展示的是当前缓存的该次结果。
### 4.6 显示模式切换开关
控件类型:`el-switch`
两种模式:
- 显示文字:`text`
- 显示图标:`icon`
作用:
- 控制库位单元格显示简称文字还是图标。
切换效果:
- 文字模式显示:`物 / 盘 / 空 / 锁`
- 图标模式显示:
- 锁库:`el-icon-lock`
- 有物料有托盘:`el-icon-box`
- 有托盘无物料:`el-icon-tickets`
- 空库位:`el-icon-minus`
页面提示:
- 切换后会提示当前已切换到文字模式或图标模式。
## 5. 统计信息卡片区说明
页面中部已实现 6 个统计卡片:
### 5.1 总库位数
计算方式:
- `立库行数 * 列数`
### 5.2 有物料有托盘
统计状态值为 `2` 的库位数量。
### 5.3 有托盘无物料
统计状态值为 `1` 的库位数量。
### 5.4 空闲库位
统计状态值为 `0` 的库位数量。
### 5.5 使用率
计算方式:
- `(有物料有托盘 + 有托盘无物料) / 总库位数 * 100%`
保留 1 位小数。
### 5.6 数据行数
显示当前 `warehouseData.length`,即当前已加载的立库行数。
## 6. 状态图例说明
页面已实现状态图例,用于帮助用户理解库位颜色含义:
- 绿色:有物料有托盘
- 橙色:有托盘无物料
- 灰色:空库位
- 蓝色边框:当前选中库位
另外,代码中还支持:
- 蓝色底:锁库(状态值 `9`
该状态在单元格样式和状态文字中已实现,但图例区未单独展示“锁库”说明。
## 7. 立库可视化表格区说明
这是页面的核心区域。
### 7.1 展示形式
页面使用原生 `table` 绘制立库矩阵,不是 `el-table`
结构包含:
- 表头列号
- 左侧行层标识
- 中间库位矩阵单元格
### 7.2 行列组织方式
#### 行
每一行对应一个“行-层”值,例如:
- `01-01`
- `03-12`
页面左侧固定显示该行层编号。
#### 列
每列按照数字列号展示,从 `1``columnCount`
`columnCount` 的计算规则为:
- 读取首行对象字段
- 排除 `行-层`
- 其余字段个数即为列数
### 7.3 单元格状态值
当前库位单元格支持以下状态:
| 状态值 | 含义 | 前端显示简称 | 颜色 |
| --- | --- | --- | --- |
| `0` | 空库位 | 空 | 灰色 |
| `1` | 有托盘无物料 | 盘 | 橙色 |
| `2` | 有物料有托盘 | 物 | 绿色 |
| `9` | 锁库/在存取 | 锁 | 蓝色 |
### 7.4 单元格交互效果
当前已实现以下交互:
- 鼠标悬停单元格时放大
- 鼠标悬停显示状态浮层
- 点击后高亮选中库位
- 点击后高亮整行对应的行层
- 点击某列后,对应列表头也会高亮
### 7.5 空数据处理
`warehouseData.length === 0` 时,页面显示空白占位区域。
说明:
- 代码里曾预留 `el-empty` 结构,但目前已注释,没有实际“暂无数据”文案和“加载示例数据”按钮。
## 8. 分页功能说明
页面底部已实现分页控件,条件为:
- `totalPages > 1` 时显示
分页能力包括:
- 查看总数
- 切换每页条数
- 上一页/下一页
- 页码跳转
- 直接输入页码跳转
每页可选条数:
- `5`
- `8`
- `10`
- `15`
说明:
- 分页针对的是“行数据”,不是单元格。
- 也就是一次显示若干个“行-层”。
## 9. 库位详情弹窗说明
**图 3库位详情弹窗示意**
![库位详情弹窗](assets/warehouse-visualization/detail.png)
点击非物料状态库位时,会弹出“库位详情”窗口。
触发条件:
- 单元格状态不等于 `2`
### 9.1 弹窗标题
格式:
- `库位详情 + 库位编号`
例如:
- `库位详情 03-12-01`
### 9.2 弹窗内容
当前弹窗内容为库位状态说明文本,即 `selectedCellDetail.description`
不同状态说明如下:
- `0`:空闲库位,可存放物料
- `1`:有托盘但无物料,需补充物料
- `2`:有托盘且有物料,正常使用中
- `9`:在存取,库位已锁
### 9.3 当前实现特点
目前该弹窗属于轻量说明弹窗,展示内容较简洁,没有进一步展示:
- 仓库编号
- 创建时间
- 最近操作人
- 任务锁定来源
这些字段在前端对象构造里有部分预留,但未在弹窗模板中完整展示。
## 10. 物料信息弹窗说明
**图 4物料信息弹窗示意**
![物料信息弹窗](assets/warehouse-visualization/material.png)
点击状态为 `2` 的库位时,会弹出“库位信息”窗口。
触发条件:
- 单元格状态等于 `2`
### 10.1 弹窗标题
格式:
- `库位信息 + pno`
即使用返回数据第一条的库位编号作为标题显示。
### 10.2 数据来源
点击单元格后会调用 `StockDetail(rowLayer, column)`,查询数据库视图:
- `StockDetailView`
查询方式为手写 SQL
- `select ... from StockDetailView where pno='xx-xx-xx'`
### 10.3 库位编号拼接规则
点击单元格时,前端会把界面上的 `行-层` 和列号转换为后端查询用的 `pno`
1. 将列号补足 2 位
2.`行-层``-` 拆分
3.`行-列-层` 重新拼装
例如:
- 页面行层:`03-01`
- 点击列:`12`
- 后端查询编码:`03-12-01`
### 10.4 弹窗字段
当前弹窗表格已展示以下字段:
- 物料编号 `mno`
- 物料名称 `mname`
- 图号 `spec`
- 型号 `thao`
- 库存数量 `qty`
- 锁定库存 `lockqty`
- 批次 `batch`
其中数量字段已实现:
- 保留三位小数格式化
- 大于 0 时显示强调色
- 在数量下方显示单位 `munit`
### 10.5 返回数据兼容处理
前端对接口返回做了多种兼容判断,支持以下几类格式:
1. `response.data` 直接就是数组
2. `response.data.data` 是数组
3. `response.data.rows` 是数组
4. `response.data` 是单对象,且对象里包含 `mno``pno`
说明:
- 这意味着当前页面对后端返回结构的容错较强。
### 10.6 无数据提示
如果接口无返回数据,则提示:
- `未查询到数据`
## 11. 后端接口与数据来源说明
当前页面实际对接了两类立库相关数据。
### 11.1 立库布局/状态数据
请求方法:
- `GetPosition(hang)`
请求构造:
- `this.CreateData('11', 'GetPositionView', param)`
参数内容:
- `hang`:巷道编号
- `potvar`:空字符串
- `tt`:固定值 `1`
调用方式:
- `this.ExecDatabase1(Data)`
返回结果用途:
- 写入 `defaultWarehouseData`
- 再触发 `refreshData()`
- 最终更新主界面矩阵
### 11.2 库位物料明细数据
请求方法:
- `StockDetail(rowLayer, column)`
请求方式:
- 直接构造 SQL 查询 `StockDetailView`
调用方式:
- `this.ExecDatabase1(Data)`
返回结果用途:
- 写入 `materialInfoList`
- 打开物料信息弹窗
### 11.3 接口地址配置
立库接口使用独立配置地址:
- `static/config.js`
- `url1 = http://192.168.1.27:10006`
- `request_config1 = url1 + '/submit/MESCommonBase.ashx'`
说明:
- 这表明立库数据并未走默认 MES 主接口,而是走单独的立库数据库服务入口。
## 12. 已实现功能清单
基于当前页面代码,已经实现并可确认的功能如下。
### 12.1 页面展示类功能
- 立库矩阵可视化展示
- 行层与列号展示
- 库位状态颜色区分
- 文字/图标两种展示模式切换
- 统计卡片展示
- 状态图例展示
- 分页展示
### 12.2 数据查询类功能
- 默认加载立库数据
- 按巷道重新查询立库数据
- 点击状态为 `2` 的库位查询物料明细
- 点击非 `2` 状态库位查看库位状态说明
### 12.3 筛选与交互类功能
- 按行层筛选
- 点击搜索按钮重新按巷道加载
- 重置筛选条件
- 刷新当前缓存数据
- 鼠标悬停显示状态提示
- 单元格选中高亮
- 行高亮
- 列高亮
### 12.4 数据展示增强功能
- 数量保留 3 位小数
- 锁定库存单独显示
- 单位显示
- 物料列表支持多条记录显示
- 接口返回结构兼容处理
## 13. 代码中存在但当前未完整启用的能力
以下能力在代码中已有部分实现或预留,但从当前模板和实际入口看,尚未完整开放为可见功能。
### 13.1 状态筛选
页面 `data` 中存在:
- `statusFilter`
筛选逻辑中也保留了状态筛选分支,但顶部没有对应状态下拉框,因此用户当前无法直接按状态筛选。
### 13.2 关键字搜索
页面 `data` 中存在:
- `searchKeyword`
并保留了按列搜索的逻辑入口,但模板中没有对应输入框,因此当前不构成实际功能。
### 13.3 图表分析功能
代码中已实现:
- 状态分布饼图
- 行列使用率图
- ECharts 初始化与销毁
但模板中没有对应图表容器,也没有实际启用 `warehouse-analysis` 页签,因此当前属于预留代码。
### 13.4 数据导出功能
代码中已实现 `exportWarehouseData()`,可以把当前 `warehouseData` 导出为 JSON 文件。
但当前页面模板没有挂出“导出”按钮,因此用户在界面上无法直接使用。
### 13.5 打印报表功能
代码中存在 `printWarehouseReport()`,但当前只是提示“功能开发中”,没有实际打印逻辑。
### 13.6 物料标签打印
代码中存在 `printMaterialInfo()`,当前同样仅提示“功能开发中”。
### 13.7 焦点定位功能
代码中存在 `focusOnCell(rowLayer, column)`,可自动滚动到指定单元格位置。
但当前页面没有表格明细区或其他入口去调用它,因此属于预留能力。
## 14. 现有业务规则总结
从当前实现可归纳出以下业务规则。
### 14.1 单元格状态规则
- `2` 代表该库位已有物料,应进入物料明细查询流程
-`2` 状态不查物料,进入库位说明流程
- `9` 表示库位正在存取或已锁定
### 14.2 巷道数据规则
- 页面默认在 `mounted` 时调用 `GetPosition(1)`,默认先加载第一巷道
- 用户再通过顶部选择器切换其他巷道
### 14.3 编码转换规则
界面展示编码与后端查询编码不是同一个结构:
- 界面主要使用 `行-层`
- 后端明细查询使用 `行-列-层`
因此点击库位时必须做一次编号重组。
## 15. 使用流程说明
典型操作流程如下:
1. 打开立库可视化页面
2. 系统默认加载第一巷道数据
3. 页面显示统计信息、图例和库位矩阵
4. 用户可切换巷道并点击“搜索”
5. 用户可按“行-层”筛选关注区域
6. 用户可切换文字/图标显示模式
7. 鼠标悬停可查看库位状态提示
8. 点击空库位/托盘位/锁定位,查看库位说明
9. 点击有物料库位,查看该库位物料明细
10. 需要恢复视图时可点击“重置”或“刷新数据”
## 16. 当前界面优点
从现有实现看,该页面已经具备以下优点:
- 视觉上能直接反映立库占用状态
- 巷道切换清晰
- 物料库位点击查询路径明确
- 状态颜色和图标表达直观
- 对后端返回结构适配较灵活
- 前端交互响应比较轻量
## 17. 当前界面限制
从代码现状看,也存在一些限制:
- 状态筛选未开放界面入口
- 关键字搜索未开放界面入口
- 导出功能未开放按钮
- 图表分析功能未在页面中启用
- 空数据时缺少明确提示文案
- 锁库状态没有放入图例区
- 库位详情弹窗信息量偏少
- 刷新逻辑更多是前端缓存刷新,不是强制重新拉取所有条件数据
## 18. 维护与扩展建议
如果后续继续完善该页面,建议优先考虑以下方向:
1. 增加状态筛选下拉框,补齐 `statusFilter` 实际入口
2. 增加库位搜索输入框,支持直接定位 `行-层-列`
3. 在界面上开放“导出 JSON”按钮
4. 补上锁库状态图例
5. 完善空数据占位提示
6. 把图表分析页签正式接入模板
7. 在库位详情弹窗中增加更多业务字段
8. 将明细查询 SQL 封装为标准后端接口名,减少前端直接拼 SQL 的耦合
## 19. 结论
当前“立库详细界面”已经不是简单静态展示,而是一个具备实际业务查询能力的立库可视化页面。它已经实现了:
- 按巷道加载立库状态
- 立库矩阵可视化展示
- 库位状态识别
- 库位详情查看
- 库位物料明细查询
- 筛选、分页、刷新、显示模式切换等基础交互
同时,代码中还保留了导出、图表分析、状态筛选、定位等扩展能力,说明该页面已经具备进一步升级为完整“立库监控与查询中心”的基础。

Binary file not shown.

View File

@@ -0,0 +1,366 @@
# JY1.0 采购件入库查询卡顿修改方案
## 1. 背景说明
仓储管理模块页面 `src/views/WarehouseManagement/PurchasePartsStorage/index.vue` 在执行查询操作时,存在页面明显卡顿、浏览器长时间无响应、严重时看起来像页面崩溃的问题。
本方案用于整理当前排查结论、明确问题成因,并提出分阶段修改建议。本文档只给出修改方案,不直接改动代码。
## 2. 问题现象
当前页面包含采购件入库、自制件入库、外协到货等多个页签,其中采购件入库页签在选择供应商或合同后会自动执行查询。
现场表现主要包括:
- 点击查询后页面长时间卡住。
- 表格数据返回后界面渲染明显变慢。
- 数据量较大时浏览器出现假死或无响应。
- 用户主观感受为“查询就崩”。
## 3. 涉及文件
- `src/views/WarehouseManagement/PurchasePartsStorage/index.vue`
- `src/main.js`
- `src/utils/request.js`
重点排查方法:
- `searchTable1()`
- `searchTable3()`
- `getSummaries()`
- `getSummaries1()`
- `handleSelectionChange()`
- `recalcSelection()`
## 4. 原因分析
### 4.1 采购件入库查询未分页,前端一次性加载全量数据
`searchTable1()` 中,查询请求通过 `CreateData('11', ..., param)` 发送,但未传入分页参数。
这会带来两个问题:
- 后端可能直接返回该供应商或合同下的全部数据。
- 前端需要一次性渲染整张大表,数据量较大时会明显拖慢页面。
相比之下,自制件入库的 `searchTable3()` 已传入 `pageSize1``pageCurrent1`,说明当前页面内部本身就存在两种不同的数据加载策略,采购件入库部分的实现明显更重。
### 4.2 查询完成后自动逐行全选,进一步放大渲染压力
`searchTable1()` 中,查询成功后会遍历 `tableData1`,并对满足条件的行逐个执行:
- `this.$refs.table.toggleRowSelection(row, true)`
虽然代码里使用了 `_suppressSelectionChange` 来避免自定义选中计算被重复触发,但 Element UI 表格内部仍需要为每一行更新选中状态、复选框状态和相关渲染结果。
当查询结果很多时,这段“逐行自动勾选”的逻辑会成为非常重的同步操作,容易造成主线程阻塞。
### 4.3 表格每行挂载多个重量级组件,导致大表渲染成本过高
采购件入库与自制件入库表格中,每行都包含较多交互组件,例如:
- `el-select`
- `el-input-number`
- 选择列
- 按钮列
其中 `el-input-number``el-select` 在大数据表格中本身就属于渲染和响应式开销较高的组件。若查询结果达到数百行甚至更多组件实例数、watcher 数量、DOM 节点数量都会迅速增大。
### 4.4 表格启用汇总行,汇总函数会对数据做多轮遍历
页面使用了:
- `show-summary`
- `summary-method`
`getSummaries()``getSummaries1()` 中对多个列执行了多次 `map + reduce` 计算。
这意味着:
- 查询后首次渲染要做汇总计算。
- 表格数据变化、选中变化、输入变化后,汇总有机会被再次触发。
- 当数据量大时,汇总计算会持续放大卡顿感。
### 4.5 查询动作与下拉联动查询存在叠加请求
例如自制件入库页签中,订单切换时会同时触发:
- `searchTable3()`
- `getnumberSelection()`
这类逻辑虽然不是采购件入库“卡死”的唯一主因,但会增加同一时间段内的请求数和页面状态更新次数,让用户更容易感知到页面迟滞。
### 4.6 当前超时配置较大,不容易快速暴露性能问题
`src/utils/request.js` 中 Axios `timeout` 设置为 `1500000`
这会导致:
- 如果接口慢,页面会长时间停留在等待状态。
- 如果前端渲染本身很重,用户更容易把“卡顿”感知为“页面崩溃”。
这不是根因,但会放大问题表现。
## 5. 结论判断
综合代码结构,当前“查询就卡页面崩溃”的高概率原因不是单一点故障,而是以下因素叠加:
1. 采购件入库查询无分页。
2. 查询完成后自动逐行勾选可入库数据。
3. 大表格中每行挂载多个重量级输入组件。
4. 汇总行对全量数据重复做统计计算。
其中,优先级最高、最值得首先处理的是前两项。
## 6. 修改目标
本次优化建议以“先止血,再优化体验”为原则,目标如下:
- 将查询后的页面卡顿控制在可接受范围内。
- 避免浏览器因大数据量渲染出现假死。
- 保持现有业务流程基本不变。
- 尽量复用项目现有分页、查询、表格处理模式。
- 将改动范围控制在 `PurchasePartsStorage` 页面内部,避免影响其他模块。
## 7. 修改方案
### 7.1 方案一:为采购件入库查询增加分页
这是首要方案。
#### 修改思路
- 为采购件入库表格引入与自制件入库一致的分页机制。
-`searchTable1()` 中调用 `CreateData()` 时补充分页参数。
- 页面新增采购件入库对应的分页组件与分页状态。
- 后端查询过程若已支持分页,则直接接入。
- 后端若尚未支持分页,需要同步补充分页查询能力。
#### 预期收益
- 避免一次性返回几百或几千条数据。
- 大幅降低首屏渲染压力。
- 为后续汇总优化、选择优化提供基础。
#### 风险点
- 需要确认后端 `仓储管理_采购入库合同明细_查询` 是否支持分页返回 `rows/total`
- 如果当前接口只返回普通数组,前后端都需要调整返回结构。
### 7.2 方案二:取消查询后自动全选所有可入库行
这是与分页并列的高优先级方案。
#### 修改思路
- 查询完成后不再自动执行 `toggleRowSelection` 遍历全表。
- 保留手工勾选模式,由用户自主选择入库数据。
- 如业务上确实需要批量选择,可增加单独按钮,例如“勾选当前页可入库项”。
- 如仍需保留自动选择能力,建议限制为只处理当前页数据,而不是全量结果。
#### 预期收益
- 避免一次查询后产生大量同步 UI 更新。
- 显著降低 Element 表格内部状态维护成本。
- 使查询动作本身更纯粹,减少联动副作用。
#### 风险点
- 用户操作习惯会发生轻微变化。
- 若一线人员依赖默认全选,需要补充一个显式批量勾选按钮作为替代。
### 7.3 方案三:降低表格每行组件数量
这是第二阶段优化方案。
#### 修改思路
- 默认以文本方式展示数值和货位。
- 仅在用户点击某行或某单元格后切换为可编辑组件。
- 或将数量、货位编辑能力迁移到弹窗/侧边表单中进行。
#### 可选实现方式
- 方式 A保留表格编辑但只对当前编辑行渲染 `el-input-number``el-select`
- 方式 B表格只展示编辑统一在“设置货位/修改本次到货数”弹窗内完成。
#### 预期收益
- 大幅减少页面初次渲染和更新渲染的组件实例数。
- 降低响应式系统负担。
#### 风险点
- 交互形式改变较大,需业务确认。
- 改动量大于分页和取消自动全选。
### 7.4 方案四:优化汇总逻辑
这是辅助优化方案。
#### 修改思路
- 汇总只统计当前页数据,不统计全量数据。
- 或在查询成功后一次性计算并缓存汇总结果,避免在渲染阶段多次 `map + reduce`
- 对不必要展示汇总的列取消统计。
#### 预期收益
- 降低表格重渲染时的计算开销。
- 对大页数据场景效果明显。
#### 风险点
- 需确认业务是否要求“全量合计”还是“当前页合计”。
### 7.5 方案五:收敛筛选联动查询,减少重复请求
这是体验优化方案。
#### 修改思路
- 将“切换筛选项即自动查询”改为“选择条件后点击查询按钮再执行”。
- 对必须联动的下拉,仅查询下一级选项,不立即刷新大表。
- 对输入型查询条件增加节流或按回车触发。
#### 预期收益
- 避免用户频繁切换条件时重复打接口和重复渲染。
- 页面行为更可控。
#### 风险点
- 与当前“选择即查”的使用习惯不完全一致。
## 8. 推荐实施顺序
建议按以下顺序推进:
### 第一阶段:快速止血
目标是在不大改页面交互的前提下,尽快解决“查询卡死”。
建议执行:
1. 采购件入库增加分页。
2. 取消查询后自动全选。
3. 采购件入库增加显式“勾选当前页可入库项”按钮,作为自动全选替代。
### 第二阶段:性能优化
目标是继续提升大数据场景下的流畅度。
建议执行:
1. 优化汇总函数。
2. 减少表格内 `el-input-number``el-select` 的常驻渲染数量。
3. 调整筛选联动逻辑,减少重复请求。
### 第三阶段:体验收敛
目标是统一仓储大表页面的实现模式。
建议执行:
1. 对采购件入库、自制件入库、外协到货的查询行为做统一规范。
2. 明确哪些页签必须分页、哪些页签允许全量查。
3. 统一表格汇总、批量勾选、行编辑的实现方式。
## 9. 建议的具体改动点
以下为建议修改点,不代表本次已经改动:
### 9.1 页面状态
`PurchasePartsStorage/index.vue` 中补充采购件入库分页状态,例如:
- `pageCurrentRK`
- `pageSizeRK`
- `totalRK`
或直接复用现有命名体系,保持与页面其他分区一致。
### 9.2 查询方法
重点调整:
- `searchTable1()`
建议:
- 查询请求增加分页参数。
- 查询结果改为接收分页结构。
- 删除查询成功后的逐行 `toggleRowSelection` 自动勾选逻辑。
### 9.3 表格区域
采购件入库表格下方增加分页组件,行为对齐自制件入库分页实现。
### 9.4 批量选择逻辑
新增显式按钮:
- “勾选当前页可入库”
- “取消当前页勾选”
这样既能保留批量能力,也能避免查询完成后立即执行重计算。
### 9.5 汇总逻辑
重构:
- `getSummaries()`
- `getSummaries1()`
建议尽量减少重复遍历次数,并明确统计口径为“当前页合计”。
## 10. 验证方案
修改完成后建议按以下方式验证:
### 10.1 功能验证
- 选择供应商后能正常查询采购件入库数据。
- 切换合同后能正常刷新结果。
- 分页切换后数据正确。
- 手工勾选、批量勾选后入库功能正常。
- 货位选择、数量输入、打印功能不受影响。
### 10.2 性能验证
- 查询 50 条、100 条、300 条时页面响应是否明显改善。
- 查询后页面是否仍出现长时间卡死。
- 批量勾选当前页时是否可接受。
- 输入数量、选择货位时是否仍然卡顿。
### 10.3 回归验证
- 自制件入库页签行为不受影响。
- 外协到货页签行为不受影响。
- 入库单弹窗显示和合并逻辑不受影响。
## 11. 风险与注意事项
- 若后端接口当前不支持分页,本次优化需要前后端联动处理。
- 若业务习惯依赖“查询即默认勾选全部可入库项”,需要提前通知用户交互变更。
- 若汇总口径从全量改为当前页,需业务确认是否接受。
- 若后续计划继续扩展该页面,建议优先统一仓储大表的分页和编辑规范,避免重复出现同类问题。
## 12. 最终建议
推荐采用“先小改止血,再做结构优化”的策略:
1. 先做采购件入库分页。
2. 同时取消查询后的自动逐行全选。
3. 再根据现场体验决定是否继续推进表格轻量化和汇总优化。
若只能先做一项,优先级最高的是:
- `searchTable1()` 增加分页能力。
若可以同时做两项,最佳组合是:
- 采购件入库分页。
- 取消查询后自动勾选。
这两项组合最有希望直接解决当前“查询就卡页面崩溃”的核心问题。

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

22
docs/preview-server.js Normal file
View File

@@ -0,0 +1,22 @@
const http = require('http');
const fs = require('fs');
const path = require('path');
const root = __dirname;
const types = { '.html': 'text/html; charset=utf-8', '.png': 'image/png', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.md': 'text/plain; charset=utf-8' };
const server = http.createServer((req, res) => {
try {
const url = new URL(req.url, 'http://127.0.0.1:18765');
const rel = decodeURIComponent(url.pathname).replace(/^\/+/, '');
const filePath = path.resolve(root, rel);
if (!filePath.startsWith(path.resolve(root))) {
res.writeHead(403); res.end('Forbidden'); return;
}
fs.readFile(filePath, (err, data) => {
if (err) { res.writeHead(404); res.end('Not found: ' + filePath); return; }
res.writeHead(200, { 'Content-Type': types[path.extname(filePath).toLowerCase()] || 'application/octet-stream' });
res.end(data);
});
} catch (e) { res.writeHead(500); res.end(String(e)); }
});
server.listen(18765, '127.0.0.1', () => console.log('preview server on 18765'));
setInterval(() => {}, 1000);

View File

@@ -0,0 +1,482 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>立库可视化功能截图预览</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
font-family: "Microsoft YaHei", Arial, sans-serif;
background: #eef2f7;
color: #303133;
}
.app-container { padding: 20px; }
.card {
height: 850px;
background: #fff;
border: 1px solid #e4e7ed;
box-shadow: 0 2px 12px rgba(0,0,0,.06);
}
.tab {
height: 44px;
border-bottom: 1px solid #dcdfe6;
background: #f5f7fa;
display: flex;
align-items: end;
padding-left: 0;
}
.tab span {
display: inline-flex;
align-items: center;
height: 44px;
padding: 0 22px;
background: #fff;
border-right: 1px solid #dcdfe6;
border-top: 2px solid #409eff;
font-size: 14px;
color: #409eff;
}
.toolbar {
margin: 7px 0;
padding: 0 12px;
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
height: 42px;
}
.toolbar label { font-size: 13px; color: #000; }
.select, .button, .switch {
height: 32px;
border-radius: 4px;
font-size: 13px;
display: inline-flex;
align-items: center;
}
.select {
width: 120px;
border: 1px solid #dcdfe6;
padding: 0 10px;
justify-content: space-between;
color: #606266;
background: #fff;
}
.select::after { content: "⌄"; color: #909399; }
.button {
border: 1px solid transparent;
padding: 0 12px;
color: #fff;
gap: 4px;
}
.primary { background: #409eff; }
.default { background: #fff; color: #606266; border-color: #dcdfe6; }
.success { background: #67c23a; }
.switch {
margin-left: 10px;
gap: 8px;
color: #606266;
}
.switch-track {
width: 42px;
height: 22px;
border-radius: 12px;
background: #409eff;
position: relative;
}
.switch-track::after {
content: "";
position: absolute;
right: 2px;
top: 2px;
width: 18px;
height: 18px;
border-radius: 50%;
background: #fff;
}
.stats {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 10px;
margin: 10px 12px;
}
.stat {
border: 1px solid #ebeef5;
border-radius: 4px;
text-align: center;
padding: 10px 0;
box-shadow: 0 2px 8px rgba(0,0,0,.04);
}
.stat .label { color: #909399; font-size: 12px; }
.stat .value { font-size: 20px; font-weight: bold; margin-top: 4px; }
.legend {
margin-bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
flex-wrap: wrap;
}
.legend-item {
display: flex;
align-items: center;
font-size: 12px;
}
.legend-color {
width: 20px;
height: 20px;
border-radius: 4px;
border: 1px solid #ddd;
margin-right: 5px;
}
.warehouse-area {
height: 600px;
overflow: auto;
border: 1px solid #e4e7ed;
border-radius: 4px;
margin: 0 12px;
padding: 15px;
background: #f5f7fa;
}
.warehouse-group {
margin-bottom: 30px;
padding: 15px;
background: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0,0,0,.1);
}
.group-title {
text-align: center;
margin-bottom: 10px;
color: #303133;
font-weight: bold;
background: #e8f4fd;
padding: 5px;
border-radius: 4px;
}
table {
width: 100%;
min-width: 1200px;
border-collapse: collapse;
margin: 0 auto;
table-layout: fixed;
}
th, td {
border: 1px solid #e4e7ed;
text-align: center;
height: 40px;
min-width: 40px;
font-size: 12px;
position: relative;
}
th {
background: #f5f7fa;
font-weight: bold;
color: #303133;
}
.row-layer-header, .row-layer-cell {
width: 80px;
min-width: 80px;
background: #e8f4fd;
color: #2196f3;
font-weight: bold;
}
.row-layer-cell { background: #f5f7fa; color: #303133; }
.highlight-row .row-layer-cell {
background: #bbdefb;
color: #1976d2;
}
.highlight-col { background: #bbdefb; color: #1976d2; }
.cell {
cursor: pointer;
font-size: 14px;
font-weight: bold;
transition: all .2s;
}
.cell:hover {
transform: scale(1.08);
z-index: 2;
box-shadow: 0 2px 8px rgba(0,0,0,.2);
}
.status-2 { background: #4caf50; color: #fff; }
.status-1 { background: #ff9800; color: #fff; }
.status-0 { background: #e0e0e0; color: #666; }
.status-9 { background: skyblue; color: #fff; }
.selected {
box-shadow: 0 0 0 2px #2196f3 inset;
outline: 2px solid #2196f3;
outline-offset: -2px;
}
.tooltip {
position: absolute;
bottom: calc(100% + 6px);
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,.82);
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
z-index: 4;
font-weight: normal;
}
.pagination {
text-align: center;
margin-top: 10px;
color: #606266;
font-size: 13px;
}
.page-box {
display: inline-flex;
align-items: center;
gap: 8px;
border: 1px solid #ebeef5;
padding: 6px 10px;
border-radius: 4px;
background: #fff;
}
.page-num {
min-width: 28px;
height: 28px;
border-radius: 3px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.page-current { background: #409eff; color: #fff; }
.modal-backdrop {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,.2);
align-items: center;
justify-content: center;
z-index: 10;
}
body.show-detail #detailModal,
body.show-material #materialModal { display: flex; }
.dialog {
background: #fff;
border-radius: 2px;
box-shadow: 0 4px 18px rgba(0,0,0,.18);
overflow: hidden;
}
.detail-dialog { width: 500px; }
.material-dialog { width: 941px; }
.dialog-title {
height: 54px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
border-bottom: 1px solid #ebeef5;
}
.dialog-body { padding: 20px; }
.detail-box {
padding: 10px;
background: #f5f7fa;
border-radius: 4px;
line-height: 1.7;
}
.dialog-footer {
border-top: 1px solid #ebeef5;
padding: 10px 20px;
text-align: right;
}
.close-button {
height: 32px;
padding: 0 15px;
border: 1px solid #dcdfe6;
background: #fff;
border-radius: 4px;
color: #606266;
}
.material-table {
min-width: 0;
table-layout: auto;
font-size: 13px;
}
.material-table th {
color: #606266;
height: 38px;
background: #f5f7fa;
}
.material-table td { height: 46px; }
.qty { color: #67c23a; font-weight: bold; }
.lockqty { color: #e6a23c; font-weight: bold; }
.unit { font-size: 10px; color: #909399; }
.mode-icon .cell { font-size: 18px; }
.mode-icon .text-symbol { display: none; }
.mode-icon .icon-symbol { display: inline; }
.icon-symbol { display: none; }
.callout {
position: absolute;
right: 30px;
top: 105px;
background: #fffbe6;
border: 1px solid #ffe58f;
color: #6b4c00;
padding: 8px 12px;
border-radius: 4px;
font-size: 13px;
z-index: 5;
display: none;
}
body.show-filter-tip .callout { display: block; }
</style>
</head>
<body>
<div class="app-container">
<div class="card">
<div class="tab"><span>立库可视化</span></div>
<div class="toolbar">
<label>库位筛选</label>
<div class="select" id="aisleSelect">第一巷道</div>
<div class="select" id="rowSelect">03-01</div>
<div class="button primary">🔍 搜索</div>
<div class="button default">↻ 重置</div>
<div class="button success">⟲ 刷新数据</div>
<div class="switch"><span id="modeText">显示文字</span><span class="switch-track"></span></div>
</div>
<div class="callout">筛选区支持按巷道重新取数、按行-层收窄视图,并可切换文字/图标显示模式。</div>
<div class="stats">
<div class="stat"><div class="label">总库位数</div><div class="value">384</div></div>
<div class="stat"><div class="label">有物料有托盘</div><div class="value" style="color:#4caf50">138</div></div>
<div class="stat"><div class="label">有托盘无物料</div><div class="value" style="color:#ff9800">76</div></div>
<div class="stat"><div class="label">空闲库位</div><div class="value" style="color:#9e9e9e">154</div></div>
<div class="stat"><div class="label">使用率</div><div class="value" style="color:#2196f3">55.7%</div></div>
<div class="stat"><div class="label">数据行数</div><div class="value" style="color:#9c27b0">8</div></div>
</div>
<div class="legend">
<div class="legend-item"><div class="legend-color" style="background:#4caf50"></div>有物料有托盘 (物)</div>
<div class="legend-item"><div class="legend-color" style="background:#ff9800"></div>有托盘无物料 (盘)</div>
<div class="legend-item"><div class="legend-color" style="background:#e0e0e0"></div>空库位 (空)</div>
<div class="legend-item"><div class="legend-color" style="background:#bbdefb;border:2px solid #2196f3"></div>选中库位</div>
</div>
<div class="warehouse-area">
<div class="warehouse-group">
<div class="group-title">立库组</div>
<table id="warehouseTable">
<thead>
<tr id="headRow"><th class="row-layer-header">行-层</th></tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
<div style="text-align:center;margin-top:20px;color:#909399;font-size:12px">显示 8 行数据</div>
</div>
<div class="pagination">
<div class="page-box">
<span>共 8 条</span>
<span>8条/页</span>
<span></span>
<span class="page-num page-current">1</span>
<span></span>
<span>前往 1 页</span>
</div>
</div>
</div>
</div>
<div class="modal-backdrop" id="detailModal">
<div class="dialog detail-dialog">
<div class="dialog-title">库位详情 03-08-01</div>
<div class="dialog-body">
<div class="detail-box">有托盘但无物料,需补充物料。当前库位已被识别为托盘占用状态,适合仓储人员核对托盘与物料绑定情况。</div>
</div>
<div class="dialog-footer"><button class="close-button">关闭</button></div>
</div>
</div>
<div class="modal-backdrop" id="materialModal">
<div class="dialog material-dialog">
<div class="dialog-title">库位信息 03-12-01</div>
<div class="dialog-body">
<table class="material-table">
<thead>
<tr>
<th>物料编号</th>
<th>物料名称</th>
<th>图号</th>
<th>型号</th>
<th>库存数量</th>
<th>锁定库存</th>
<th>批次</th>
</tr>
</thead>
<tbody>
<tr>
<td>02.009.001.0012</td>
<td>直线导轨组件</td>
<td>LRS3056-0312-01</td>
<td>HGW25CA</td>
<td><div class="qty">14.000</div><div class="unit"></div></td>
<td><div class="lockqty">2.000</div><div class="unit"></div></td>
<td>BATCH031201</td>
</tr>
<tr>
<td>02.009.001.0013</td>
<td>滑块组件</td>
<td>LRS3056-0312-02</td>
<td>HGW25CC</td>
<td><div class="qty">6.000</div><div class="unit"></div></td>
<td><div style="color:#909399">0.000</div><div class="unit"></div></td>
<td>BATCH031202</td>
</tr>
</tbody>
</table>
</div>
<div class="dialog-footer"><button class="close-button">关闭</button></div>
</div>
</div>
<script>
const columns = 48;
const rows = ['04-04','04-03','04-02','04-01','03-04','03-03','03-02','03-01'];
const statuses = [2,0,1,2,0,0,2,1,0,2,1,2,0,9,2,0,1,0,2,2,0,1,0,2,0,0,1,2,0,2,0,1,2,0,0,9,2,1,0,2,0,1,2,0,0,2,1,0];
const head = document.getElementById('headRow');
for (let c = 1; c <= columns; c++) {
const th = document.createElement('th');
th.textContent = c;
if (c === 12) th.className = 'highlight-col';
head.appendChild(th);
}
const tbody = document.getElementById('tableBody');
rows.forEach((row, rIndex) => {
const tr = document.createElement('tr');
if (row === '03-01') tr.className = 'highlight-row';
const rowCell = document.createElement('td');
rowCell.className = 'row-layer-cell';
rowCell.textContent = row;
tr.appendChild(rowCell);
for (let c = 1; c <= columns; c++) {
const status = statuses[(c + rIndex * 5) % statuses.length];
const td = document.createElement('td');
td.className = 'cell status-' + status;
if (row === '03-01' && c === 12) td.className += ' selected';
const text = status === 2 ? '物' : status === 1 ? '盘' : status === 9 ? '锁' : '空';
const icon = status === 2 ? '▣' : status === 1 ? '▤' : status === 9 ? '🔒' : '';
td.innerHTML = '<span class="text-symbol">' + text + '</span><span class="icon-symbol">' + icon + '</span>';
if (row === '03-01' && c === 12) {
const tip = document.createElement('span');
tip.className = 'tooltip';
tip.textContent = '有物料有托盘';
td.appendChild(tip);
}
tr.appendChild(td);
}
tbody.appendChild(tr);
});
const params = new URLSearchParams(location.search);
const shot = params.get('shot');
if (shot === 'filter') {
document.body.classList.add('show-filter-tip');
document.body.classList.add('mode-icon');
document.getElementById('modeText').textContent = '显示图标';
document.getElementById('aisleSelect').textContent = '第三巷道';
document.getElementById('rowSelect').textContent = '03-01';
}
if (shot === 'detail') document.body.classList.add('show-detail');
if (shot === 'material') document.body.classList.add('show-material');
</script>
</body>
</html>