chore: upload dashboard updates

This commit is contained in:
2026-07-08 13:36:22 +08:00
parent 4e4a419816
commit 4eef87d9e5
8 changed files with 552 additions and 393 deletions

BIN
dist.zip

Binary file not shown.

BIN
doc/~$未完成计划.xlsx Normal file

Binary file not shown.

BIN
doc/未完成计划.xlsx Normal file

Binary file not shown.

View File

@@ -391,3 +391,237 @@
### 结论 ### 结论
所有当前修改已提交并推送到远程 `origin/main`,主要提交为 `41af5a7 feat: add warehouse dashboard`。本次补充日志也将一并提交并推送,保持本地与远程一致。 所有当前修改已提交并推送到远程 `origin/main`,主要提交为 `41af5a7 feat: add warehouse dashboard`。本次补充日志也将一并提交并推送,保持本地与远程一致。
## 2026-07-06 仓库任务大屏按客户 Excel 调整
### 提问
用户要求:继续修改仓库任务大屏,参考 `C:\Users\meswork764\Documents\xwechat_files\wxid_3113011130614_d858\msg\file\2026-07\库房大屏幕260704.xlsx`,客户提出数据源需要调整;当前四个图表占两行较松,表格应为四个明细表;上面的汇总数不允许有省略号。随后用户补充:条数放上面,件数放下面,大屏条数比件数有用,没必要大调整排版,数据能显示出来即可。
### 执行过程
1. 检查 Git 状态,确认当前分支 `main` 与远程一致,工作区初始干净。
2. 确认客户 Excel 文件存在,解析 xlsx 内部 XML读取工作表 `布局说明`
3. 从 Excel 中提取新布局和新数据口径:入库任务、加急入库列表、拣配任务列表、加急拣配列表、发货任务列表,以及右侧数据源和取值逻辑。
4. 核对 Excel 中数据源:入库汇总仍使用 `UBT_MES_DP_receipttask_101/103/105/106`;加急入库列表使用四个 `UBT_MES_DP_receipttask_material_*` 明细视图按加急合并;拣配任务列表使用未清拣配明细;加急拣配列表使用两类拣配明细按加急合并;发货汇总使用 `UBT_MES_DP_OpenODLN_material` 和今日发货视图;异常出库使用 `UBT_MES_DP_OpenOPKL_material_10Days`
5. 查询数据库字段结构,确认四个入库明细视图包含 `external_num/item_name/open_quantity/加急数量`,拣配明细视图包含 `项目/生产订单号/物料名称/齐套状态/打印状态/加急状态`,发货相关已有过程包含 `合同号/物料描述/交货数量/要求发货日期/检验数量`
6. 更新数据库包装过程 `仓库大屏_汇总查询`,按用户补充要求将 `任务数` 作为条数主指标,将 `数量` 作为件数辅助指标。入库件数改从入库明细 `open_quantity` 统计,拣配件数改从拣配明细 `计划数量` 统计,及时处理和发货分别保留条数与件数。
7. 新增或更新只读包装过程 `仓库大屏_加急入库列表``仓库大屏_拣配任务列表``仓库大屏_加急拣配列表`,分别返回客户表格需要的字段。
8. 修改 `src/views/WarehouseDashboard.vue`:顶部 8 个汇总卡主数字全部改为条数,副文本显示件数和加急件数。
9. 修改 `src/views/WarehouseDashboard.vue`:四个图表压缩为一行展示,减少图表区高度,整体不做大幅重构。
10. 修改 `src/views/WarehouseDashboard.vue`:底部明细由 3 张表调整为 4 张表,分别为加急入库列表、拣配任务列表、加急拣配列表、发货任务列表。
11. 修改 `src/views/WarehouseDashboard.vue`:加急入库、拣配任务、加急拣配、发货任务分别接入新过程或现有发货过程,并按客户给出的字段展示。
12. 修改顶部 KPI 样式,去掉主数字的 `overflow hidden``max-width``text-overflow: ellipsis`,确保汇总数不显示省略号。
13. 调整表格和图表高度、字号、行高,四个明细表每表可见 8 行,保持原大屏风格。
14. 执行数据库过程样例验证,确认 `仓库大屏_汇总查询``仓库大屏_加急入库列表``仓库大屏_拣配任务列表``仓库大屏_加急拣配列表``装配中心任务看板_发货状态查询` 均能返回数据。
15. 执行 `npm run build`,构建成功,仅保留既有 Sass legacy JS API 提示和 Vite chunk size 警告。
### 结论
已按客户 Excel 和补充说明调整仓库任务大屏:汇总卡改为条数在上、件数在下;顶部汇总数字不再省略;四个图表压缩到一行;底部改为四个明细表并接入新数据源。构建验证通过。
## 2026-07-06 仓库任务大屏数据为 0 和布局空白修复
### 提问
用户反馈:仓库任务大屏顶部和图表数据都没了,下面太空;要求在四个图表下面再加四个图表。随后用户补充:八个图表两行加上列表三行高度都一样。
### 执行过程
1. 根据用户截图判断:明细表有数据,但顶部 KPI 和图表全为 0问题集中在汇总接口或前端汇总数据处理。
2. 直接执行数据库过程 `仓库大屏_汇总查询`,发现执行约 8.3 秒,超过前端 Axios 原 5 秒超时,导致前端查询失败后把汇总数据置空,因此顶部和图表显示 0。
3. 尝试使用 `OPENQUERY` 优化 SAP 远程聚合,单类测试约 1.8 秒;随后确认完整聚合仍可能受 SAP 链接查询波动影响。
4.`仓库大屏_汇总查询` 改回较快的直接汇总口径,优先保证 `任务数` 条数能快速返回,件数保留为辅助数量;复测完整过程约 4.7 秒。
5. 修改 `src/utils/request.js`,将 Axios 超时时间从 5 秒调整为 15 秒,避免 SAP 链接偶发慢一点时前端清空汇总。
6. 修改 `src/views/WarehouseDashboard.vue`,增加第二排 4 个图表,合计 8 个图表:
- 入库任务结构
- 拣配任务结构
- 超期风险分布
- 今日发货保障
- 入库条数结构
- 拣配条数结构
- 及时处理件数
- 发货件数结构
7. 为新增 4 个图表增加 ECharts ref、实例初始化、渲染、resize 和 dispose 管理。
8. 新增通用单柱状图配置函数,复用现有汇总数据,不新增额外接口压力。
9. 按用户要求将内容区域设置为三行等高:前两行为 8 个图表,第三行为 4 个列表;顶部汇总卡单独保留。
10. 将明细表可见行数调为 5 行,真实数据超过可见行数时继续自动滚动,避免列表区域显得过空。
11. 给汇总数据应用函数增加兜底:如果汇总接口偶发返回空数组,不覆盖已有汇总数据,避免刷新后突然全 0。
12. 执行 `npm run build`,构建成功;仅保留既有 Sass legacy JS API 提示和 Vite chunk size 警告。
### 结论
已修复汇总数据因接口超时导致全为 0 的问题,并将大屏主体调整为三行等高:两行 8 个图表加一行 4 个列表。构建验证通过。
## 2026-07-06 仓库任务大屏字号放大
### 提问
用户要求:因为是看板,需要把字号调大。
### 执行过程
1. 保持现有三行等高布局和数据逻辑不变,只调整视觉字号。
2. 放大顶部 KPI 卡片:标题从 19px 调整为 22px主数字从 39px 调整为 48px单位从 18px 调整为 22px副文本从 14px 调整为 16px。
3. 放大面板标题:图表和表格标题调整为 28px并保持 38px 标题栏高度。
4. 放大表格文字:表头从 13px 调整为 16px表体从 12px 调整为 15px行高同步增加。
5. 放大 ECharts 图表文字tooltip、图例、坐标轴标签、柱状图标签、饼图标签等统一上调字号。
6. 执行 `npm run build`,构建成功;仍仅有既有 Sass legacy JS API 提示和 Vite chunk size 警告。
### 结论
已放大仓库任务大屏的 KPI、图表和表格字号更适合大屏看板远距离查看。构建验证通过。
## 2026-07-06 仓库任务大屏列表右侧遮挡修复
### 提问
用户反馈:四个列表右边被挡住了,没有完全显示出来。
### 执行过程
1. 根据截图判断,四个列表面板为四等分布局,但表格列宽仍使用固定像素,列宽总和超过单个面板宽度,导致右侧列被裁切。
2. 将四个列表的列宽从固定 `px` 改为百分比宽度,确保每张表总宽度不超过所在面板。
3. 拣配任务列表去掉非必要的 `状态` 列,保留客户要求的 `合同号/生产订单/物料名称/齐套`,减少横向挤压。
4. 调整表格容器左右内边距,从 8px 收紧到 6px。
5. 表格单元格增加 `overflow: hidden``text-overflow: ellipsis`,避免长文本把右侧列挤出面板。
6. 表格字号略收紧到表头 15px、表体 14px保证右侧列完整显示。
7. 执行 `npm run build`,构建成功;仍仅有既有 Sass legacy JS API 提示和 Vite chunk size 警告。
### 结论
已修复四个列表右侧列被挡住的问题。四个列表现在使用百分比列宽,右侧字段可以完整落在面板内。构建验证通过。
## 2026-07-06 仓库任务大屏图表合并与网格线去除
### 提问
用户要求去掉第三行的图表第一行的数量放入到对应第二行的图表里面去掉图表里面的横线Y 轴),数量显示在柱状图上面。
### 执行过程
1. 定位 `src/views/WarehouseDashboard.vue` 中的 8 个图表模板和对应 ECharts 实例。
2. 删除第二排独立数量/件数图表,只保留入库任务、拣配任务、及时处理、发货任务 4 个图表。
3. 清理第二排 4 个图表对应的 ref、实例变量、初始化、渲染、resize 和 dispose 逻辑。
4. 将入库和拣配图表改为双柱展示:条数和件数在同一图表内显示。
5. 将及时处理和发货任务图表从原先独立图表/饼图样式统一改为条数与件数双柱图。
6. 修改通用柱状图配置,隐藏 Y 轴轴线、刻度、标签和横向网格线。
7. 给柱状图两个系列增加顶部标签,直接在柱子上方显示数值。
8. 将主体布局改回顶部汇总、4 个图表、4 个列表三段结构。
9. 执行 `npm run build`,构建成功;仍仅有既有 Sass legacy JS API 提示和 Vite chunk size 警告。
### 结论
已去掉第三行图表,将数量/件数合并到对应的 4 个主图表中;图表横线和 Y 轴视觉元素已隐藏,数值直接显示在柱状图上方。构建验证通过。
## 2026-07-06 仓库任务大屏汇总并入图表与加急口径修正
### 提问
用户反馈:当前图表数据不对,加急和任务一样多;要求把上边汇总放到对应图表里,基本取消第一行汇总区域。
### 执行过程
1. 检查 `src/views/WarehouseDashboard.vue` 当前实现,确认上一轮把图表第二组数据接成了 `quantity` 件数,导致图例虽显示第二组柱子,但并不是加急口径。
2. 删除模板中的顶部 `kpi-row` 汇总卡片区域,不再单独占用第一行。
3. 在四个主图表面板内部新增汇总头部,分别显示入库任务、拣配任务、及时处理、发货任务的总条数,并在下方显示件数和加急数量。
4. 删除不再使用的 `summaryCards` 计算逻辑,新增 `chartSummaries` 计算对象和 `formatChartSummary` 格式化函数。
5. 将内容主体布局从“汇总行 + 图表行 + 列表行”调整为“两行”:第一行为四个图表,第二行为四个列表。
6. 将四个列表可见行数从 5 行调整为 8 行,减少取消顶部汇总行之后列表区域显得过空的问题。
7. 修正四个柱状图的数据口径:蓝色柱为 `taskCount` 任务,红色柱为 `urgentQty` 加急,不再使用 `quantity` 件数作为红色柱。
8. 保留图表内隐藏 Y 轴、隐藏横向网格线、柱顶显示数值的配置。
9. 执行静态搜索,确认旧的顶部卡片类名和 `quantity` 红柱绑定已清理。
10. 执行 `npm run build`,构建成功;仍仅有既有 Sass legacy JS API 提示和 Vite chunk size 警告。
11. 检查本地 5173 端口服务仍在监听,并通过 HTTP 请求确认页面入口返回 200。
### 结论
已将顶部汇总并入四个图表面板,页面主体不再有独立第一行汇总卡片;柱状图已改回“任务 / 加急”口径,避免加急数据误用件数导致看起来与任务一样多。构建验证通过,本地页面入口可访问。
## 2026-07-06 仓库任务大屏去除件数与异常出库合并
### 提问
用户要求:
1. 四个图表柱状图不要数量(件数),去掉所有件数,只要条数;加急的数量要求是红色。
2. 异常出库的数据跟拣配任务一起展示。
3. 加大字号显示。
### 执行过程
1. 读取 `src/views/WarehouseDashboard.vue`,确认当前图表头部仍显示“件数 / 加急”,柱状图已显示“任务 / 加急”,但数据库过程里部分 `加急数量` 仍为件数累加。
2. 修改四个图表面板头部摘要:去掉所有“件”相关展示,只保留总条数和“加急 X 条”。
3. 修改 `formatChartSummary`,返回总条数和加急条数,不再返回件数文本。
4. 修改拣配图表:分类从“机加 / 装配”扩展为“机加 / 装配 / 异常出库”,其中异常出库从发货环节的“异常出库”汇总行读取。
5. 修改发货图表:分类只保留“发货任务 / 今日”,不再单独展示异常出库。
6. 修改拣配图表汇总数:拣配总条数和加急条数合并异常出库数据,保证面板汇总与图表分类一致。
7. 放大 ECharts 字号tooltip、图例、X 轴、柱顶标签均上调字号,柱宽也略加宽。
8. 将加急柱顶标签颜色改为红色 `#ff3345`,加急汇总数字也设置为红色。
9. 放大页面字号:图表标题、图表主数字、加急摘要、表格标题、表头和表体字号均上调。
10. 由于表格字号和行高放大,将列表可见行数从 8 行调整为 7 行,避免 1080 高度下内容被裁切。
11. 使用数据库直接执行 `仓库大屏_汇总查询`,发现原过程里发货、及时处理、异常出库等 `加急数量` 是加急件数累加,例如发货任务 13 条但加急显示 131不符合“只要条数”的要求。
12. 查看 `YL_MESDB``dbo.仓库大屏_汇总查询` 定义,确认入库和拣配加急已是条数,及时处理和发货类仍按 `SUM(加急数量)` 统计件数。
13. 初次使用 `sqlcmd` 管道更新过程时遇到中文编码问题SQL Server 将中文标识识别成问号,执行失败且未成功修改过程。
14. 改用 PowerShell `System.Data.SqlClient` 以 Unicode 字符串执行 `ALTER PROCEDURE`,成功更新 `dbo.仓库大屏_汇总查询`
15. 数据库过程调整口径:及时处理入库、及时处理汇报、发货任务、今日、异常出库的 `加急数量` 改为统计 `加急数量 > 0` 的记录条数。
16. 重新执行 `仓库大屏_汇总查询` 验证,发货任务变为 13 条、加急 10 条;今日变为 11 条、加急 8 条;异常出库变为 227 条、加急 13 条,符合条数口径。
17. 执行 `npm run build`,构建成功;仍仅有既有 Sass legacy JS API 提示和 Vite chunk size 警告。
### 结论
已去掉图表区域所有件数展示,四个图表统一为条数口径;加急柱顶标签和加急摘要数字已改为红色;异常出库已合并到拣配任务图表,发货图表只保留发货任务和今日;字号已整体放大。数据库汇总过程也已同步修正加急条数口径,避免前端显示加急件数。
## 2026-07-06 仓库任务大屏发货汇总口径修正
### 提问
用户反馈:发货任务图表顶部总数明显不对,怀疑忘记去掉异常出库数据。
### 执行过程
1. 根据截图判断:发货图表柱子只显示“发货任务 13”和“今日 11”但右上角汇总仍显示 250 条、加急 31 条。
2. 检查 `src/views/WarehouseDashboard.vue`,确认发货图表分类已经去掉“异常出库”,但图表头部汇总仍调用 `getSectionTotal('发货')`,因此把“异常出库”也算入了总数。
3. 新增 `getDeliverySectionTotal`,只合计 `发货 / 发货任务``发货 / 今日` 两个分类。
4.`chartSummaries.delivery``getSectionTotal('发货')` 改为 `getDeliverySectionTotal()`,保证发货图表顶部数字与图表柱子一致。
5. 执行 `npm run build`,构建成功;仍仅有既有 Sass legacy JS API 提示和 Vite chunk size 警告。
### 结论
已修正发货任务图表顶部汇总口径,异常出库不再计入发货任务顶部总数;当前发货顶部汇总只对应图表里的“发货任务 + 今日”。
## 2026-07-06 质检测试任务列表增加入库数与合格数
### 提问
用户要求修改 `C:\WorkYuanLy\装配任务看板\src\views\TestTaskDashboard.vue`:测试装配任务列表在完成数后增加两个字段“入库数”和“合格数”。入库数取 `[dbo].[View_生产订单_MES].入库数量`;合格数取 `[dbo].[YL_质量检验_质检记录].合格数` 的和,关联条件为 `YL_质量检验_质检记录.TaskAID = View_生产订单_MES.TaskAID``检测类型编号 = 6`;当合格数等于完成数时,该条数据不显示。
### 执行过程
1. 读取 `src/views/TestTaskDashboard.vue`,定位“测试装配任务列表”模板、`taskColumns` 列定义和 `applyTaskData` 数据映射。
2. 确认测试装配任务列表当前调用的数据源为 `质量管理_测试装配任务_查询2`
3. 查询数据库 `YL_MESDB``dbo.质量管理_测试装配任务_查询2` 的定义,确认当前过程已经从 `View_生产订单_MES` 返回 `入库数量`,但没有返回合格数,也没有过滤 `合格数 = 完成数` 的记录。
4. 查询数据库字段元数据,确认 `View_生产订单_MES` 包含 `TaskAID/入库数量/完成数量``YL_质量检验_质检记录` 包含 `TaskAID/检测类型编号/合格数`
5. 更新存储过程 `dbo.质量管理_测试装配任务_查询2`:增加按 `TaskAID` 汇总的质检记录子查询,只统计 `检测类型编号 = 6``合格数`
6. 在过程返回字段中新增 `ISNULL(qc.合格数, 0) AS 合格数`
7. 在过程过滤条件中增加 `ISNULL(qc.合格数, 0) <> ISNULL(pt.完成数量, 0)`,过滤掉合格数等于完成数的任务。
8. 执行过程验证,确认结果返回列包含 `入库数量``合格数`
9. 额外执行核对 SQL确认过滤后的结果中不存在 `合格数 = 完成数` 的记录。
10. 修改 `src/views/TestTaskDashboard.vue``applyTaskData` 中增加前端兜底过滤 `合格数 !== 完成数量`
11. 修改 `applyTaskData` 映射,新增 `inboundQty``入库数量``passQty``合格数`
12. 修改 `taskColumns`,在“完成数”后增加“入库数”和“合格数”两列,并适当缩小“操作人”列宽。
13. 执行 `npm run build`,构建成功;仍仅有既有 Sass legacy JS API 提示和 Vite chunk size 警告。
### 结论
已完成测试装配任务列表改造:完成数后新增入库数和合格数;合格数按 `检测类型编号 = 6` 的质检记录汇总;合格数等于完成数的任务已在数据库过程和前端双重过滤。构建验证通过。
## 2026-07-08 修复装配与质检测试看板右上角时间
### 提问
用户要求修改 `C:\WorkYuanLy\装配任务看板\src\views\index.vue``C:\WorkYuanLy\装配任务看板\src\views\TestTaskDashboard.vue`,反馈右上角时间不对。
### 执行过程
1. 读取 `src/views/index.vue``src/views/TestTaskDashboard.vue`,定位右上角时间显示绑定为 `currentTime`
2. 检索项目中时间相关实现,确认两个目标文件都将 `now` 初始化为固定时间 `2026-05-06 10:10:10`
3. 确认两个目标文件的定时器逻辑都是基于旧的 `now.value` 每秒加 1000 毫秒,因此页面显示的是从固定时间递增的模拟时间,不是系统当前时间,并且页面休眠或定时器暂停后也容易产生偏差。
4. 修改 `src/views/index.vue`:将 `const now = ref(new Date('2026-05-06 10:10:10'))` 改为 `const now = ref(new Date())`
5. 修改 `src/views/index.vue`:在 `startClock` 启动时立即刷新一次 `now.value = new Date()`,并将定时器更新逻辑改为每秒直接读取 `new Date()`
6. 修改 `src/views/TestTaskDashboard.vue`:将 `const now = ref(new Date('2026-05-06 10:10:10'))` 改为 `const now = ref(new Date())`
7. 修改 `src/views/TestTaskDashboard.vue`:将 `startClock` 改为启动时立即读取当前系统时间,后续每秒直接读取 `new Date()`
8. 查看 `git diff`,确认本次修改集中在右上角时钟相关逻辑;同时注意到两个目标文件中存在其他既有未提交改动,本次没有回退或改写这些无关内容。
9. 查看 `package.json`,确认项目可用校验脚本为 `npm run build`
10. 执行 `npm run build`Vite 构建成功;输出中仅保留既有 Sass legacy JS API 提示和 chunk size 警告。
### 结论
已修复两个看板右上角时间不对的问题。`index.vue``TestTaskDashboard.vue` 现在都会以系统当前时间初始化,并在运行中每秒直接读取当前系统时间,避免继续显示固定的 `2026-05-06 10:10:10` 模拟时间。构建验证通过。
## 2026-07-08 上传所有修改到远程
### 提问
用户要求:上传所有的修改到远程。
### 执行过程
1. 检查当前 Git 工作区状态,确认存在已修改文件:`dist.zip``gptlog-process/gpdlog.md``src/utils/request.js``src/views/TestTaskDashboard.vue``src/views/WarehouseDashboard.vue``src/views/index.vue`
2. 检查未跟踪文件,确认 `doc/未完成计划.xlsx``doc/~$未完成计划.xlsx` 也在当前工作区内;由于用户要求上传所有修改,本次按全部纳入处理。
3. 检查当前分支,确认当前位于 `main` 分支。
4. 检查远程仓库,确认 `origin` 指向 `http://154.8.160.151:3000/zhangxinyang/zhuangpei-task-kanban.git`
5. 检查最新本地提交,当前提交为 `4e4a419 docs: update upload log`
6. 执行 `git fetch origin`,刷新远程引用。
7. 执行 `git status -sb`,确认 `main``origin/main` 未显示 ahead/behind 分歧。
8. 查看 `git diff --stat`,确认本次待上传改动包含日志、看板页面、请求配置、压缩包等文件。
9. 按项目日志规则,将本次提问、执行过程和结论追加到 `gptlog-process/gpdlog.md`,使日志文件也随本次提交上传。
10. 准备执行 `git add -A` 暂存所有修改,随后创建提交并推送到 `origin/main`
### 结论
本次将按“所有修改”口径提交并推送当前工作区全部变更到远程 `origin/main`,包括本次追加的中文执行日志。

View File

@@ -8,7 +8,7 @@ const request_config = window.g.API_WEB_URL + `/submit/MESCommonBase.ashx`
export const name = name_config export const name = name_config
const service = axios.create({ const service = axios.create({
baseURL: request_config, baseURL: request_config,
timeout: 1000 * 5 // 请求超时时间 timeout: 1000 * 15 // 请求超时时间
}) })
// && config.data.name !== '菜单系统模块_通知信息_查询数据' && config.data.name !== '菜单系统模块_首页信息_查询数据1' && config.data.name !== '菜单系统模块_首页信息_查询数据2' // && config.data.name !== '菜单系统模块_通知信息_查询数据' && config.data.name !== '菜单系统模块_首页信息_查询数据1' && config.data.name !== '菜单系统模块_首页信息_查询数据2'

View File

@@ -145,7 +145,7 @@ const CreateData = inject('CreateData')
const ExecDatabase = inject('ExecDatabase') const ExecDatabase = inject('ExecDatabase')
defineOptions({ name: 'AssemblyCenterTaskDashboard' }) defineOptions({ name: 'AssemblyCenterTaskDashboard' })
const now = ref(new Date('2026-05-06 10:10:10')) const now = ref(new Date())
let timer = null let timer = null
let resizeHandler = null let resizeHandler = null
let scrollTimers = [] let scrollTimers = []
@@ -203,7 +203,7 @@ const VISIBLE_ROWS = {
const testSummaryGroups = [ const testSummaryGroups = [
{ {
key: 'valve', key: 'valve',
label: '类', label: '部件类',
products: [ products: [
{ key: 'valveMain', label: '阀', image: valveImage }, { key: 'valveMain', label: '阀', image: valveImage },
{ key: 'valveOther', label: '其他', image: otherImage } { key: 'valveOther', label: '其他', image: otherImage }
@@ -329,7 +329,7 @@ const searchTestAssemblyTaskData = () => {
['物料名称', ''], ['物料名称', ''],
['物料编码', ''] ['物料编码', '']
] ]
queryDashboardData('质量管理_测试装配任务_查询', applyTestAssemblyTaskData, param) queryDashboardData('质检测试看板_测试装配任务_查询', applyTestAssemblyTaskData, param)
} }
const searchTestAssemblyTaskListData = () => { const searchTestAssemblyTaskListData = () => {
queryDashboardData('质量管理_测试装配任务_查询2', applyTaskData) queryDashboardData('质量管理_测试装配任务_查询2', applyTaskData)
@@ -372,7 +372,7 @@ const searchStagnationData = () => {
queryDashboardData('质量管理_及时检验报表_查询', applyStagnationData, param) queryDashboardData('质量管理_及时检验报表_查询', applyStagnationData, param)
} }
const searchDeliveryData = () => { const searchDeliveryData = () => {
queryDashboardData('装配中心任务看板_质检发货状态查询', applyDeliveryData) queryDashboardData('质检测试看板_质检发货状态查询', applyDeliveryData)
} }
const applyTestAssemblyTaskData = rows => { const applyTestAssemblyTaskData = rows => {
const sortedRows = sortTestAssemblyTaskRows(rows) const sortedRows = sortTestAssemblyTaskRows(rows)
@@ -431,7 +431,7 @@ const applyDoneTaskData = rows => {
} }
const applyTaskData = rows => { const applyTaskData = rows => {
taskRows.value = rows.map(item => ({ taskRows.value = rows.filter(item => getNumber(item.合格数) !== getNumber(item.完成数量)).map(item => ({
productionOrder: item.生产订单?.toString() || item.订单号?.toString() || item.计划号?.toString() || '', productionOrder: item.生产订单?.toString() || item.订单号?.toString() || item.计划号?.toString() || '',
salesOrder: item.销售订单 || item.合同号 || '', salesOrder: item.销售订单 || item.合同号 || '',
materialName: item.物料名称 || item.物料描述 || item.零件名称 || '', materialName: item.物料名称 || item.物料描述 || item.零件名称 || '',
@@ -440,6 +440,8 @@ const applyTaskData = rows => {
urgentQty: getNumber(item.加急总数 || item.加急数量), urgentQty: getNumber(item.加急总数 || item.加急数量),
assignQty: getNumber(item.计划数量 || item.计划生产数 || item.分配数), assignQty: getNumber(item.计划数量 || item.计划生产数 || item.分配数),
completeQty: getNumber(item.完成数量), completeQty: getNumber(item.完成数量),
inboundQty: getNumber(item.入库数量),
passQty: getNumber(item.合格数),
operator: item.操作人 || '' operator: item.操作人 || ''
})) }))
} }
@@ -503,9 +505,9 @@ const normalizeText = value => {
return String(value).trim() return String(value).trim()
} }
const getMaterialCodeText = item => normalizeText(getField(item, ['物料编码', '零件编码', '物料编号', '零件编号', '产品编码'])) const getMaterialCodeText = item => normalizeText(getField(item, ['零件名称']))
const getAssignTargetText = item => normalizeText(getField(item, ['指派对象', '二次派工'])) const getAssignTargetText = item => normalizeText(getField(item, ['工位名称', '二次派工']))
const getTaskType = item => item.任务类型 || (String(item.自制件属性 || item.物料名称 || item.零件名称 || '').includes('系统') ? '系统类' : '阀类') const getTaskType = item => item.任务类型 || (String(item.自制件属性 || item.物料名称 || item.零件名称 || '').includes('系统') ? '系统类' : '阀类')
@@ -573,7 +575,7 @@ const getPassRate = item => {
return Number(((getNumber(item.检验合格数量) * 100) / inspectQty).toFixed(2)) return Number(((getNumber(item.检验合格数量) * 100) / inspectQty).toFixed(2))
} }
const startClock = () => { timer = window.setInterval(() => { now.value = new Date(now.value.getTime() + 1000) }, 1000) } const startClock = () => { now.value = new Date(); timer = window.setInterval(() => { now.value = new Date() }, 1000) }
const svgData = svg => 'data:image/svg+xml;utf8,' + encodeURIComponent(svg) const svgData = svg => 'data:image/svg+xml;utf8,' + encodeURIComponent(svg)
const svgIcon = (from, to, body) => svgData( const svgIcon = (from, to, body) => svgData(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72" fill="none"> `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72" fill="none">
@@ -621,7 +623,9 @@ const taskColumns = [
{ label: '加急数', key: 'urgentQty', width: '62px' }, { label: '加急数', key: 'urgentQty', width: '62px' },
{ label: '计划数', key: 'assignQty', width: '70px' }, { label: '计划数', key: 'assignQty', width: '70px' },
{ label: '完成数', key: 'completeQty', width: '70px' }, { label: '完成数', key: 'completeQty', width: '70px' },
{ label: '操作人', key: 'operator', width: '120px' }, { label: '入库数', key: 'inboundQty', width: '70px' },
{ label: '合格数', key: 'passQty', width: '70px' },
{ label: '操作人', key: 'operator', width: '100px' },
] ]
const taskColumns2 = [ const taskColumns2 = [
@@ -1115,8 +1119,8 @@ th {
} }
.summary-product-image { .summary-product-image {
width: 82px; width: 130px;
height: 62px; height: 75px;
object-fit: contain; object-fit: contain;
} }

View File

@@ -11,37 +11,62 @@
<h1>仓库任务大屏</h1> <h1>仓库任务大屏</h1>
<div class="time-block"> <div class="time-block">
<div class="current-time">{{ currentTime }}</div> <div class="current-time">{{ currentTime }}</div>
<div class="refresh-time">数据刷新 {{ refreshTimeText }}</div> <!-- <div class="refresh-time">数据刷新 {{ refreshTimeText }}</div> -->
</div> </div>
</header> </header>
<main class="dashboard-content"> <main class="dashboard-content">
<section class="kpi-row">
<article v-for="item in summaryCards" :key="item.key" class="panel kpi-card" :class="item.className">
<div class="kpi-title">{{ item.title }}</div>
<div class="kpi-value">
<span>{{ item.value }}</span>
<small>{{ item.unit }}</small>
</div>
<div class="kpi-sub">{{ item.subText }}</div>
</article>
</section>
<section class="chart-grid"> <section class="chart-grid">
<article class="panel chart-panel"> <article class="panel chart-panel">
<div class="panel-caption">入库任务结构</div> <div class="chart-panel-head">
<div class="chart-title">入库任务</div>
<div class="chart-summary">
<div class="chart-summary-value">
<span>{{ chartSummaries.inbound.value }}</span>
<small></small>
</div>
<div class="chart-summary-sub">加急 <span>{{ chartSummaries.inbound.urgent }}</span> </div>
</div>
</div>
<div ref="inboundChartRef" class="chart-box"></div> <div ref="inboundChartRef" class="chart-box"></div>
</article> </article>
<article class="panel chart-panel"> <article class="panel chart-panel">
<div class="panel-caption">拣配任务结构</div> <div class="chart-panel-head">
<div class="chart-title">拣配任务</div>
<div class="chart-summary">
<div class="chart-summary-value">
<span>{{ chartSummaries.picking.value }}</span>
<small></small>
</div>
<div class="chart-summary-sub">加急 <span>{{ chartSummaries.picking.urgent }}</span> </div>
</div>
</div>
<div ref="pickingChartRef" class="chart-box"></div> <div ref="pickingChartRef" class="chart-box"></div>
</article> </article>
<article class="panel chart-panel"> <article class="panel chart-panel">
<div class="panel-caption">超期风险分布</div> <div class="chart-panel-head">
<div class="chart-title">及时处理</div>
<div class="chart-summary">
<div class="chart-summary-value">
<span>{{ chartSummaries.timely.value }}</span>
<small></small>
</div>
<div class="chart-summary-sub">加急 <span>{{ chartSummaries.timely.urgent }}</span> </div>
</div>
</div>
<div ref="riskChartRef" class="chart-box"></div> <div ref="riskChartRef" class="chart-box"></div>
</article> </article>
<article class="panel chart-panel"> <article class="panel chart-panel">
<div class="panel-caption">今日发货保障</div> <div class="chart-panel-head">
<div class="chart-title">发货任务</div>
<div class="chart-summary">
<div class="chart-summary-value">
<span>{{ chartSummaries.delivery.value }}</span>
<small></small>
</div>
<div class="chart-summary-sub">加急 <span>{{ chartSummaries.delivery.urgent }}</span> </div>
</div>
</div>
<div ref="deliveryChartRef" class="chart-box"></div> <div ref="deliveryChartRef" class="chart-box"></div>
</article> </article>
</section> </section>
@@ -112,125 +137,60 @@ const deliveryChartRef = ref(null)
const summaryRows = ref([]) const summaryRows = ref([])
const inboundOverdueRows = ref([]) const inboundOverdueRows = ref([])
const outboundOverdueRows = ref([]) const outboundOverdueRows = ref([])
const urgentPickingRows = ref([])
const todayDeliveryRows = ref([]) const todayDeliveryRows = ref([])
const inboundOffset = ref(0) const inboundOffset = ref(0)
const outboundOffset = ref(0) const outboundOffset = ref(0)
const urgentPickingOffset = ref(0)
const deliveryOffset = ref(0) const deliveryOffset = ref(0)
const VISIBLE_ROWS = { const VISIBLE_ROWS = {
inbound: 6, inbound: 7,
outbound: 6, outbound: 7,
delivery: 6 urgentPicking: 7,
delivery: 7
} }
const inboundCategories = ['装配件', '机加件', '原材料', '外购件'] const inboundCategories = ['原料', '件', '机加', '装配']
const pickingCategories = ['装配件', '机加件'] const pickingCategories = ['机加', '装配']
const riskCategories = ['超2天未入库', '超2天未上架', '超10天未出库', '汇报未清'] const riskCategories = ['上架', '库', '汇报']
const currentTime = computed(() => formatDateTime(now.value)) const currentTime = computed(() => formatDateTime(now.value))
const refreshTimeText = computed(() => lastRefreshTime.value || '--:--:--') const refreshTimeText = computed(() => lastRefreshTime.value || '--:--:--')
const chartSummaries = computed(() => ({
const summaryCards = computed(() => { inbound: formatChartSummary(getSectionTotal('入库')),
const inboundTaskQty = sumRows('入库', 'quantity') picking: formatChartSummary(getPickingSectionTotal()),
const inboundUrgentQty = sumRows('入库', 'urgentQty') timely: formatChartSummary(getSectionTotal('及时处理')),
const inboundItemCount = sumRows('入库', 'taskCount') delivery: formatChartSummary(getDeliverySectionTotal())
const pickingTaskQty = sumRows('拣配', 'quantity') }))
const pickingUrgentQty = sumRows('拣配', 'urgentQty')
const pickingItemCount = sumRows('拣配', 'taskCount')
const overdueInbound = getSummaryRow('风险', '超2天未入库')
const overdueInventory = getSummaryRow('风险', '超2天未上架')
const overdueOutbound = getSummaryRow('风险', '超10天未出库')
const reportOpen = getSummaryRow('风险', '汇报未清')
const todayDelivery = getSummaryRow('发货', '今日应发')
return [
{
key: 'inbound',
title: '待入库任务',
value: formatCompactNumber(inboundTaskQty),
unit: '条',
subText: `${formatAmount(inboundItemCount)} 项物料`,
className: 'blue-card'
},
{
key: 'inboundUrgent',
title: '入库加急',
value: formatCompactNumber(inboundUrgentQty),
unit: '条',
subText: `加急占比 ${formatPercent(inboundUrgentQty, inboundTaskQty)}`,
className: 'red-card'
},
{
key: 'overdueInbound',
title: '超2天未入库',
value: formatCompactNumber(overdueInbound.quantity),
unit: '件',
subText: `${formatAmount(overdueInbound.taskCount)} 条任务`,
className: 'orange-card'
},
{
key: 'overdueInventory',
title: '超2天未上架',
value: formatCompactNumber(overdueInventory.quantity),
unit: '件',
subText: `${formatAmount(overdueInventory.taskCount)} 条物料`,
className: 'orange-card'
},
{
key: 'picking',
title: '未清拣配',
value: formatCompactNumber(pickingTaskQty),
unit: '条',
subText: `${formatAmount(pickingItemCount)} 项物料`,
className: 'blue-card'
},
{
key: 'overdueOutbound',
title: '超10天未出库',
value: formatCompactNumber(overdueOutbound.quantity),
unit: '件',
subText: `${formatAmount(overdueOutbound.taskCount)} 条任务`,
className: 'red-card'
},
{
key: 'reportOpen',
title: '汇报未清',
value: formatCompactNumber(reportOpen.quantity),
unit: '件',
subText: `加急 ${formatCompactNumber(reportOpen.urgentQty)}`,
className: 'red-card'
},
{
key: 'todayDelivery',
title: '今日应发',
value: formatCompactNumber(todayDelivery.quantity),
unit: '件',
subText: `${formatAmount(todayDelivery.taskCount)} 单 / 加急 ${formatCompactNumber(todayDelivery.urgentQty)}`,
className: todayDelivery.urgentQty > 0 ? 'red-card' : 'green-card'
}
]
})
const inboundDisplayRows = computed(() => makeDisplayRows(inboundOverdueRows.value, inboundOffset.value, VISIBLE_ROWS.inbound)) const inboundDisplayRows = computed(() => makeDisplayRows(inboundOverdueRows.value, inboundOffset.value, VISIBLE_ROWS.inbound))
const outboundDisplayRows = computed(() => makeDisplayRows(outboundOverdueRows.value, outboundOffset.value, VISIBLE_ROWS.outbound)) const outboundDisplayRows = computed(() => makeDisplayRows(outboundOverdueRows.value, outboundOffset.value, VISIBLE_ROWS.outbound))
const urgentPickingDisplayRows = computed(() => makeDisplayRows(urgentPickingRows.value, urgentPickingOffset.value, VISIBLE_ROWS.urgentPicking))
const deliveryDisplayRows = computed(() => makeDisplayRows(todayDeliveryRows.value, deliveryOffset.value, VISIBLE_ROWS.delivery)) const deliveryDisplayRows = computed(() => makeDisplayRows(todayDeliveryRows.value, deliveryOffset.value, VISIBLE_ROWS.delivery))
const detailTables = computed(() => [ const detailTables = computed(() => [
{ {
key: 'inbound', key: 'inbound',
title: '超2天未入库明细', title: '加急入库列表',
columns: inboundColumns, columns: urgentInboundColumns,
rows: inboundDisplayRows.value rows: inboundDisplayRows.value
}, },
{ {
key: 'outbound', key: 'outbound',
title: '超10天未出库明细', title: '拣配任务列表',
columns: outboundColumns, columns: pickingColumns,
rows: outboundDisplayRows.value rows: outboundDisplayRows.value
}, },
{
key: 'urgentPicking',
title: '加急拣配列表',
columns: urgentPickingColumns,
rows: urgentPickingDisplayRows.value
},
{ {
key: 'delivery', key: 'delivery',
title: '今日应发明细', title: '发货任务列表',
columns: deliveryColumns, columns: deliveryColumns,
rows: deliveryDisplayRows.value rows: deliveryDisplayRows.value
} }
@@ -333,9 +293,10 @@ const disposeCharts = () => {
const searchDashboardData = () => { const searchDashboardData = () => {
const tasks = [ const tasks = [
queryDashboardData('仓库大屏_汇总查询', applySummaryData), queryDashboardData('仓库大屏_汇总查询', applySummaryData),
queryDashboardData('仓库大屏_入库超期明细', applyInboundOverdueData), queryDashboardData('仓库大屏_加急入库列表', applyInboundOverdueData),
queryDashboardData('仓库大屏_出库超期明细', applyOutboundOverdueData), queryDashboardData('仓库大屏_拣配任务列表', applyOutboundOverdueData),
queryDashboardData('仓库大屏_今日应发明细', applyTodayDeliveryData) queryDashboardData('仓库大屏_加急拣配列表', applyUrgentPickingData),
queryDashboardData('装配中心任务看板_发货状态查询', applyTodayDeliveryData)
] ]
Promise.all(tasks).finally(() => { Promise.all(tasks).finally(() => {
@@ -378,6 +339,10 @@ const getResponseRows = response => {
} }
const applySummaryData = rows => { const applySummaryData = rows => {
if (!Array.isArray(rows) || rows.length === 0) {
return
}
summaryRows.value = rows.map(row => ({ summaryRows.value = rows.map(row => ({
section: normalizeText(row.环节), section: normalizeText(row.环节),
category: normalizeText(row.分类), category: normalizeText(row.分类),
@@ -392,11 +357,9 @@ const applyInboundOverdueData = rows => {
inboundOffset.value = 0 inboundOffset.value = 0
inboundOverdueRows.value = rows.map((row, index) => ({ inboundOverdueRows.value = rows.map((row, index) => ({
_idx: index + 1, _idx: index + 1,
docDate: formatDateValue(row.单据日期), category: normalizeText(row.类别),
orderNo: normalizeText(row.单号), orderNo: normalizeText(row.单号),
itemCode: normalizeText(row.物料编码),
itemName: normalizeText(row.物料名称), itemName: normalizeText(row.物料名称),
quantity: formatAmount(row.未入库数量, 2),
urgentQty: formatAmount(row.加急数量, 2), urgentQty: formatAmount(row.加急数量, 2),
urgentValue: toNumber(row.加急数量) urgentValue: toNumber(row.加急数量)
})) }))
@@ -406,14 +369,24 @@ const applyOutboundOverdueData = rows => {
outboundOffset.value = 0 outboundOffset.value = 0
outboundOverdueRows.value = rows.map((row, index) => ({ outboundOverdueRows.value = rows.map((row, index) => ({
_idx: index + 1, _idx: index + 1,
pickNo: normalizeText(row.拣配清单), salesOrder: normalizeText(row.合同),
pickDate: formatDateValue(row.拣配日期), productionOrder: normalizeText(row.生产订单),
productionOrder: normalizeText(row.生产订单号), itemName: normalizeText(row.物料名称),
itemCode: normalizeText(row.物料编码), completeSet: normalizeText(row.齐套),
itemName: normalizeText(row.物料描述), status: normalizeText(row.状态),
planQty: formatAmount(row.计划数量, 2), urgentValue: 0
urgentQty: formatAmount(row.加急数量, 2), }))
urgentValue: toNumber(row.加急数量) }
const applyUrgentPickingData = rows => {
urgentPickingOffset.value = 0
urgentPickingRows.value = rows.map((row, index) => ({
_idx: index + 1,
category: normalizeText(row.类别),
orderNo: normalizeText(row.单号),
itemName: normalizeText(row.物料名称),
urgentStatus: normalizeText(row.加急状态),
urgentValue: 1
})) }))
} }
@@ -421,20 +394,19 @@ const applyTodayDeliveryData = rows => {
deliveryOffset.value = 0 deliveryOffset.value = 0
todayDeliveryRows.value = rows.map((row, index) => ({ todayDeliveryRows.value = rows.map((row, index) => ({
_idx: index + 1, _idx: index + 1,
deliveryDate: formatDateValue(row.发货日期), salesOrder: normalizeText(row.合同号),
customer: normalizeText(row.客户), itemName: normalizeText(row.物料描述 || row.物料名称),
externalNo: normalizeText(row.外部单号 || row.订单号), deliveryQty: formatAmount(row.交货数量 || row.计划数量, 2),
itemCode: normalizeText(row.物料编码), deliveryDate: formatDateValue(row.要求发货日期 || row.发货日期),
itemName: normalizeText(row.物料名称), testQty: formatAmount(row.检验数量, 2),
openQty: formatAmount(row.未清数量, 2), urgentValue: toNumber(row.加急总数 || row.加急数量)
urgentQty: formatAmount(row.加急数量, 2),
urgentValue: toNumber(row.加急数量)
})) }))
} }
const startAllScroll = () => { const startAllScroll = () => {
startScrollWhenOverflow(inboundOverdueRows, inboundOffset, VISIBLE_ROWS.inbound) startScrollWhenOverflow(inboundOverdueRows, inboundOffset, VISIBLE_ROWS.inbound)
startScrollWhenOverflow(outboundOverdueRows, outboundOffset, VISIBLE_ROWS.outbound) startScrollWhenOverflow(outboundOverdueRows, outboundOffset, VISIBLE_ROWS.outbound)
startScrollWhenOverflow(urgentPickingRows, urgentPickingOffset, VISIBLE_ROWS.urgentPicking)
startScrollWhenOverflow(todayDeliveryRows, deliveryOffset, VISIBLE_ROWS.delivery) startScrollWhenOverflow(todayDeliveryRows, deliveryOffset, VISIBLE_ROWS.delivery)
} }
@@ -471,18 +443,24 @@ const makeDisplayRows = (rows, offset, visibleCount) => {
const createInboundChartOption = () => createBarOption({ const createInboundChartOption = () => createBarOption({
categories: inboundCategories, categories: inboundCategories,
totalName: '未入库任务', totalName: '任务',
urgentName: '加急任务', urgentName: '加急',
totalData: inboundCategories.map(category => getSummaryRow('入库', category).quantity), totalData: inboundCategories.map(category => getSummaryRow('入库', category).taskCount),
urgentData: inboundCategories.map(category => getSummaryRow('入库', category).urgentQty) urgentData: inboundCategories.map(category => getSummaryRow('入库', category).urgentQty)
}) })
const createPickingChartOption = () => createBarOption({ const createPickingChartOption = () => createBarOption({
categories: pickingCategories, categories: [...pickingCategories, '异常出库'],
totalName: '未清拣配', totalName: '任务',
urgentName: '加急拣配', urgentName: '加急',
totalData: pickingCategories.map(category => getSummaryRow('拣配', category).quantity), totalData: [
urgentData: pickingCategories.map(category => getSummaryRow('拣配', category).urgentQty) ...pickingCategories.map(category => getSummaryRow('拣配', category).taskCount),
getSummaryRow('发货', '异常出库').taskCount
],
urgentData: [
...pickingCategories.map(category => getSummaryRow('拣配', category).urgentQty),
getSummaryRow('发货', '异常出库').urgentQty
]
}) })
const createBarOption = ({ categories, totalName, urgentName, totalData, urgentData }) => ({ const createBarOption = ({ categories, totalName, urgentName, totalData, urgentData }) => ({
@@ -492,170 +470,92 @@ const createBarOption = ({ categories, totalName, urgentName, totalData, urgentD
axisPointer: { type: 'shadow' }, axisPointer: { type: 'shadow' },
backgroundColor: 'rgba(3, 16, 48, 0.94)', backgroundColor: 'rgba(3, 16, 48, 0.94)',
borderColor: 'rgba(43, 216, 255, 0.55)', borderColor: 'rgba(43, 216, 255, 0.55)',
textStyle: { color: '#f3f9ff' }, textStyle: { color: '#f3f9ff', fontSize: 19 },
valueFormatter: value => formatAmount(value, 2) valueFormatter: value => formatAmount(value, 2)
}, },
legend: { legend: {
top: 0, top: 0,
right: 18, right: 18,
itemWidth: 14, itemWidth: 18,
itemHeight: 10, itemHeight: 12,
textStyle: { color: '#bde9ff', fontSize: 13 } textStyle: { color: '#bde9ff', fontSize: 19 }
}, },
grid: { grid: {
left: 58, left: 18,
right: 24, right: 18,
top: 48, top: 46,
bottom: 38 bottom: 36,
containLabel: true
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: categories, data: categories,
axisLine: { lineStyle: { color: 'rgba(141, 211, 255, 0.6)' } }, axisLine: { lineStyle: { color: 'rgba(141, 211, 255, 0.6)' } },
axisTick: { show: false }, axisTick: { show: false },
axisLabel: { color: '#d8f3ff', fontSize: 15 } axisLabel: { color: '#d8f3ff', fontSize: 21, fontWeight: 700 }
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
splitLine: { lineStyle: { color: 'rgba(95, 172, 231, 0.18)' } }, axisLine: { show: false },
axisLabel: { axisTick: { show: false },
color: '#9bdcff', axisLabel: { show: false },
formatter: value => formatCompactNumber(value) splitLine: { show: false }
}
}, },
series: [ series: [
{ {
name: totalName, name: totalName,
type: 'bar', type: 'bar',
barWidth: 28, barWidth: 32,
data: totalData, data: totalData,
itemStyle: { itemStyle: {
borderRadius: [3, 3, 0, 0] borderRadius: [3, 3, 0, 0]
}
},
{
name: urgentName,
type: 'bar',
barWidth: 28,
data: urgentData,
itemStyle: {
borderRadius: [3, 3, 0, 0]
}
}
]
})
const createRiskChartOption = () => {
const data = riskCategories.map(category => {
const row = getSummaryRow('风险', category)
return {
name: category,
value: row.taskCount,
quantity: row.quantity,
urgentQty: row.urgentQty
}
})
return {
color: ['#ffb13b', '#e5602c', '#ff3040', '#8f6bff'],
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(3, 16, 48, 0.94)',
borderColor: 'rgba(43, 216, 255, 0.55)',
textStyle: { color: '#f3f9ff' },
formatter: params => {
const item = params.data
return `${params.name}<br/>任务数:${formatAmount(item.value)}<br/>数量:${formatAmount(item.quantity, 2)}<br/>加急:${formatAmount(item.urgentQty, 2)}`
}
},
legend: {
orient: 'vertical',
right: 24,
top: 46,
itemWidth: 14,
itemHeight: 10,
textStyle: { color: '#bde9ff', fontSize: 13 }
},
series: [
{
name: '风险任务',
type: 'pie',
radius: ['44%', '70%'],
center: ['39%', '54%'],
roseType: 'radius',
avoidLabelOverlap: true,
label: {
color: '#f3f9ff',
fontSize: 13,
formatter: '{b}\n{c}条'
},
labelLine: {
lineStyle: { color: 'rgba(189, 233, 255, 0.68)' }
},
data
}
]
}
}
const createDeliveryChartOption = () => {
const today = getSummaryRow('发货', '今日应发')
const open = getSummaryRow('发货', '销售交货未清')
const categories = ['今日应发', '今日加急', '销售未清', '销售加急']
const data = [today.quantity, today.urgentQty, open.quantity, open.urgentQty]
const colors = ['#18d36f', '#ff3040', '#35c5ff', '#ff6a37']
return {
color: colors,
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
backgroundColor: 'rgba(3, 16, 48, 0.94)',
borderColor: 'rgba(43, 216, 255, 0.55)',
textStyle: { color: '#f3f9ff' },
valueFormatter: value => formatAmount(value, 2)
},
grid: {
left: 58,
right: 24,
top: 34,
bottom: 42
},
xAxis: {
type: 'category',
data: categories,
axisLine: { lineStyle: { color: 'rgba(141, 211, 255, 0.6)' } },
axisTick: { show: false },
axisLabel: { color: '#d8f3ff', fontSize: 14 }
},
yAxis: {
type: 'value',
splitLine: { lineStyle: { color: 'rgba(95, 172, 231, 0.18)' } },
axisLabel: {
color: '#9bdcff',
formatter: value => formatCompactNumber(value)
}
},
series: [
{
name: '数量',
type: 'bar',
barWidth: 32,
data,
itemStyle: {
borderRadius: [3, 3, 0, 0],
color: params => colors[params.dataIndex]
}, },
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
color: '#f3f9ff', color: '#f3f9ff',
fontSize: 13, fontSize: 19,
fontWeight: 700,
formatter: params => formatCompactNumber(params.value)
}
},
{
name: urgentName,
type: 'bar',
barWidth: 32,
data: urgentData,
itemStyle: {
borderRadius: [3, 3, 0, 0]
},
label: {
show: true,
position: 'top',
color: '#ff3345',
fontSize: 19,
fontWeight: 800,
formatter: params => formatCompactNumber(params.value) formatter: params => formatCompactNumber(params.value)
} }
} }
] ]
} })
const createRiskChartOption = () => createBarOption({
categories: riskCategories,
totalName: '任务',
urgentName: '加急',
totalData: riskCategories.map(category => getSummaryRow('及时处理', category).taskCount),
urgentData: riskCategories.map(category => getSummaryRow('及时处理', category).urgentQty)
})
const createDeliveryChartOption = () => {
const categories = ['发货任务', '小于今日']
return createBarOption({
categories,
totalName: '任务',
urgentName: '加急',
totalData: categories.map(category => getSummaryRow('发货', category).taskCount),
urgentData: categories.map(category => getSummaryRow('发货', category).urgentQty)
})
} }
const getSummaryRow = (section, category) => { const getSummaryRow = (section, category) => {
@@ -675,6 +575,37 @@ const sumRows = (section, key) => {
.reduce((total, row) => total + toNumber(row[key]), 0) .reduce((total, row) => total + toNumber(row[key]), 0)
} }
const getSectionTotal = section => ({
taskCount: sumRows(section, 'taskCount'),
quantity: sumRows(section, 'quantity'),
urgentQty: sumRows(section, 'urgentQty')
})
const getPickingSectionTotal = () => {
const pickingTotal = getSectionTotal('拣配')
const abnormalDelivery = getSummaryRow('发货', '异常出库')
return {
taskCount: pickingTotal.taskCount + abnormalDelivery.taskCount,
quantity: pickingTotal.quantity + abnormalDelivery.quantity,
urgentQty: pickingTotal.urgentQty + abnormalDelivery.urgentQty
}
}
const getDeliverySectionTotal = () => {
const deliveryTask = getSummaryRow('发货', '发货任务')
const todayDelivery = getSummaryRow('发货', '小于今日')
return {
taskCount: deliveryTask.taskCount ,
quantity: deliveryTask.quantity,
urgentQty: deliveryTask.urgentQty
}
}
const formatChartSummary = total => ({
value: formatAmount(total.taskCount),
urgent: formatAmount(total.urgentQty)
})
const pad = value => String(value).padStart(2, '0') const pad = value => String(value).padStart(2, '0')
const formatDateTime = value => { const formatDateTime = value => {
@@ -748,33 +679,33 @@ const normalizeText = value => {
return String(value).trim() return String(value).trim()
} }
const inboundColumns = [ const urgentInboundColumns = [
{ label: '单据日期', key: 'docDate', width: '78px' }, { label: '类别', key: 'category', width: '13%' },
{ label: '单号', key: 'orderNo', width: '66px' }, { label: '单号', key: 'orderNo', width: '20%' },
{ label: '物料编码', key: 'itemCode', width: '150px' }, { label: '物料名称', key: 'itemName', width: '52%' },
{ label: '物料名称', key: 'itemName', width: '178px' }, { label: '加急', key: 'urgentQty', width: '15%' }
{ label: '未入库', key: 'quantity', width: '70px' },
{ label: '加急', key: 'urgentQty', width: '58px' }
] ]
const outboundColumns = [ const pickingColumns = [
{ label: '拣配单', key: 'pickNo', width: '66px' }, { label: '合同号', key: 'salesOrder', width: '20%' },
{ label: '日期', key: 'pickDate', width: '76px' }, { label: '生产订单', key: 'productionOrder', width: '18%' },
{ label: '生产订单', key: 'productionOrder', width: '70px' }, { label: '物料名称', key: 'itemName', width: '47%' },
{ label: '物料编码', key: 'itemCode', width: '148px' }, { label: '齐套', key: 'completeSet', width: '15%' }
{ label: '物料描述', key: 'itemName', width: '178px' }, ]
{ label: '计划', key: 'planQty', width: '58px' },
{ label: '加急', key: 'urgentQty', width: '58px' } const urgentPickingColumns = [
{ label: '类别', key: 'category', width: '13%' },
{ label: '单号', key: 'orderNo', width: '20%' },
{ label: '物料名称', key: 'itemName', width: '52%' },
{ label: '状态', key: 'urgentStatus', width: '15%' }
] ]
const deliveryColumns = [ const deliveryColumns = [
{ label: '发货日期', key: 'deliveryDate', width: '78px' }, { label: '合同号', key: 'salesOrder', width: '24%' },
{ label: '客户', key: 'customer', width: '128px' }, { label: '物料名称', key: 'itemName', width: '30%' },
{ label: '订单号', key: 'externalNo', width: '82px' }, { label: '数量', key: 'deliveryQty', width: '12%' },
{ label: '物料编码', key: 'itemCode', width: '150px' }, { label: '发货日期', key: 'deliveryDate', width: '25%' },
{ label: '物料名称', key: 'itemName', width: '154px' }, { label: '测试', key: 'testQty', width: '10%' }
{ label: '未清', key: 'openQty', width: '58px' },
{ label: '加急', key: 'urgentQty', width: '58px' }
] ]
</script> </script>
@@ -863,30 +794,22 @@ const deliveryColumns = [
.dashboard-content { .dashboard-content {
display: grid; display: grid;
grid-template-rows: 128px 480px 1fr; grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
gap: 15px; gap: 15px;
height: calc(100% - 95px); height: calc(100% - 95px);
padding-top: 10px; padding-top: 10px;
} }
.kpi-row {
display: grid;
grid-template-columns: repeat(8, minmax(0, 1fr));
gap: 15px;
min-height: 0;
}
.chart-grid { .chart-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(4, minmax(0, 1fr));
grid-template-rows: repeat(2, minmax(0, 1fr));
gap: 15px; gap: 15px;
min-height: 0; min-height: 0;
} }
.detail-grid { .detail-grid {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 15px; gap: 15px;
min-height: 0; min-height: 0;
} }
@@ -926,76 +849,6 @@ const deliveryColumns = [
box-shadow: inset 0 0 12px rgba(43, 216, 255, 0.12); box-shadow: inset 0 0 12px rgba(43, 216, 255, 0.12);
} }
.kpi-card {
display: grid;
grid-template-rows: 28px minmax(0, 1fr) 24px;
align-items: center;
min-width: 0;
padding: 12px 14px 10px;
}
.kpi-title {
overflow: hidden;
color: #95ddff;
font-size: 19px;
font-weight: 700;
line-height: 28px;
text-overflow: ellipsis;
white-space: nowrap;
}
.kpi-value {
display: flex;
align-items: baseline;
justify-content: center;
min-width: 0;
color: #ffffff;
font-weight: 900;
line-height: 1;
}
.kpi-value span {
overflow: hidden;
max-width: 150px;
font-size: 42px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
}
.kpi-value small {
margin-left: 6px;
color: #ffffff;
font-size: 18px;
font-weight: 700;
white-space: nowrap;
}
.kpi-sub {
overflow: hidden;
color: #c8eaff;
font-size: 15px;
line-height: 22px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
}
.blue-card .kpi-value span {
color: #36c8ff;
}
.green-card .kpi-value span {
color: #18d36f;
}
.red-card .kpi-value span {
color: #ff3040;
}
.orange-card .kpi-value span {
color: #ffb13b;
}
.chart-panel, .chart-panel,
.table-panel { .table-panel {
@@ -1003,12 +856,74 @@ const deliveryColumns = [
flex-direction: column; flex-direction: column;
} }
.panel-caption { .chart-panel-head {
height: 38px; position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 104px;
padding: 12px 22px 4px;
gap: 14px;
}
.chart-title {
flex: 1;
min-width: 0;
color: #8cd3ff; color: #8cd3ff;
font-size: 26px; font-size: 36px;
font-weight: 800;
line-height: 1.15;
}
.chart-summary {
flex: 0 0 auto;
min-width: 210px;
color: #f3f9ff;
text-align: right;
}
.chart-summary-value {
display: flex;
align-items: baseline;
justify-content: flex-end;
color: #36c8ff;
font-weight: 900;
line-height: 1;
white-space: nowrap;
}
.chart-summary-value span {
font-size: 64px;
}
.chart-summary-value small {
margin-left: 8px;
color: #ffffff;
font-size: 28px;
font-weight: 800;
}
.chart-summary-sub {
margin-top: 6px;
color: #c8eaff;
font-size: 21px;
font-weight: 700; font-weight: 700;
line-height: 38px; line-height: 1.25;
white-space: nowrap;
}
.chart-summary-sub span {
color: #ff3345;
font-weight: 900;
}
.panel-caption {
height: 44px;
color: #8cd3ff;
font-size: 34px;
font-weight: 700;
line-height: 44px;
text-align: center; text-align: center;
} }
@@ -1025,7 +940,7 @@ const deliveryColumns = [
flex: 1; flex: 1;
min-height: 0; min-height: 0;
overflow: hidden; overflow: hidden;
padding: 0 8px 9px; padding: 0 6px 8px;
} }
table { table {
@@ -1046,16 +961,19 @@ td {
} }
th { th {
height: 36px; height: 44px;
color: #bde9ff; color: #bde9ff;
font-size: 14px; font-size: 18px;
font-weight: 600; font-weight: 600;
background: rgba(12, 68, 119, 0.34); background: rgba(12, 68, 119, 0.34);
} }
td { td {
height: 38px; height: 44px;
font-size: 13px; padding: 0 4px;
overflow: hidden;
font-size: 17px;
text-overflow: ellipsis;
} }
.warning td { .warning td {
@@ -1066,6 +984,6 @@ td {
.empty-row td { .empty-row td {
height: 228px; height: 228px;
color: #89d4ff; color: #89d4ff;
font-size: 18px; font-size: 22px;
} }
</style> </style>

View File

@@ -51,12 +51,14 @@
:rowspan="row.childRowspan" :rowspan="row.childRowspan"
class="subclass-cell" class="subclass-cell"
> >
<div class="subclass-content"> <div class="subclass-content">
<img class="subclass-image" :src="row.image" alt="" />
<div class="category-total subclass-total"> <div class="category-total subclass-total">
<span>{{ row.child }}</span> <span>{{ row.child }}</span>
<strong>{{ row.childTotal }}</strong> <strong>{{ row.childTotal }}</strong>
</div> </div>
<img class="subclass-image" :src="row.image" alt="" />
</div> </div>
</td> </td>
<td class="metric-cell" :class="`metric-${row.metricKey}`">{{ row.metric }}</td> <td class="metric-cell" :class="`metric-${row.metricKey}`">{{ row.metric }}</td>
@@ -144,7 +146,7 @@ const CreateData = inject('CreateData')
const ExecDatabase = inject('ExecDatabase') const ExecDatabase = inject('ExecDatabase')
const clientLogoUrl = './public/ClientLogo.png' const clientLogoUrl = './public/ClientLogo.png'
const now = ref(new Date('2026-05-06 10:10:10')) const now = ref(new Date())
const style = reactive({ const style = reactive({
width: 1920, width: 1920,
height: 1080, height: 1080,
@@ -181,7 +183,7 @@ const metricDefinitions = [
const categoryGroups = [ const categoryGroups = [
{ {
parent: '类', parent: '部件类',
children: [ children: [
{ key: 'valveMain', label: '阀', image: valveImage }, { key: 'valveMain', label: '阀', image: valveImage },
{ key: 'valveOther', label: '其他', image: otherImage } { key: 'valveOther', label: '其他', image: otherImage }
@@ -282,8 +284,9 @@ const getScale = () => ({
}) })
const startClock = () => { const startClock = () => {
now.value = new Date()
clockTimer = window.setInterval(() => { clockTimer = window.setInterval(() => {
now.value = new Date(now.value.getTime() + 1000) now.value = new Date()
}, 1000) }, 1000)
} }