Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2
This commit is contained in:
31
docs/功能测试-托盘追溯.md
Normal file
31
docs/功能测试-托盘追溯.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# 功能测试 - 托盘追溯
|
||||||
|
|
||||||
|
> 模块:数据中台 / 基础追溯 / 托盘追溯 (Tray Traceability)
|
||||||
|
> 路由:`/data_middleground/produce/traceability/tray`
|
||||||
|
|
||||||
|
## 测试前置条件
|
||||||
|
|
||||||
|
- 测试账号具备访问“托盘追溯”和“电池追溯”的菜单权限。
|
||||||
|
- 准备至少 1 个存在电池明细和工序时间线的托盘号。
|
||||||
|
- 后端接口 `tray`、`traydetail`、`batteryactive` 可正常访问。
|
||||||
|
|
||||||
|
## 测试任务列表
|
||||||
|
|
||||||
|
| 序号 | 测试项 | 操作步骤 | 预期结果 |
|
||||||
|
|---:|---|---|---|
|
||||||
|
| 1 | 页面入口 | 进入“托盘追溯”页面 | 页面显示托盘号输入框、查询、重置按钮和托盘列表 |
|
||||||
|
| 2 | 托盘查询 | 输入有效托盘号并查询 | 表格展示托盘、批次、LOT、激活状态、投入电池数和时间信息 |
|
||||||
|
| 3 | 空数据查询 | 输入无数据托盘号并查询 | 表格显示空态,页面不报错 |
|
||||||
|
| 4 | 打开电池明细 | 点击某行“电池明细” | 打开全屏抽屉,展示左侧工序时间线和右侧电池明细表 |
|
||||||
|
| 5 | 明细搜索 | 在抽屉内输入电池条码关键字 | 明细表按电池条码过滤 |
|
||||||
|
| 6 | 取消激活校验 | 不选择电池,点击取消电池激活 | 页面提示请至少选择一个电池 |
|
||||||
|
| 7 | 取消激活 | 选择一个或多个电池后点击取消电池激活 | 调用取消激活接口,成功后刷新明细 |
|
||||||
|
| 8 | 跳转电池追溯 | 点击明细中的电池条码 | 跳转到电池追溯页面并携带 battery_id 查询参数 |
|
||||||
|
| 9 | 重置功能 | 查询后点击重置 | 托盘号、列表和分页状态清空 |
|
||||||
|
| 10 | 国际化检查 | 切换中英文语言后重新进入页面 | 页面按钮、表格列和抽屉文案随语言切换显示 |
|
||||||
|
|
||||||
|
## 回归关注点
|
||||||
|
|
||||||
|
- 电池明细过滤需要排除空电池条码和 0。
|
||||||
|
- 取消激活接口参数 `batterData` 必须是已选择电池数组的 JSON 字符串。
|
||||||
|
- 电池条码跳转需保留 `battery_id` 查询参数。
|
||||||
23
docs/功能测试-电池详情报表.md
Normal file
23
docs/功能测试-电池详情报表.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# 功能测试 - 电池详情报表
|
||||||
|
|
||||||
|
> 模块:数据中台 / 生产报表 / 电池详情报表 (Battery Detail Report)
|
||||||
|
> 路由:`/data_middleground/produce/report/battery-detail`
|
||||||
|
|
||||||
|
## 测试任务列表
|
||||||
|
|
||||||
|
| 序号 | 测试项 | 操作步骤 | 预期结果 |
|
||||||
|
|---:|---|---|---|
|
||||||
|
| 1 | 页面入口 | 进入电池详情报表页面 | 页面显示工艺流程、批次、工序、托盘、时间筛选项 |
|
||||||
|
| 2 | 工艺选择 | 选择工艺流程 | 批次和工序下拉数据按工艺刷新 |
|
||||||
|
| 3 | 批次必填校验 | 不选批次点击查询 | 页面提示请选择批次 |
|
||||||
|
| 4 | 查询报表 | 选择批次和筛选条件后查询 | 表格按动态表头展示电池详情数据 |
|
||||||
|
| 5 | 分页切换 | 查询出多页数据后切换分页 | 当前页数据刷新,总数正确 |
|
||||||
|
| 6 | 导出任务 | 选择批次后点击导出并确认 | 创建导出任务成功并跳转任务页面 |
|
||||||
|
| 7 | 重置功能 | 点击重置 | 筛选项、动态表头、表格和分页清空 |
|
||||||
|
| 8 | 国际化检查 | 切换中英文语言 | 页面文案随语言切换 |
|
||||||
|
|
||||||
|
## 回归关注点
|
||||||
|
|
||||||
|
- 查询前必须先获取动态表头。
|
||||||
|
- 导出任务 action 必须为 `download`。
|
||||||
|
- 后端返回嵌套表头时需要展开为可展示列。
|
||||||
31
docs/功能测试-电池追溯.md
Normal file
31
docs/功能测试-电池追溯.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# 功能测试 - 电池追溯
|
||||||
|
|
||||||
|
> 模块:数据中台 / 基础追溯 / 电池追溯 (Battery Traceability)
|
||||||
|
> 路由:`/data_middleground/produce/traceability/battery`
|
||||||
|
|
||||||
|
## 测试前置条件
|
||||||
|
|
||||||
|
- 测试账号具备访问“电池追溯”的菜单权限。
|
||||||
|
- 准备至少 1 个存在工序过程数据的电池条码。
|
||||||
|
- 准备 1 个已激活电池和 1 个 NG 且未激活电池,用于验证操作按钮。
|
||||||
|
|
||||||
|
## 测试任务列表
|
||||||
|
|
||||||
|
| 序号 | 测试项 | 操作步骤 | 预期结果 |
|
||||||
|
|---:|---|---|---|
|
||||||
|
| 1 | 页面入口 | 进入“电池追溯”页面 | 页面显示电池条码输入框、查询、重置按钮和列表区域 |
|
||||||
|
| 2 | 电池查询 | 输入有效电池条码并查询 | 表格展示批次、托盘、LOT、激活状态、GOOD/NG、等级、不良信息、当前工序 |
|
||||||
|
| 3 | URL 参数查询 | 访问路由并携带 `?battery_id=xxx` | 页面自动按该电池条码查询 |
|
||||||
|
| 4 | 打开电池详情 | 点击某行“电池详情” | 弹出全屏详情,左侧展示工序列表,右侧展示默认工序数据 |
|
||||||
|
| 5 | 切换工序 | 在详情中点击不同工序 | 右侧工序数据按选中工序刷新 |
|
||||||
|
| 6 | 工序数据搜索 | 在详情中输入项目名称关键字 | 工序数据表按项目名称过滤 |
|
||||||
|
| 7 | 取消激活 | 对已激活电池点击取消激活并确认 | 调用取消激活接口,行状态更新为停止 |
|
||||||
|
| 8 | 复投激活 | 对 NG 且未激活电池点击复投激活并确认 | 调用 Workerman 复投接口,成功后刷新该电池数据 |
|
||||||
|
| 9 | 重置功能 | 查询后点击重置 | 电池条码、列表和分页状态清空 |
|
||||||
|
| 10 | 国际化检查 | 切换中英文语言后重新进入页面 | 页面按钮、表格列和弹窗文案随语言切换显示 |
|
||||||
|
|
||||||
|
## 回归关注点
|
||||||
|
|
||||||
|
- 工序详情接口必须携带当前行的批次、电池条码、托盘、LOT 和工序信息。
|
||||||
|
- 取消激活接口参数 `batterData` 必须是数组 JSON。
|
||||||
|
- 复投激活必须发送 `set_battery_rebatch` action。
|
||||||
22
docs/功能测试-设备履历报表.md
Normal file
22
docs/功能测试-设备履历报表.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# 功能测试 - 设备履历报表
|
||||||
|
|
||||||
|
> 模块:数据中台 / 生产报表 / 设备履历报表 (Equipment History Report)
|
||||||
|
> 路由:`/data_middleground/produce/report/equipment-history`
|
||||||
|
|
||||||
|
## 测试任务列表
|
||||||
|
|
||||||
|
| 序号 | 测试项 | 操作步骤 | 预期结果 |
|
||||||
|
|---:|---|---|---|
|
||||||
|
| 1 | 页面入口 | 进入设备履历报表页面 | 页面显示设备编码、状态、时间范围筛选项和列表 |
|
||||||
|
| 2 | 设备编码查询 | 输入有效设备编码并查询 | 表格展示设备履历记录 |
|
||||||
|
| 3 | 状态筛选 | 选择运行/空闲/异常状态查询 | 表格仅展示匹配状态数据 |
|
||||||
|
| 4 | 时间范围筛选 | 选择开始结束时间后查询 | 表格展示时间范围内履历 |
|
||||||
|
| 5 | 分页切换 | 查询出多页数据后切换分页 | 当前页数据刷新,总数正确 |
|
||||||
|
| 6 | 重置功能 | 点击重置 | 筛选项、表格和分页状态清空 |
|
||||||
|
| 7 | 空数据 | 输入无匹配条件查询 | 显示空态,不出现脚本错误 |
|
||||||
|
| 8 | 国际化检查 | 切换中英文语言 | 页面文案随语言切换 |
|
||||||
|
|
||||||
|
## 回归关注点
|
||||||
|
|
||||||
|
- 接口必须调用 `report/device/log`,method 为 `get.device.status.log`。
|
||||||
|
- 时间范围需要拆分为 `start_time` 和 `end_time`。
|
||||||
36
docs/功能测试-鹰眼.md
Normal file
36
docs/功能测试-鹰眼.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# 鹰眼功能测试任务列表
|
||||||
|
|
||||||
|
## 基础入口
|
||||||
|
|
||||||
|
- [ ] 进入 `数据中台 / 相关性分析 / 鹰眼`,页面正常加载,无控制台报错。
|
||||||
|
- [ ] 左侧分析条件区域显示生产批次、工序、NG 码三个筛选项和分析按钮。
|
||||||
|
- [ ] 页面右侧显示分析详情区域,未查询时为空状态。
|
||||||
|
|
||||||
|
## 查询条件
|
||||||
|
|
||||||
|
- [ ] 选择生产批次后,工序下拉框自动加载该批次下的不良工序列。
|
||||||
|
- [ ] 选择工序后,NG 码下拉框自动切换为该工序对应的不良代码。
|
||||||
|
- [ ] 点击重置后,批次、工序、NG 码、相关性结果和图表全部清空。
|
||||||
|
|
||||||
|
## 相关性分析
|
||||||
|
|
||||||
|
- [ ] 未选择生产批次时点击分析,系统提示需要选择批次。
|
||||||
|
- [ ] 未选择工序时点击分析,系统提示需要选择工序。
|
||||||
|
- [ ] 选择有效批次、工序和 NG 码后点击分析,Pearson 相关性散点图正常渲染。
|
||||||
|
- [ ] PCC 表格显示工序参数、样本量、相关系数、P 值和相关性判断。
|
||||||
|
- [ ] 卡方表格显示工序参数、样本量、卡方值、P 值和相关性判断。
|
||||||
|
- [ ] 有不能参与分析的数据列时,顶部折叠区域展示对应参数名称。
|
||||||
|
|
||||||
|
## 图表与交互
|
||||||
|
|
||||||
|
- [ ] 散点图鼠标悬停时显示参数名称、相关系数和 P 值。
|
||||||
|
- [ ] P 值大于 0.05 的记录以蓝色相关状态显示。
|
||||||
|
- [ ] P 值小于或等于 0.05 的记录以红色不相关状态显示。
|
||||||
|
- [ ] 已选择 NG 码时点击表格中的相关性文字,弹出分析报告弹窗。
|
||||||
|
- [ ] 分析报告弹窗内按分类展示折线分布图,关闭后再次打开能正常刷新。
|
||||||
|
|
||||||
|
## 兼容性
|
||||||
|
|
||||||
|
- [ ] 页面在 1366px 宽度下表格和图表不重叠。
|
||||||
|
- [ ] 浏览器窗口缩放后图表能自动适配。
|
||||||
|
- [ ] 切换不同批次再次分析,旧批次结果不会残留。
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
> 根据 `后台Webman界面截图对照表.md` 生成。状态以当前 V2 项目中已落地的页面目录为准。
|
> 根据 `后台Webman界面截图对照表.md` 生成。状态以当前 V2 项目中已落地的页面目录为准。
|
||||||
|
|
||||||
- 总功能数:79
|
- 总功能数:79
|
||||||
- 已迁移:30
|
- 已迁移:35
|
||||||
- 未迁移:49
|
- 未迁移:44
|
||||||
|
|
||||||
| 状态 | 一级模块 | 二级模块 | 三级模块 | 功能说明 | V2 目标路径 |
|
| 状态 | 一级模块 | 二级模块 | 三级模块 | 功能说明 | V2 目标路径 |
|
||||||
|:---:|---|---|---|---|---|
|
|:---:|---|---|---|---|---|
|
||||||
@@ -82,11 +82,11 @@
|
|||||||
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 反向追溯 (Backward Traceability) | 反向追溯 | `src/views/data-platform/traceability/backward/` |
|
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 反向追溯 (Backward Traceability) | 反向追溯 | `src/views/data-platform/traceability/backward/` |
|
||||||
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 正向追溯 (Forward Traceability) | 正向追溯 | `src/views/data-platform/traceability/forward/` |
|
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 正向追溯 (Forward Traceability) | 正向追溯 | `src/views/data-platform/traceability/forward/` |
|
||||||
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 电池曲线 (Battery Curve) | 电池曲线 | `src/views/data-platform/traceability/battery-curve/` |
|
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 电池曲线 (Battery Curve) | 电池曲线 | `src/views/data-platform/traceability/battery-curve/` |
|
||||||
| ⬜ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 托盘追溯 (Tray Traceability) | | 待确认 |
|
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 托盘追溯 (Tray Traceability) | 托盘追溯 | `src/views/data-platform/traceability/tray/` |
|
||||||
| ⬜ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 电池追溯 (Battery Traceability) | | 待确认 |
|
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 电池追溯 (Battery Traceability) | 电池追溯 | `src/views/data-platform/traceability/battery/` |
|
||||||
| ⬜ | 数据中台 (Data Platform) | 生产报表 (Production Reports) | 设备履历报表 (Equipment History Report) | | 待确认 |
|
| ✅ | 数据中台 (Data Platform) | 生产报表 (Production Reports) | 设备履历报表 (Equipment History Report) | 设备履历报表 | `src/views/data-platform/production-reports/equipment-history/` |
|
||||||
| ⬜ | 数据中台 (Data Platform) | 生产报表 (Production Reports) | 电池详情报表 (Battery Detail Report) | | 待确认 |
|
| ✅ | 数据中台 (Data Platform) | 生产报表 (Production Reports) | 电池详情报表 (Battery Detail Report) | 电池详情报表 | `src/views/data-platform/production-reports/battery-detail/` |
|
||||||
| ⬜ | 数据中台 (Data Platform) | 相关性分析 (Correlation Analysis) | 鹰眼 (Hawkeye) | | 待确认 |
|
| ✅ | 数据中台 (Data Platform) | 相关性分析 (Correlation Analysis) | 鹰眼 (Hawkeye) | 鹰眼 | `src/views/data-platform/correlation-analysis/hawkeye/` |
|
||||||
|
|
||||||
## 状态说明
|
## 状态说明
|
||||||
|
|
||||||
|
|||||||
43
src/api/data-platform/correlation-analysis/hawkeye.js
Normal file
43
src/api/data-platform/correlation-analysis/hawkeye.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { request } from '@/api/_service'
|
||||||
|
|
||||||
|
const BASE = 'data_middleground/eagle_eyes/'
|
||||||
|
|
||||||
|
function apiParams (method, data = {}) {
|
||||||
|
return {
|
||||||
|
method,
|
||||||
|
platform: 'admin',
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNGWorkstationBatch (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'getNGWorkstationBatch',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('data_middleground_eagle_eyes_getNGWorkstationBatch', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBatchResultParam (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'getBatchResultParam',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('data_middleground_eagle_eyes_getBatchResultParam', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function analyzeHawkeyeCorrelation (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'saveCsvFile',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('data_middleground_eagle_eyes_saveCsvFile', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataClassificationByNGCode (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'getDataClassificationByNGCode',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('data_middleground_eagle_eyes_get_data_classification_by_ng_code', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
23
src/api/data-platform/production-reports/battery-detail.js
Normal file
23
src/api/data-platform/production-reports/battery-detail.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { request } from '@/api/_service'
|
||||||
|
|
||||||
|
const BASE = 'planning_production/produce/battery_details_report/'
|
||||||
|
|
||||||
|
function dataParams (method, data = {}, platform = 'api') {
|
||||||
|
return { method, platform, ...data }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBatteryDetailTitle (data) {
|
||||||
|
return request({ url: BASE + 'battery_details_title', method: 'post', data: dataParams('production_report_battery_details_title', data) })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBatteryDetailList (data) {
|
||||||
|
return request({ url: BASE + 'battery_details_list', method: 'post', data: dataParams('production_report_battery_details_list', data) })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBatteryDetailFlowBatch (data) {
|
||||||
|
return request({ url: BASE + 'battery_details_flow_batch', method: 'get', params: dataParams('production_report_battery_details_flow_batch', data) })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createBatteryDetailExportTask (data) {
|
||||||
|
return request({ url: BASE + 'battery_details_task', method: 'post', data: dataParams('system_exporttask_battery_details_task', data, 'admin') })
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { request } from '@/api/_service'
|
||||||
|
|
||||||
|
const BASE = 'report/'
|
||||||
|
|
||||||
|
function apiParams (method, data = {}) {
|
||||||
|
return { method, platform: 'api', ...data }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEquipmentHistoryList (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'device/log',
|
||||||
|
method: 'post',
|
||||||
|
data: apiParams('get.device.status.log', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
31
src/api/data-platform/traceability/battery.js
Normal file
31
src/api/data-platform/traceability/battery.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { request } from '@/api/_service'
|
||||||
|
|
||||||
|
const BASE = 'planning_production/produce/traceability/'
|
||||||
|
|
||||||
|
function apiParams (method, data = {}) {
|
||||||
|
return { method, platform: 'background', ...data }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBatteryTraceList (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'battery',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('planning_production_produce_traceability_battery', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBatteryProcessData (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'batteryProcess',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('planning_production_produce_traceability_batteryProcess', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cancelBatteryActive (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'batteryactive',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('planning_production_produce_traceability_batteryactive', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
31
src/api/data-platform/traceability/tray.js
Normal file
31
src/api/data-platform/traceability/tray.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { request } from '@/api/_service'
|
||||||
|
|
||||||
|
const BASE = 'planning_production/produce/traceability/'
|
||||||
|
|
||||||
|
function apiParams (method, data = {}) {
|
||||||
|
return { method, platform: 'background', ...data }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTrayTraceList (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'tray',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('planning_production_produce_traceability_tray', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTrayTraceDetail (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'traydetail',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('planning_production_produce_traceability_traydetail', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cancelTraceBatteryActive (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'batteryactive',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('planning_production_produce_traceability_batteryactive', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1336,6 +1336,151 @@
|
|||||||
"current": "Current",
|
"current": "Current",
|
||||||
"voltage": "Voltage",
|
"voltage": "Voltage",
|
||||||
"capacity": "Capacity"
|
"capacity": "Capacity"
|
||||||
|
},
|
||||||
|
"tray": {
|
||||||
|
"query": "Search",
|
||||||
|
"reset": "Reset",
|
||||||
|
"tray_code": "Tray No.",
|
||||||
|
"tray_code_placeholder": "Enter tray no.",
|
||||||
|
"data_empty": "No data",
|
||||||
|
"id": "ID",
|
||||||
|
"tray": "Tray",
|
||||||
|
"login_batch": "Login Batch",
|
||||||
|
"lot": "LOT",
|
||||||
|
"is_active": "Active",
|
||||||
|
"active": "Active",
|
||||||
|
"inactive": "Inactive",
|
||||||
|
"input_battery_count": "Input Battery Count",
|
||||||
|
"login_time": "Login Time",
|
||||||
|
"cancel_active_time": "Cancel Active Time",
|
||||||
|
"battery_detail": "Battery Detail",
|
||||||
|
"battery_detail_data": "Battery Detail Data",
|
||||||
|
"process": "Process",
|
||||||
|
"start_time": "Start Time",
|
||||||
|
"end_time": "End Time",
|
||||||
|
"device_no": "Device No.",
|
||||||
|
"battery_id": "Battery Barcode",
|
||||||
|
"search_battery_id": "Search battery barcode",
|
||||||
|
"cancel_battery_active": "Cancel Battery Active",
|
||||||
|
"sort": "No.",
|
||||||
|
"production_batch": "Production Batch",
|
||||||
|
"model": "Model",
|
||||||
|
"process_flow_name": "Process Flow Name",
|
||||||
|
"tray_no": "Tray No.",
|
||||||
|
"activation_status": "Activation Status",
|
||||||
|
"category": "Category",
|
||||||
|
"grade": "Grade",
|
||||||
|
"please_select_at_least_one_battery": "Select at least one battery",
|
||||||
|
"cancel_success": "Cancel active successfully"
|
||||||
|
},
|
||||||
|
"battery": {
|
||||||
|
"query": "Search",
|
||||||
|
"reset": "Reset",
|
||||||
|
"battery_code": "Battery Barcode",
|
||||||
|
"enter_battery_code": "Enter battery barcode",
|
||||||
|
"login_batch": "Login Batch",
|
||||||
|
"tray_no": "Tray No.",
|
||||||
|
"lot": "LOT",
|
||||||
|
"is_active": "Active",
|
||||||
|
"activated": "Activated",
|
||||||
|
"stopped": "Stopped",
|
||||||
|
"good_or_ng": "GOOD/NG",
|
||||||
|
"grade": "Grade",
|
||||||
|
"ng_info": "NG Info",
|
||||||
|
"current_process_code": "Current Process Code",
|
||||||
|
"battery_detail": "Battery Detail",
|
||||||
|
"cancel_activation": "Cancel Active",
|
||||||
|
"reinvestment_activation": "Rework Activation",
|
||||||
|
"no_data": "No data",
|
||||||
|
"title": "Battery [{battery_id}] Traceability Detail",
|
||||||
|
"process_label": "Process",
|
||||||
|
"process_data": "Process Data",
|
||||||
|
"item_name": "Item Name",
|
||||||
|
"content": "Content",
|
||||||
|
"search_item_name": "Search item name",
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"cancel_active_confirm": "Cancel activation for this battery?",
|
||||||
|
"activation_confirm": "Execute rework activation?",
|
||||||
|
"cancel_success": "Cancel active successfully",
|
||||||
|
"activation_success": "Rework activation successfully"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"production_reports": {
|
||||||
|
"equipment_history": {
|
||||||
|
"query": "Search",
|
||||||
|
"reset": "Reset",
|
||||||
|
"device_code": "Device Code",
|
||||||
|
"enter_device_code": "Enter device code",
|
||||||
|
"device_name": "Device Name",
|
||||||
|
"status": "Status",
|
||||||
|
"select_status": "Select status",
|
||||||
|
"running": "Running",
|
||||||
|
"idle": "Idle",
|
||||||
|
"error": "Error",
|
||||||
|
"status_name": "Status Name",
|
||||||
|
"time_range": "Time Range",
|
||||||
|
"start_time": "Start Time",
|
||||||
|
"end_time": "End Time",
|
||||||
|
"duration": "Duration",
|
||||||
|
"remark": "Remark",
|
||||||
|
"no_data": "No data"
|
||||||
|
},
|
||||||
|
"battery_detail": {
|
||||||
|
"query": "Search",
|
||||||
|
"reset": "Reset",
|
||||||
|
"export": "Export",
|
||||||
|
"flow": "Process Flow",
|
||||||
|
"select_flow": "Select process flow",
|
||||||
|
"batch": "Batch",
|
||||||
|
"select_batch": "Select batch",
|
||||||
|
"process": "Process",
|
||||||
|
"select_process": "Select process",
|
||||||
|
"tray_no": "Tray No.",
|
||||||
|
"enter_tray_no": "Enter tray no.",
|
||||||
|
"time": "Completion Time",
|
||||||
|
"start_date": "Start Time",
|
||||||
|
"end_date": "End Time",
|
||||||
|
"no_data": "No data",
|
||||||
|
"please_select_batch": "Select batch",
|
||||||
|
"export_confirm": "Create battery detail export task?",
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"create_download_task_success": "Download task created successfully"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"correlation_analysis": {
|
||||||
|
"hawkeye": {
|
||||||
|
"analysis_condition": "Analysis Conditions",
|
||||||
|
"analyze": "Analyze",
|
||||||
|
"reset": "Reset",
|
||||||
|
"production_batch": "Production Batch",
|
||||||
|
"select_production_batch": "Select production batch",
|
||||||
|
"process": "Process",
|
||||||
|
"select_ng_column": "Select NG column",
|
||||||
|
"ng_code": "NG Code",
|
||||||
|
"select_ng_code": "Select NG code",
|
||||||
|
"analysis_detail": "Analysis Detail",
|
||||||
|
"correlated": "Correlated",
|
||||||
|
"not_correlated": "Not Correlated",
|
||||||
|
"correlated_short": "Related",
|
||||||
|
"not_correlated_short": "Not Related",
|
||||||
|
"no_analysis_data_hint": "Columns skipped because values are all identical or all different",
|
||||||
|
"process_param": "Process Parameter",
|
||||||
|
"sample_size": "Sample Size",
|
||||||
|
"correlation_coeff": "Correlation Coeff.",
|
||||||
|
"chi_square_value": "Chi-square Value",
|
||||||
|
"p_value": "P Value",
|
||||||
|
"correlation": "Correlation",
|
||||||
|
"p_value_hint": "P value greater than 0.05 is considered correlated",
|
||||||
|
"p_value_hint2": "P value greater than 0.05 is considered correlated",
|
||||||
|
"scientific_notation_hint": "Value may be shown in scientific notation",
|
||||||
|
"tooltip_dependent_var": "Dependent Variable",
|
||||||
|
"tooltip_corr_coeff": "Correlation Coeff.",
|
||||||
|
"tooltip_p_value": "P Value",
|
||||||
|
"analysis_report": "Analysis Report",
|
||||||
|
"total": "Total",
|
||||||
|
"no_data": "No analysis data",
|
||||||
|
"please_select_batch": "Select production batch",
|
||||||
|
"please_select_process": "Select process"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1336,6 +1336,151 @@
|
|||||||
"current": "电流",
|
"current": "电流",
|
||||||
"voltage": "电压",
|
"voltage": "电压",
|
||||||
"capacity": "容量"
|
"capacity": "容量"
|
||||||
|
},
|
||||||
|
"tray": {
|
||||||
|
"query": "查询",
|
||||||
|
"reset": "重置",
|
||||||
|
"tray_code": "托盘号",
|
||||||
|
"tray_code_placeholder": "请输入托盘号",
|
||||||
|
"data_empty": "暂无数据",
|
||||||
|
"id": "ID",
|
||||||
|
"tray": "托盘",
|
||||||
|
"login_batch": "登录批次",
|
||||||
|
"lot": "LOT",
|
||||||
|
"is_active": "是否激活",
|
||||||
|
"active": "激活",
|
||||||
|
"inactive": "未激活",
|
||||||
|
"input_battery_count": "投入电池数",
|
||||||
|
"login_time": "登录时间",
|
||||||
|
"cancel_active_time": "取消激活时间",
|
||||||
|
"battery_detail": "电池明细",
|
||||||
|
"battery_detail_data": "电池明细数据",
|
||||||
|
"process": "工序",
|
||||||
|
"start_time": "开始时间",
|
||||||
|
"end_time": "结束时间",
|
||||||
|
"device_no": "设备编号",
|
||||||
|
"battery_id": "电池条码",
|
||||||
|
"search_battery_id": "搜索电池条码",
|
||||||
|
"cancel_battery_active": "取消电池激活",
|
||||||
|
"sort": "序号",
|
||||||
|
"production_batch": "生产批次",
|
||||||
|
"model": "型号",
|
||||||
|
"process_flow_name": "工艺流程名称",
|
||||||
|
"tray_no": "托盘号",
|
||||||
|
"activation_status": "激活状态",
|
||||||
|
"category": "类别",
|
||||||
|
"grade": "等级",
|
||||||
|
"please_select_at_least_one_battery": "请至少选择一个电池",
|
||||||
|
"cancel_success": "取消激活成功"
|
||||||
|
},
|
||||||
|
"battery": {
|
||||||
|
"query": "查询",
|
||||||
|
"reset": "重置",
|
||||||
|
"battery_code": "电池条码",
|
||||||
|
"enter_battery_code": "请输入电池条码",
|
||||||
|
"login_batch": "登录批次",
|
||||||
|
"tray_no": "托盘号",
|
||||||
|
"lot": "LOT",
|
||||||
|
"is_active": "是否激活",
|
||||||
|
"activated": "已激活",
|
||||||
|
"stopped": "已停止",
|
||||||
|
"good_or_ng": "良品/不良",
|
||||||
|
"grade": "等级",
|
||||||
|
"ng_info": "不良信息",
|
||||||
|
"current_process_code": "当前工序编码",
|
||||||
|
"battery_detail": "电池详情",
|
||||||
|
"cancel_activation": "取消激活",
|
||||||
|
"reinvestment_activation": "复投激活",
|
||||||
|
"no_data": "暂无数据",
|
||||||
|
"title": "电池【{battery_id}】追溯详情",
|
||||||
|
"process_label": "工序",
|
||||||
|
"process_data": "工序数据",
|
||||||
|
"item_name": "项目名称",
|
||||||
|
"content": "内容",
|
||||||
|
"search_item_name": "搜索项目名称",
|
||||||
|
"prompt": "提示",
|
||||||
|
"cancel_active_confirm": "确认取消该电池激活?",
|
||||||
|
"activation_confirm": "确认执行复投激活?",
|
||||||
|
"cancel_success": "取消激活成功",
|
||||||
|
"activation_success": "复投激活成功"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"production_reports": {
|
||||||
|
"equipment_history": {
|
||||||
|
"query": "查询",
|
||||||
|
"reset": "重置",
|
||||||
|
"device_code": "设备编码",
|
||||||
|
"enter_device_code": "请输入设备编码",
|
||||||
|
"device_name": "设备名称",
|
||||||
|
"status": "状态",
|
||||||
|
"select_status": "请选择状态",
|
||||||
|
"running": "运行",
|
||||||
|
"idle": "空闲",
|
||||||
|
"error": "异常",
|
||||||
|
"status_name": "状态名称",
|
||||||
|
"time_range": "时间范围",
|
||||||
|
"start_time": "开始时间",
|
||||||
|
"end_time": "结束时间",
|
||||||
|
"duration": "持续时长",
|
||||||
|
"remark": "备注",
|
||||||
|
"no_data": "暂无数据"
|
||||||
|
},
|
||||||
|
"battery_detail": {
|
||||||
|
"query": "查询",
|
||||||
|
"reset": "重置",
|
||||||
|
"export": "导出",
|
||||||
|
"flow": "工艺流程",
|
||||||
|
"select_flow": "请选择工艺流程",
|
||||||
|
"batch": "批次",
|
||||||
|
"select_batch": "请选择批次",
|
||||||
|
"process": "工序",
|
||||||
|
"select_process": "请选择工序",
|
||||||
|
"tray_no": "托盘号",
|
||||||
|
"enter_tray_no": "请输入托盘号",
|
||||||
|
"time": "完成时间",
|
||||||
|
"start_date": "开始时间",
|
||||||
|
"end_date": "结束时间",
|
||||||
|
"no_data": "暂无数据",
|
||||||
|
"please_select_batch": "请选择批次",
|
||||||
|
"export_confirm": "确认创建电池详情报表导出任务?",
|
||||||
|
"prompt": "提示",
|
||||||
|
"create_download_task_success": "创建下载任务成功"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"correlation_analysis": {
|
||||||
|
"hawkeye": {
|
||||||
|
"analysis_condition": "分析条件",
|
||||||
|
"analyze": "分析",
|
||||||
|
"reset": "重置",
|
||||||
|
"production_batch": "生产批次",
|
||||||
|
"select_production_batch": "请选择生产批次",
|
||||||
|
"process": "工序",
|
||||||
|
"select_ng_column": "请选择NG列",
|
||||||
|
"ng_code": "NG码",
|
||||||
|
"select_ng_code": "请选择NG码",
|
||||||
|
"analysis_detail": "分析详情",
|
||||||
|
"correlated": "相关",
|
||||||
|
"not_correlated": "不相关",
|
||||||
|
"correlated_short": "相关",
|
||||||
|
"not_correlated_short": "不相关",
|
||||||
|
"no_analysis_data_hint": "因列中数据完全相同或完全不相同,而不进行分析的数据列",
|
||||||
|
"process_param": "工序参数",
|
||||||
|
"sample_size": "样本量",
|
||||||
|
"correlation_coeff": "相关系数",
|
||||||
|
"chi_square_value": "卡方值",
|
||||||
|
"p_value": "P值",
|
||||||
|
"correlation": "相关性",
|
||||||
|
"p_value_hint": "P值大于0.05时判定为相关",
|
||||||
|
"p_value_hint2": "P值大于0.05时判定为相关",
|
||||||
|
"scientific_notation_hint": "数值可能以科学计数法显示",
|
||||||
|
"tooltip_dependent_var": "因变量",
|
||||||
|
"tooltip_corr_coeff": "相关系数",
|
||||||
|
"tooltip_p_value": "P值",
|
||||||
|
"analysis_report": "分析报告",
|
||||||
|
"total": "总数",
|
||||||
|
"no_data": "暂无分析数据",
|
||||||
|
"please_select_batch": "请选择生产批次",
|
||||||
|
"please_select_process": "请选择工序"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,36 @@ export default {
|
|||||||
name: `${pre}traceability-curve`,
|
name: `${pre}traceability-curve`,
|
||||||
meta: { ...meta, cache: true, title: '电池曲线' },
|
meta: { ...meta, cache: true, title: '电池曲线' },
|
||||||
component: _import('data-platform/traceability/battery-curve')
|
component: _import('data-platform/traceability/battery-curve')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'produce/traceability/tray',
|
||||||
|
name: `${pre}traceability-tray`,
|
||||||
|
meta: { ...meta, cache: true, title: '托盘追溯' },
|
||||||
|
component: _import('data-platform/traceability/tray')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'produce/traceability/battery',
|
||||||
|
name: `${pre}traceability-battery`,
|
||||||
|
meta: { ...meta, cache: true, title: '电池追溯' },
|
||||||
|
component: _import('data-platform/traceability/battery')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'produce/report/equipment-history',
|
||||||
|
name: `${pre}report-equipment-history`,
|
||||||
|
meta: { ...meta, cache: true, title: '设备履历报表' },
|
||||||
|
component: _import('data-platform/production-reports/equipment-history')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'produce/report/battery-detail',
|
||||||
|
name: `${pre}report-battery-detail`,
|
||||||
|
meta: { ...meta, cache: true, title: '电池详情报表' },
|
||||||
|
component: _import('data-platform/production-reports/battery-detail')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pancake/eagle_eyes',
|
||||||
|
name: `${pre}formation-data-record`,
|
||||||
|
meta: { ...meta, cache: true, title: '鹰眼' },
|
||||||
|
component: _import('data-platform/correlation-analysis/hawkeye')
|
||||||
}
|
}
|
||||||
])('data_middleground-')
|
])('data_middleground-')
|
||||||
}
|
}
|
||||||
|
|||||||
403
src/views/data-platform/correlation-analysis/hawkeye/index.vue
Normal file
403
src/views/data-platform/correlation-analysis/hawkeye/index.vue
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
<template>
|
||||||
|
<d2-container class="hawkeye-page">
|
||||||
|
<div class="hawkeye-layout">
|
||||||
|
<aside class="condition-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<span>{{ $t(key('analysis_condition')) }}</span>
|
||||||
|
<el-button type="primary" size="mini" icon="el-icon-data-analysis" :loading="loading" @click="onAnalyze">{{ $t(key('analyze')) }}</el-button>
|
||||||
|
</div>
|
||||||
|
<el-form ref="searchForm" :model="search" label-position="top" size="mini" class="condition-form">
|
||||||
|
<el-form-item :label="$t(key('production_batch'))" prop="table">
|
||||||
|
<el-select v-model="search.table" filterable clearable :placeholder="$t(key('select_production_batch'))" @change="onBatchChange">
|
||||||
|
<el-option v-for="item in batchOptions" :key="item.id || item.batch" :label="item.batch" :value="item.batch" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t(key('process'))" prop="ng_colnum">
|
||||||
|
<el-select v-model="search.ng_colnum" filterable clearable :placeholder="$t(key('select_ng_column'))" value-key="process_code" @change="onProcessChange">
|
||||||
|
<el-option v-for="(item, index) in processOptions" :key="index" :label="formatProcessOption(item)" :value="item" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t(key('ng_code'))" prop="ng_code">
|
||||||
|
<el-select v-model="search.ng_code" filterable clearable :placeholder="$t(key('select_ng_code'))">
|
||||||
|
<el-option v-for="(item, index) in ngCodeOptions" :key="index" :label="item.explain || item.name || item.code || item" :value="item.explain || item.code || item" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" :disabled="loading" @click="onReset">{{ $t(key('reset')) }}</el-button>
|
||||||
|
</el-form>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main class="result-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<span>{{ $t(key('analysis_detail')) }}</span>
|
||||||
|
<div class="legend">
|
||||||
|
<span><i class="legend-dot is-related" />{{ $t(key('correlated')) }}</span>
|
||||||
|
<span><i class="legend-dot is-unrelated" />{{ $t(key('not_correlated')) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-collapse v-if="noCorrColumns.length" class="excluded-panel">
|
||||||
|
<el-collapse-item :title="$t(key('no_analysis_data_hint'))" name="excluded">
|
||||||
|
<el-tag v-for="item in noCorrColumns" :key="item" size="mini" type="warning">{{ getParamName(item) }}</el-tag>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
|
||||||
|
<div v-loading="loading" class="analysis-content">
|
||||||
|
<el-empty v-if="!hasAnalysis" :description="$t(key('no_data'))" :image-size="90" />
|
||||||
|
<template v-else>
|
||||||
|
<section class="chart-row">
|
||||||
|
<div ref="pccChart" class="chart-box" />
|
||||||
|
<el-table :data="tableData.pcc" size="mini" border height="100%">
|
||||||
|
<el-table-column prop="process" :label="$t(key('process_param'))" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="sample_size" :label="$t(key('sample_size'))" width="90" />
|
||||||
|
<el-table-column prop="corr_data" :label="$t(key('correlation_coeff'))" width="120" />
|
||||||
|
<el-table-column prop="pv" :label="$t(key('p_value'))" width="110" />
|
||||||
|
<el-table-column :label="$t(key('correlation'))" width="110">
|
||||||
|
<template slot="header">
|
||||||
|
{{ $t(key('correlation')) }}
|
||||||
|
<el-tooltip :content="$t(key('p_value_hint'))" placement="top">
|
||||||
|
<i class="el-icon-question" />
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" :class="relationClass(scope.row.pv)" @click="showDistribution(scope.row)">{{ relationText(scope.row.pv) }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section v-if="tableData.chi.length" class="table-section">
|
||||||
|
<el-table :data="tableData.chi" size="mini" border height="100%">
|
||||||
|
<el-table-column prop="process" :label="$t(key('process_param'))" min-width="220" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="sample_size" :label="$t(key('sample_size'))" width="120" />
|
||||||
|
<el-table-column prop="corr_data" :label="$t(key('chi_square_value'))" width="150" />
|
||||||
|
<el-table-column prop="pv" width="140">
|
||||||
|
<template slot="header">
|
||||||
|
{{ $t(key('p_value')) }}
|
||||||
|
<el-tooltip :content="$t(key('scientific_notation_hint'))" placement="top">
|
||||||
|
<i class="el-icon-question" />
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column width="120">
|
||||||
|
<template slot="header">
|
||||||
|
{{ $t(key('correlation')) }}
|
||||||
|
<el-tooltip :content="$t(key('p_value_hint2'))" placement="top">
|
||||||
|
<i class="el-icon-question" />
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" :class="relationClass(scope.row.pv)" @click="showDistribution(scope.row)">{{ relationText(scope.row.pv) }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog :title="$t(key('analysis_report'))" :visible.sync="distributionDialog.visible" width="80%" @closed="closeDistribution">
|
||||||
|
<div ref="distributionChart" class="distribution-chart" />
|
||||||
|
</el-dialog>
|
||||||
|
</d2-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import { getBatchAll } from '@/api/planning-production/batch-list'
|
||||||
|
import { analyzeHawkeyeCorrelation, getBatchResultParam, getDataClassificationByNGCode, getNGWorkstationBatch } from '@/api/data-platform/correlation-analysis/hawkeye'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'data-platform-correlation-analysis-hawkeye',
|
||||||
|
mixins: [i18nMixin('page.data_platform.correlation_analysis.hawkeye')],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
search: { table: '', ng_colnum: '', ng_code: undefined },
|
||||||
|
batchOptions: [],
|
||||||
|
processOptions: [],
|
||||||
|
ngCodeOptions: [],
|
||||||
|
processParamNames: {},
|
||||||
|
noCorrColumns: [],
|
||||||
|
tableData: { pcc: [], chi: [] },
|
||||||
|
pccChart: null,
|
||||||
|
distributionChart: null,
|
||||||
|
distributionDialog: { visible: false, data: {} }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasAnalysis () {
|
||||||
|
return this.tableData.pcc.length > 0 || this.tableData.chi.length > 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.loadBatchOptions()
|
||||||
|
window.addEventListener('resize', this.resizeCharts)
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('resize', this.resizeCharts)
|
||||||
|
if (this.pccChart) this.pccChart.dispose()
|
||||||
|
if (this.distributionChart) this.distributionChart.dispose()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
responseData (res) { return res && res.data !== undefined ? res.data : res },
|
||||||
|
async loadBatchOptions () {
|
||||||
|
const res = await getBatchAll({})
|
||||||
|
this.batchOptions = this.responseData(res) || []
|
||||||
|
},
|
||||||
|
async onBatchChange (batch) {
|
||||||
|
this.search.ng_colnum = ''
|
||||||
|
this.search.ng_code = undefined
|
||||||
|
this.processOptions = []
|
||||||
|
this.ngCodeOptions = []
|
||||||
|
this.processParamNames = {}
|
||||||
|
if (!batch) return
|
||||||
|
const [processRes, paramRes] = await Promise.all([
|
||||||
|
getNGWorkstationBatch({ batch }),
|
||||||
|
getBatchResultParam({ batch })
|
||||||
|
])
|
||||||
|
this.processOptions = this.responseData(processRes) || []
|
||||||
|
const paramData = this.responseData(paramRes) || {}
|
||||||
|
this.processParamNames = paramData.result_param_names || {}
|
||||||
|
},
|
||||||
|
onProcessChange (value) {
|
||||||
|
this.search.ng_code = undefined
|
||||||
|
this.ngCodeOptions = Array.isArray(value) ? value : []
|
||||||
|
},
|
||||||
|
formatProcessOption (item) {
|
||||||
|
const first = Array.isArray(item) ? item[0] : item
|
||||||
|
return (first && (first.name || first.process_name || first.process_code)) || ''
|
||||||
|
},
|
||||||
|
buildAnalyzeParams () {
|
||||||
|
const ngColumn = Array.isArray(this.search.ng_colnum) ? this.search.ng_colnum[0] : this.search.ng_colnum
|
||||||
|
return {
|
||||||
|
...this.search,
|
||||||
|
corr_code: [],
|
||||||
|
ng_colnum: ngColumn && ngColumn.process_code ? ngColumn.process_code : ngColumn
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async onAnalyze () {
|
||||||
|
if (!this.search.table) { this.$message.warning(this.$t(this.key('please_select_batch'))); return }
|
||||||
|
if (!this.search.ng_colnum) { this.$message.warning(this.$t(this.key('please_select_process'))); return }
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await analyzeHawkeyeCorrelation(this.buildAnalyzeParams())
|
||||||
|
this.applyAnalysisData(this.responseData(res) || {})
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
applyAnalysisData (data) {
|
||||||
|
const x2 = Array.isArray(data.X2) ? data.X2 : []
|
||||||
|
const pcc = Array.isArray(data.PCC) ? data.PCC : []
|
||||||
|
this.noCorrColumns = Array.isArray(data.no_corr_colnum) ? data.no_corr_colnum : []
|
||||||
|
this.tableData.pcc = pcc.map(item => ({
|
||||||
|
process: this.getParamName(item.independent_variable_name),
|
||||||
|
dependent_variable: item.independent_variable_name,
|
||||||
|
sample_size: this.formatNumber(item.value && item.value[3]),
|
||||||
|
corr_data: this.formatNumber(item.value && item.value[0]),
|
||||||
|
pv: item.value && item.value[2]
|
||||||
|
}))
|
||||||
|
this.tableData.chi = x2.map(item => ({
|
||||||
|
process: this.getParamName(item.independent_variable_name),
|
||||||
|
dependent_variable: item.independent_variable_name,
|
||||||
|
sample_size: this.formatNumber(item.value && item.value[0]),
|
||||||
|
corr_data: this.formatNumber(item.value && item.value[1]),
|
||||||
|
pv: item.value && item.value[2]
|
||||||
|
}))
|
||||||
|
this.$nextTick(() => this.renderPccChart(pcc))
|
||||||
|
},
|
||||||
|
renderPccChart (pcc) {
|
||||||
|
const chartDom = this.$refs.pccChart
|
||||||
|
if (!chartDom) return
|
||||||
|
if (!this.pccChart) this.pccChart = echarts.init(chartDom)
|
||||||
|
const points = pcc.map(item => ({
|
||||||
|
name: this.getParamName(item.independent_variable_name),
|
||||||
|
value: [Number(item.value && item.value[0]) || 0, Number(item.value && item.value[2]) || 0],
|
||||||
|
sampleSize: item.value && item.value[3]
|
||||||
|
}))
|
||||||
|
this.pccChart.setOption({
|
||||||
|
title: { text: this.$t(this.key('tooltip_corr_coeff')), left: 'center', textStyle: { fontSize: 14 } },
|
||||||
|
grid: { left: 52, right: 28, top: 58, bottom: 42 },
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: params => {
|
||||||
|
const item = params.data
|
||||||
|
return `${this.$t(this.key('tooltip_dependent_var'))}: ${item.name}<br>${this.$t(this.key('tooltip_corr_coeff'))}: ${this.formatNumber(item.value[0])}<br>${this.$t(this.key('tooltip_p_value'))}: ${this.formatNumber(item.value[1])}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: { type: 'value', name: this.$t(this.key('tooltip_corr_coeff')) },
|
||||||
|
yAxis: { type: 'value', name: this.$t(this.key('tooltip_p_value')) },
|
||||||
|
series: [{
|
||||||
|
type: 'scatter',
|
||||||
|
data: points,
|
||||||
|
symbolSize: 9,
|
||||||
|
itemStyle: { color: params => (Number(params.data.value[1]) > 0.05 ? '#409EFF' : '#F56C6C') }
|
||||||
|
}]
|
||||||
|
}, true)
|
||||||
|
this.pccChart.resize()
|
||||||
|
},
|
||||||
|
async showDistribution (row) {
|
||||||
|
if (!this.search.ng_code || !row.dependent_variable) return
|
||||||
|
const res = await getDataClassificationByNGCode({ ng_code: this.search.ng_code, dependent_variable: row.dependent_variable })
|
||||||
|
this.distributionDialog.data = this.responseData(res) || {}
|
||||||
|
this.distributionDialog.visible = true
|
||||||
|
this.$nextTick(this.renderDistributionChart)
|
||||||
|
},
|
||||||
|
renderDistributionChart () {
|
||||||
|
const chartDom = this.$refs.distributionChart
|
||||||
|
if (!chartDom) return
|
||||||
|
if (!this.distributionChart) this.distributionChart = echarts.init(chartDom)
|
||||||
|
const raw = Array.isArray(this.distributionDialog.data.col3) ? this.distributionDialog.data.col3 : []
|
||||||
|
const grouped = this.groupDistribution(raw)
|
||||||
|
const xAxis = grouped.values
|
||||||
|
this.distributionChart.setOption({
|
||||||
|
legend: { data: grouped.classNames, left: 10 },
|
||||||
|
tooltip: { trigger: 'axis' },
|
||||||
|
grid: { left: 48, right: 28, top: 48, bottom: 42 },
|
||||||
|
xAxis: { type: 'category', data: xAxis },
|
||||||
|
yAxis: { type: 'value', name: this.$t(this.key('total')) },
|
||||||
|
series: grouped.classNames.map(name => ({ name, type: 'line', smooth: true, symbol: 'none', data: xAxis.map(value => grouped.map[name][value] || 0) }))
|
||||||
|
}, true)
|
||||||
|
this.distributionChart.resize()
|
||||||
|
},
|
||||||
|
groupDistribution (raw) {
|
||||||
|
const classNames = Array.from(new Set(raw.map(item => item.classname))).filter(Boolean)
|
||||||
|
const values = Array.from(new Set(raw.map(item => String(item.value)))).sort((a, b) => Number(a) - Number(b))
|
||||||
|
const map = {}
|
||||||
|
classNames.forEach(name => { map[name] = {}; values.forEach(value => { map[name][value] = 0 }) })
|
||||||
|
raw.forEach(item => {
|
||||||
|
const className = item.classname
|
||||||
|
const value = String(item.value)
|
||||||
|
if (map[className] && Object.prototype.hasOwnProperty.call(map[className], value)) map[className][value] += 1
|
||||||
|
})
|
||||||
|
return { classNames, values, map }
|
||||||
|
},
|
||||||
|
getParamName (code) {
|
||||||
|
return this.processParamNames[code] || code || ''
|
||||||
|
},
|
||||||
|
relationClass (pv) {
|
||||||
|
return Number(pv) > 0.05 ? 'relation-related' : 'relation-unrelated'
|
||||||
|
},
|
||||||
|
relationText (pv) {
|
||||||
|
return Number(pv) > 0.05 ? this.$t(this.key('correlated_short')) : this.$t(this.key('not_correlated_short'))
|
||||||
|
},
|
||||||
|
formatNumber (value) {
|
||||||
|
if (value === undefined || value === null || value === '') return ''
|
||||||
|
const number = Number(value)
|
||||||
|
if (!Number.isFinite(number)) return value
|
||||||
|
return Math.abs(number) < 0.001 && number !== 0 ? number.toExponential(2) : Number(number.toFixed(4))
|
||||||
|
},
|
||||||
|
resizeCharts () {
|
||||||
|
if (this.pccChart) this.pccChart.resize()
|
||||||
|
if (this.distributionChart) this.distributionChart.resize()
|
||||||
|
},
|
||||||
|
closeDistribution () {
|
||||||
|
this.distributionDialog.data = {}
|
||||||
|
},
|
||||||
|
onReset () {
|
||||||
|
this.search = { table: '', ng_colnum: '', ng_code: undefined }
|
||||||
|
this.processOptions = []
|
||||||
|
this.ngCodeOptions = []
|
||||||
|
this.noCorrColumns = []
|
||||||
|
this.tableData = { pcc: [], chi: [] }
|
||||||
|
if (this.pccChart) this.pccChart.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.hawkeye-page {
|
||||||
|
::v-deep .d2-container-full__body { padding: 0; }
|
||||||
|
}
|
||||||
|
.hawkeye-layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 300px minmax(0, 1fr);
|
||||||
|
height: calc(100vh - 132px);
|
||||||
|
min-height: 640px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.condition-panel {
|
||||||
|
border-right: 1px solid #e5e9f2;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.panel-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
min-height: 44px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.condition-form {
|
||||||
|
padding: 14px;
|
||||||
|
::v-deep .el-select { width: 100%; }
|
||||||
|
}
|
||||||
|
.result-panel {
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.legend {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.legend-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
margin-right: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.is-related { background: #409eff; }
|
||||||
|
.is-unrelated { background: #f56c6c; }
|
||||||
|
.excluded-panel {
|
||||||
|
padding: 0 12px;
|
||||||
|
::v-deep .el-tag { margin: 0 6px 6px 0; }
|
||||||
|
}
|
||||||
|
.analysis-content {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
.chart-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(360px, 1fr) minmax(420px, 46%);
|
||||||
|
gap: 14px;
|
||||||
|
height: 360px;
|
||||||
|
min-width: 860px;
|
||||||
|
}
|
||||||
|
.chart-box {
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
.table-section {
|
||||||
|
height: 300px;
|
||||||
|
min-width: 860px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
.relation-related { color: #409eff; }
|
||||||
|
.relation-unrelated { color: #f56c6c; }
|
||||||
|
.distribution-chart {
|
||||||
|
height: 560px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.hawkeye-layout {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
.condition-panel {
|
||||||
|
border-right: 0;
|
||||||
|
border-bottom: 1px solid #e5e9f2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
<template>
|
||||||
|
<d2-container>
|
||||||
|
<template #header>
|
||||||
|
<div class="search-bar">
|
||||||
|
<el-form ref="searchForm" :inline="true" :model="search" size="mini">
|
||||||
|
<el-form-item :label="$t(key('flow'))" prop="flow_idx">
|
||||||
|
<el-select v-model="search.flow_idx" :placeholder="$t(key('select_flow'))" clearable filterable style="width:180px" @change="onFlowChange">
|
||||||
|
<el-option v-for="(item, index) in flowOptions" :key="item.id || index" :label="item.name" :value="index" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t(key('batch'))" prop="batch">
|
||||||
|
<el-select v-model="search.batch" :placeholder="$t(key('select_batch'))" multiple collapse-tags clearable filterable style="width:220px">
|
||||||
|
<el-option v-for="item in batchOptions" :key="item.id || item.batch" :label="item.batch" :value="item.batch" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t(key('process'))" prop="process_id">
|
||||||
|
<el-select v-model="search.process_id" :placeholder="$t(key('select_process'))" multiple collapse-tags clearable filterable style="width:220px">
|
||||||
|
<el-option v-for="item in processOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t(key('tray_no'))" prop="tray">
|
||||||
|
<el-input v-model.trim="search.tray" :placeholder="$t(key('enter_tray_no'))" clearable style="width:180px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t(key('time'))" prop="time">
|
||||||
|
<el-date-picker v-model="search.time" type="datetimerange" value-format="yyyy-MM-dd HH:mm:ss" :start-placeholder="$t(key('start_date'))" :end-placeholder="$t(key('end_date'))" style="width:330px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" :disabled="loading" @click="onSearch">{{ $t(key('query')) }}</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" :disabled="loading" @click="onReset">{{ $t(key('reset')) }}</el-button>
|
||||||
|
<el-button type="primary" icon="el-icon-download" :disabled="loading" @click="exportTask">{{ $t(key('export')) }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="tableData" size="mini" border height="calc(100vh - 220px)">
|
||||||
|
<el-table-column type="index" width="55" />
|
||||||
|
<el-table-column v-for="column in flatColumns" :key="column.prop" :prop="column.prop" :label="column.label" min-width="160" show-overflow-tooltip />
|
||||||
|
<template #empty><el-empty :description="$t(key('no_data'))" :image-size="80" /></template>
|
||||||
|
</el-table>
|
||||||
|
<div class="pager">
|
||||||
|
<el-pagination background layout="total, sizes, prev, pager, next, jumper" :current-page="pagination.current" :page-size="pagination.size" :total="pagination.total" @current-change="changePage" @size-change="changeSize" />
|
||||||
|
</div>
|
||||||
|
</d2-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import { createBatteryDetailExportTask, getBatteryDetailFlowBatch, getBatteryDetailList, getBatteryDetailTitle } from '@/api/data-platform/production-reports/battery-detail'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'data-platform-production-reports-battery-detail',
|
||||||
|
mixins: [i18nMixin('page.data_platform.production_reports.battery_detail')],
|
||||||
|
data () {
|
||||||
|
return { loading: false, search: { flow_idx: '', flow_id: '', batch: [], process_id: [], tray: '', time: [] }, flowOptions: [], batchOptions: [], processOptions: [], title: [], tableData: [], pagination: { current: 1, size: 10, total: 0 } }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
flatColumns () {
|
||||||
|
const result = []
|
||||||
|
const walk = list => {
|
||||||
|
;(Array.isArray(list) ? list : []).forEach(item => {
|
||||||
|
if (Array.isArray(item.child) && item.child.length) walk(item.child)
|
||||||
|
else if (item.prop) result.push({ prop: item.prop, label: item.label || item.prop })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
walk(this.title)
|
||||||
|
if (result.length) return result
|
||||||
|
const first = this.tableData[0] || {}
|
||||||
|
return Object.keys(first).slice(0, 30).map(key => ({ prop: key, label: key }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () { this.loadOptions() },
|
||||||
|
methods: {
|
||||||
|
responseData (res) { return res && res.data !== undefined ? res.data : res },
|
||||||
|
async loadOptions () {
|
||||||
|
const res = await getBatteryDetailFlowBatch({ action: 'where_data', batch: 'batch' })
|
||||||
|
this.flowOptions = this.responseData(res) || []
|
||||||
|
},
|
||||||
|
onFlowChange (index) {
|
||||||
|
const flow = this.flowOptions[index] || {}
|
||||||
|
this.search.flow_id = flow.id || ''
|
||||||
|
this.search.batch = []
|
||||||
|
this.search.process_id = []
|
||||||
|
this.batchOptions = Array.isArray(flow.batch_manage) ? flow.batch_manage : []
|
||||||
|
this.processOptions = Array.isArray(flow.process) ? flow.process : []
|
||||||
|
},
|
||||||
|
buildParams () {
|
||||||
|
const time = Array.isArray(this.search.time) ? this.search.time : []
|
||||||
|
return { ...this.search, start_time: time[0], end_time: time[1], page_no: this.pagination.current, page_size: this.pagination.size }
|
||||||
|
},
|
||||||
|
async fetchData () {
|
||||||
|
if (!this.search.batch || !this.search.batch.length) { this.$message.warning(this.$t(this.key('please_select_batch'))); return }
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const params = this.buildParams()
|
||||||
|
const titleRes = await getBatteryDetailTitle(params)
|
||||||
|
this.title = this.responseData(titleRes) || []
|
||||||
|
const res = await getBatteryDetailList({ ...params, action: 'get_data' })
|
||||||
|
const data = this.responseData(res)
|
||||||
|
const list = data && data.data && Array.isArray(data.data.data) ? data.data.data : (Array.isArray(data && data.data) ? data.data : (Array.isArray(data) ? data : []))
|
||||||
|
this.tableData = list
|
||||||
|
this.pagination.total = Number(data && data.data && data.data.count) || Number(data && data.count) || list.length
|
||||||
|
} finally { this.loading = false }
|
||||||
|
},
|
||||||
|
onSearch () { this.pagination.current = 1; this.fetchData() },
|
||||||
|
onReset () { this.search = { flow_idx: '', flow_id: '', batch: [], process_id: [], tray: '', time: [] }; this.batchOptions = []; this.processOptions = []; this.title = []; this.tableData = []; this.pagination.current = 1; this.pagination.total = 0 },
|
||||||
|
changePage (page) { this.pagination.current = page; this.fetchData() },
|
||||||
|
changeSize (size) { this.pagination.size = size; this.pagination.current = 1; this.fetchData() },
|
||||||
|
async exportTask () {
|
||||||
|
if (!this.search.batch || !this.search.batch.length) { this.$message.warning(this.$t(this.key('please_select_batch'))); return }
|
||||||
|
await this.$confirm(this.$t(this.key('export_confirm')), this.$t(this.key('prompt')), { type: 'warning' })
|
||||||
|
await createBatteryDetailExportTask({ ...this.buildParams(), action: 'download' })
|
||||||
|
this.$message.success(this.$t(this.key('create_download_task_success')))
|
||||||
|
this.$router.push({ name: 'task' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.search-bar { margin-bottom: -18px; }
|
||||||
|
.pager { padding-top: 10px; text-align: right; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
<d2-container>
|
||||||
|
<template #header>
|
||||||
|
<div class="search-bar">
|
||||||
|
<el-form ref="searchForm" :inline="true" :model="search" size="mini">
|
||||||
|
<el-form-item :label="$t(key('device_code'))" prop="device_code">
|
||||||
|
<el-input v-model.trim="search.device_code" :placeholder="$t(key('enter_device_code'))" clearable style="width:200px" @keyup.enter.native="onSearch" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t(key('status'))" prop="status">
|
||||||
|
<el-select v-model="search.status" :placeholder="$t(key('select_status'))" clearable style="width:160px">
|
||||||
|
<el-option :label="$t(key('running'))" value="running" />
|
||||||
|
<el-option :label="$t(key('idle'))" value="idle" />
|
||||||
|
<el-option :label="$t(key('error'))" value="error" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t(key('time_range'))" prop="time">
|
||||||
|
<el-date-picker v-model="search.time" type="datetimerange" value-format="yyyy-MM-dd HH:mm:ss" range-separator="-" :start-placeholder="$t(key('start_time'))" :end-placeholder="$t(key('end_time'))" style="width:330px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" :disabled="loading" @click="onSearch">{{ $t(key('query')) }}</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" :disabled="loading" @click="onReset">{{ $t(key('reset')) }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<page-table :columns="columns" :data="tableData" :loading="loading" :toolbar-buttons="[]" :row-buttons="[]" :pagination="pagination" :table-attrs="{ size: 'mini', rowKey: rowKey, highlightCurrentRow: true }" auto-height @page-change="onPageChange">
|
||||||
|
<template #empty><el-empty :description="$t(key('no_data'))" :image-size="80" /></template>
|
||||||
|
</page-table>
|
||||||
|
</d2-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useTableColumns } from '@/composables/useTableColumns'
|
||||||
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import PageTable from '@/components/page-table'
|
||||||
|
import { getEquipmentHistoryList } from '@/api/data-platform/production-reports/equipment-history'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'data-platform-production-reports-equipment-history',
|
||||||
|
components: { PageTable },
|
||||||
|
mixins: [i18nMixin('page.data_platform.production_reports.equipment_history')],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
search: { device_code: '', status: '', time: [] },
|
||||||
|
tableData: [],
|
||||||
|
pagination: { current: 1, size: 10, total: 0 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
columns () {
|
||||||
|
return useTableColumns([
|
||||||
|
{ prop: 'device_code', label: this.key('device_code'), minWidth: 160, showOverflowTooltip: true },
|
||||||
|
{ prop: 'device_name', label: this.key('device_name'), minWidth: 160, showOverflowTooltip: true },
|
||||||
|
{ prop: 'status', label: this.key('status'), minWidth: 120, showOverflowTooltip: true },
|
||||||
|
{ prop: 'status_name', label: this.key('status_name'), minWidth: 140, showOverflowTooltip: true },
|
||||||
|
{ prop: 'start_time', label: this.key('start_time'), minWidth: 170, showOverflowTooltip: true },
|
||||||
|
{ prop: 'end_time', label: this.key('end_time'), minWidth: 170, showOverflowTooltip: true },
|
||||||
|
{ prop: 'duration', label: this.key('duration'), minWidth: 120, showOverflowTooltip: true },
|
||||||
|
{ prop: 'remark', label: this.key('remark'), minWidth: 220, showOverflowTooltip: true }
|
||||||
|
], { selectionWidth: 0, indexWidth: 55 })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
rowKey (row) {
|
||||||
|
return row.id || [row.device_code, row.start_time, row.end_time].filter(Boolean).join('-')
|
||||||
|
},
|
||||||
|
responseData (res) {
|
||||||
|
return res && res.data !== undefined ? res.data : res
|
||||||
|
},
|
||||||
|
buildParams () {
|
||||||
|
const time = Array.isArray(this.search.time) ? this.search.time : []
|
||||||
|
return { ...this.search, start_time: time[0], end_time: time[1], page_no: this.pagination.current, page_size: this.pagination.size }
|
||||||
|
},
|
||||||
|
async fetchData () {
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await getEquipmentHistoryList(this.buildParams())
|
||||||
|
const data = this.responseData(res)
|
||||||
|
const list = Array.isArray(data) ? data : (Array.isArray(data && data.data) ? data.data : [])
|
||||||
|
this.tableData = list
|
||||||
|
this.pagination.total = Number(data && data.count) || list.length
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSearch () {
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
onReset () {
|
||||||
|
this.search = { device_code: '', status: '', time: [] }
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.tableData = []
|
||||||
|
this.pagination.total = 0
|
||||||
|
},
|
||||||
|
onPageChange (page) {
|
||||||
|
this.pagination = { ...this.pagination, ...page }
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.search-bar { margin-bottom: -18px; }
|
||||||
|
</style>
|
||||||
222
src/views/data-platform/traceability/battery/index.vue
Normal file
222
src/views/data-platform/traceability/battery/index.vue
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
<template>
|
||||||
|
<d2-container>
|
||||||
|
<template #header>
|
||||||
|
<div class="search-bar">
|
||||||
|
<el-form ref="searchForm" :inline="true" :model="search" size="mini">
|
||||||
|
<el-form-item :label="$t(key('battery_code'))" prop="battery_id">
|
||||||
|
<el-input v-model.trim="search.battery_id" :placeholder="$t(key('enter_battery_code'))" clearable style="width:240px" @keyup.enter.native="fetchData" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" :disabled="loading" @click="fetchData">{{ $t(key('query')) }}</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" :disabled="loading" @click="resetSearch">{{ $t(key('reset')) }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<page-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:toolbar-buttons="[]"
|
||||||
|
:row-buttons="rowButtons"
|
||||||
|
:pagination="pagination"
|
||||||
|
:table-attrs="{ size: 'mini', rowKey: 'battery_id', highlightCurrentRow: true }"
|
||||||
|
auto-height
|
||||||
|
@page-change="onPageChange"
|
||||||
|
>
|
||||||
|
<template #col-active="{ row }">
|
||||||
|
<el-tag :type="Number(row.active) === 1 ? 'success' : 'warning'" size="mini">{{ Number(row.active) === 1 ? $t(key('activated')) : $t(key('stopped')) }}</el-tag>
|
||||||
|
</template>
|
||||||
|
<template #col-class="{ row }">
|
||||||
|
<el-tag v-if="row.class === 'GOOD'" type="success" size="mini">GOOD</el-tag>
|
||||||
|
<el-tag v-else-if="row.class === 'NG'" type="warning" size="mini">NG</el-tag>
|
||||||
|
<span v-else>{{ row.class || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty :description="$t(key('no_data'))" :image-size="80" />
|
||||||
|
</template>
|
||||||
|
</page-table>
|
||||||
|
|
||||||
|
<el-dialog :visible.sync="detailVisible" :show-close="false" fullscreen append-to-body>
|
||||||
|
<div slot="title">
|
||||||
|
<el-page-header @back="closeDetail" :content="detailTitle" />
|
||||||
|
</div>
|
||||||
|
<div class="detail-layout">
|
||||||
|
<aside class="process-list">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div slot="header">{{ $t(key('process_label')) }}</div>
|
||||||
|
<el-button
|
||||||
|
v-for="(item, index) in processList"
|
||||||
|
:key="index"
|
||||||
|
class="process-button"
|
||||||
|
:type="activeProcessIndex === index ? 'primary' : 'default'"
|
||||||
|
@click="selectProcess(index)"
|
||||||
|
>
|
||||||
|
{{ item.process_name || item.process_code || '-' }}
|
||||||
|
</el-button>
|
||||||
|
</el-card>
|
||||||
|
</aside>
|
||||||
|
<main class="process-detail">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<div slot="header">【{{ activeProcessName }}】{{ $t(key('process_data')) }}</div>
|
||||||
|
<el-input v-model.trim="detailKeyword" size="mini" clearable :placeholder="$t(key('search_item_name'))" class="detail-search" />
|
||||||
|
<el-table :data="filteredGridData" border size="mini" height="calc(100vh - 230px)">
|
||||||
|
<el-table-column type="index" width="60" />
|
||||||
|
<el-table-column prop="name" :label="$t(key('item_name'))" min-width="180" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="content" :label="$t(key('content'))" min-width="220" show-overflow-tooltip />
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</d2-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useTableColumns } from '@/composables/useTableColumns'
|
||||||
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import PageTable from '@/components/page-table'
|
||||||
|
import { sendWorkerman } from '@/api/production-master-data/workerman'
|
||||||
|
import { cancelBatteryActive, getBatteryProcessData, getBatteryTraceList } from '@/api/data-platform/traceability/battery'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'data-platform-traceability-battery',
|
||||||
|
components: { PageTable },
|
||||||
|
mixins: [i18nMixin('page.data_platform.traceability.battery')],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
search: { battery_id: this.$route.query.battery_id || '' },
|
||||||
|
tableData: [],
|
||||||
|
pagination: { current: 1, size: 10, total: 0 },
|
||||||
|
detailVisible: false,
|
||||||
|
activeBattery: {},
|
||||||
|
processList: [],
|
||||||
|
activeProcessIndex: -1,
|
||||||
|
gridData: [],
|
||||||
|
detailKeyword: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
columns () {
|
||||||
|
return useTableColumns([
|
||||||
|
{ prop: 'battery_id', label: this.key('battery_code'), minWidth: 180, showOverflowTooltip: true },
|
||||||
|
{ prop: 'batch', label: this.key('login_batch'), minWidth: 140, showOverflowTooltip: true },
|
||||||
|
{ prop: 'tray', label: this.key('tray_no'), minWidth: 130, showOverflowTooltip: true },
|
||||||
|
{ prop: 'lot', label: this.key('lot'), minWidth: 120, showOverflowTooltip: true },
|
||||||
|
{ prop: 'active', label: this.key('is_active'), minWidth: 110, slot: 'active' },
|
||||||
|
{ prop: 'class', label: this.key('good_or_ng'), minWidth: 110, slot: 'class' },
|
||||||
|
{ prop: 'classname', label: this.key('grade'), minWidth: 120, showOverflowTooltip: true },
|
||||||
|
{ prop: 'explain', label: this.key('ng_info'), minWidth: 160, showOverflowTooltip: true },
|
||||||
|
{ prop: 'next_process_code', label: this.key('current_process_code'), minWidth: 160, showOverflowTooltip: true }
|
||||||
|
], { selectionWidth: 0, indexWidth: 55, operationWidth: 260 })
|
||||||
|
},
|
||||||
|
rowButtons () {
|
||||||
|
return [
|
||||||
|
{ key: 'detail', label: this.key('battery_detail'), type: 'primary', icon: 'el-icon-document', size: 'mini', onClick: this.openDetail },
|
||||||
|
{ key: 'cancel', label: this.key('cancel_activation'), type: 'warning', icon: 'el-icon-close', size: 'mini', visible: row => Number(row.active) === 1, onClick: this.cancelActive },
|
||||||
|
{ key: 'rebatch', label: this.key('reinvestment_activation'), type: 'success', icon: 'el-icon-refresh-right', size: 'mini', visible: row => Number(row.active) === 0 && row.class === 'NG', onClick: this.reinvestmentActivation }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
detailTitle () {
|
||||||
|
return this.$t(this.key('title'), { battery_id: this.activeBattery.battery_id || '' })
|
||||||
|
},
|
||||||
|
activeProcessName () {
|
||||||
|
const item = this.processList[this.activeProcessIndex] || {}
|
||||||
|
return item.process_name || item.process_code || '-'
|
||||||
|
},
|
||||||
|
filteredGridData () {
|
||||||
|
const keyword = String(this.detailKeyword || '').toLowerCase()
|
||||||
|
return this.gridData.filter(item => !keyword || String(item.name || '').toLowerCase().includes(keyword))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route.query.battery_id' (value) {
|
||||||
|
if (value) {
|
||||||
|
this.search.battery_id = value
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (this.search.battery_id) this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
responseData (res) { return res && res.data !== undefined ? res.data : res },
|
||||||
|
async fetchData () {
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await getBatteryTraceList({ ...this.search, action: 'battery_traceability_search', page_no: this.pagination.current, page_size: this.pagination.size })
|
||||||
|
const data = this.responseData(res)
|
||||||
|
this.tableData = Array.isArray(data) ? data : []
|
||||||
|
this.pagination.total = Number(data && data.count) || this.tableData.length
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetSearch () {
|
||||||
|
this.search.battery_id = ''
|
||||||
|
this.tableData = []
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.pagination.total = 0
|
||||||
|
},
|
||||||
|
onPageChange (page) {
|
||||||
|
this.pagination = { ...this.pagination, ...page }
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
openDetail (row) {
|
||||||
|
this.activeBattery = row
|
||||||
|
this.processList = Array.isArray(row.process) ? row.process : []
|
||||||
|
this.activeProcessIndex = -1
|
||||||
|
this.gridData = []
|
||||||
|
this.detailKeyword = ''
|
||||||
|
this.detailVisible = true
|
||||||
|
if (this.processList.length) this.selectProcess(0)
|
||||||
|
},
|
||||||
|
closeDetail () {
|
||||||
|
this.detailVisible = false
|
||||||
|
},
|
||||||
|
async selectProcess (index) {
|
||||||
|
const process = this.processList[index]
|
||||||
|
if (!process) return
|
||||||
|
this.activeProcessIndex = index
|
||||||
|
const res = await getBatteryProcessData({
|
||||||
|
process_code: process.process_code,
|
||||||
|
process_id: process.process_id,
|
||||||
|
batch: this.activeBattery.batch,
|
||||||
|
battery_id: this.activeBattery.battery_id,
|
||||||
|
tray: this.activeBattery.tray,
|
||||||
|
lot: this.activeBattery.lot,
|
||||||
|
action: 'get_one_battery_data'
|
||||||
|
})
|
||||||
|
const data = this.responseData(res)
|
||||||
|
this.gridData = Array.isArray(data) ? data : []
|
||||||
|
},
|
||||||
|
async cancelActive (row) {
|
||||||
|
await this.$confirm(this.$t(this.key('cancel_active_confirm')), this.$t(this.key('prompt')), { type: 'warning' })
|
||||||
|
const batterData = [{ batch: row.batch, tray: row.tray, lot: row.lot, battery_id: row.battery_id }]
|
||||||
|
await cancelBatteryActive({ batterData: JSON.stringify(batterData) })
|
||||||
|
row.active = 0
|
||||||
|
this.$message.success(this.$t(this.key('cancel_success')))
|
||||||
|
},
|
||||||
|
async reinvestmentActivation (row) {
|
||||||
|
await this.$confirm(this.$t(this.key('activation_confirm')), this.$t(this.key('prompt')), { type: 'warning' })
|
||||||
|
const sendData = { action: 'set_battery_rebatch', param: { battery_ids: [row.battery_id] } }
|
||||||
|
await sendWorkerman({ sendData })
|
||||||
|
this.$message.success(this.$t(this.key('activation_success')))
|
||||||
|
this.search.battery_id = row.battery_id
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.search-bar { margin-bottom: -18px; }
|
||||||
|
.detail-layout { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 16px; }
|
||||||
|
.process-list { max-height: calc(100vh - 150px); overflow: auto; }
|
||||||
|
.process-button { display: block; width: 100%; margin: 0 0 10px; }
|
||||||
|
.process-detail { min-width: 0; }
|
||||||
|
.detail-search { width: 260px; margin-bottom: 12px; }
|
||||||
|
</style>
|
||||||
199
src/views/data-platform/traceability/tray/index.vue
Normal file
199
src/views/data-platform/traceability/tray/index.vue
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
<template>
|
||||||
|
<d2-container>
|
||||||
|
<template #header>
|
||||||
|
<div class="search-bar">
|
||||||
|
<el-form ref="searchForm" :inline="true" :model="search" size="mini">
|
||||||
|
<el-form-item :label="$t(key('tray_code'))" prop="tray_id">
|
||||||
|
<el-input v-model.trim="search.tray_id" :placeholder="$t(key('tray_code_placeholder'))" clearable style="width:220px" @keyup.enter.native="fetchData" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" :disabled="loading" @click="fetchData">{{ $t(key('query')) }}</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" :disabled="loading" @click="resetSearch">{{ $t(key('reset')) }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<page-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:toolbar-buttons="[]"
|
||||||
|
:row-buttons="rowButtons"
|
||||||
|
:pagination="pagination"
|
||||||
|
:table-attrs="{ size: 'mini', rowKey: 'id', highlightCurrentRow: true }"
|
||||||
|
auto-height
|
||||||
|
@page-change="onPageChange"
|
||||||
|
>
|
||||||
|
<template #col-active="{ row }">
|
||||||
|
<el-tag :type="Number(row.active) === 1 ? 'success' : 'info'" size="mini">
|
||||||
|
{{ Number(row.active) === 1 ? $t(key('active')) : $t(key('inactive')) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty :description="$t(key('data_empty'))" :image-size="80" />
|
||||||
|
</template>
|
||||||
|
</page-table>
|
||||||
|
|
||||||
|
<el-drawer :visible.sync="detailVisible" :with-header="false" size="100%" append-to-body>
|
||||||
|
<div class="drawer-header">
|
||||||
|
<el-page-header @back="detailVisible = false" :content="$t(key('battery_detail_data'))" />
|
||||||
|
</div>
|
||||||
|
<div class="detail-layout">
|
||||||
|
<aside v-if="timeLine.length" class="detail-timeline">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item v-for="(item, index) in timeLine" :key="index">
|
||||||
|
<p>{{ $t(key('process')) }}: {{ item.process_name || '-' }}</p>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<p>{{ $t(key('start_time')) }}: {{ item.beginTime || '-' }}</p>
|
||||||
|
<p>{{ $t(key('end_time')) }}: {{ item.endTime || '-' }}</p>
|
||||||
|
<p>{{ $t(key('device_no')) }}: {{ item.device_code || '-' }}</p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</aside>
|
||||||
|
<main class="detail-main">
|
||||||
|
<el-form :inline="true" :model="detailSearch" size="mini">
|
||||||
|
<el-form-item :label="$t(key('battery_id'))">
|
||||||
|
<el-input v-model.trim="detailSearch.battery_id" :placeholder="$t(key('search_battery_id'))" clearable style="width:220px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-close" @click="cancelBatteryActive">{{ $t(key('cancel_battery_active')) }}</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table :data="filteredDetails" border size="mini" height="calc(100vh - 170px)" @selection-change="selection = $event">
|
||||||
|
<el-table-column type="selection" width="48" />
|
||||||
|
<el-table-column prop="id" :label="$t(key('sort'))" width="70" />
|
||||||
|
<el-table-column prop="battery_id" :label="$t(key('battery_id'))" min-width="180" show-overflow-tooltip>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-link type="primary" @click="goBatteryTrace(scope.row.battery_id)">{{ scope.row.battery_id }}</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="batch" :label="$t(key('production_batch'))" min-width="140" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="model" :label="$t(key('model'))" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="flow_name" :label="$t(key('process_flow_name'))" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="tray" :label="$t(key('tray_no'))" min-width="130" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="lot" :label="$t(key('lot'))" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="active" :label="$t(key('activation_status'))" min-width="120" />
|
||||||
|
<el-table-column prop="class" :label="$t(key('category'))" min-width="120" />
|
||||||
|
<el-table-column prop="classname" :label="$t(key('grade'))" min-width="120" />
|
||||||
|
</el-table>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
</d2-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useTableColumns } from '@/composables/useTableColumns'
|
||||||
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import PageTable from '@/components/page-table'
|
||||||
|
import { cancelTraceBatteryActive, getTrayTraceDetail, getTrayTraceList } from '@/api/data-platform/traceability/tray'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'data-platform-traceability-tray',
|
||||||
|
components: { PageTable },
|
||||||
|
mixins: [i18nMixin('page.data_platform.traceability.tray')],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
detailLoading: false,
|
||||||
|
search: { tray_id: this.$route.query.tray || '' },
|
||||||
|
tableData: [],
|
||||||
|
pagination: { current: 1, size: 10, total: 0 },
|
||||||
|
detailVisible: false,
|
||||||
|
activeTrayId: '',
|
||||||
|
detailData: [],
|
||||||
|
timeLine: [],
|
||||||
|
detailSearch: { battery_id: '' },
|
||||||
|
selection: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
columns () {
|
||||||
|
return useTableColumns([
|
||||||
|
{ prop: 'id', label: this.key('id'), width: 90, sortable: 'custom' },
|
||||||
|
{ prop: 'tray', label: this.key('tray'), minWidth: 140, showOverflowTooltip: true },
|
||||||
|
{ prop: 'batch', label: this.key('login_batch'), minWidth: 140, showOverflowTooltip: true },
|
||||||
|
{ prop: 'lot', label: this.key('lot'), minWidth: 120, showOverflowTooltip: true },
|
||||||
|
{ prop: 'active', label: this.key('is_active'), minWidth: 110, slot: 'active' },
|
||||||
|
{ prop: 'input_battery_count', label: this.key('input_battery_count'), minWidth: 140 },
|
||||||
|
{ prop: 'create_time', label: this.key('login_time'), minWidth: 170, showOverflowTooltip: true },
|
||||||
|
{ prop: 'cancel_active_time', label: this.key('cancel_active_time'), minWidth: 170, showOverflowTooltip: true }
|
||||||
|
], { selectionWidth: 0, indexWidth: 55, operationWidth: 130 })
|
||||||
|
},
|
||||||
|
rowButtons () {
|
||||||
|
return [{ key: 'detail', label: this.key('battery_detail'), type: 'primary', icon: 'el-icon-document', size: 'mini', onClick: this.openDetail }]
|
||||||
|
},
|
||||||
|
filteredDetails () {
|
||||||
|
const keyword = String(this.detailSearch.battery_id || '').toLowerCase()
|
||||||
|
return this.detailData.filter(item => !keyword || String(item.battery_id || '').toLowerCase().includes(keyword))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (this.search.tray_id) this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
responseData (res) { return res && res.data !== undefined ? res.data : res },
|
||||||
|
async fetchData () {
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await getTrayTraceList({ ...this.search, page_no: this.pagination.current, page_size: this.pagination.size })
|
||||||
|
const data = this.responseData(res)
|
||||||
|
this.tableData = Array.isArray(data) ? data : []
|
||||||
|
this.pagination.total = Number(data && data.count) || this.tableData.length
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetSearch () {
|
||||||
|
this.search.tray_id = ''
|
||||||
|
this.tableData = []
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.pagination.total = 0
|
||||||
|
},
|
||||||
|
onPageChange (page) {
|
||||||
|
this.pagination = { ...this.pagination, ...page }
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
async openDetail (row) {
|
||||||
|
this.activeTrayId = row.id
|
||||||
|
this.detailVisible = true
|
||||||
|
await this.loadDetail()
|
||||||
|
},
|
||||||
|
async loadDetail () {
|
||||||
|
if (!this.activeTrayId) return
|
||||||
|
this.detailLoading = true
|
||||||
|
try {
|
||||||
|
const res = await getTrayTraceDetail({ tray_id: this.activeTrayId })
|
||||||
|
const data = this.responseData(res) || {}
|
||||||
|
this.detailData = Array.isArray(data.data) ? data.data.filter(item => item.battery_id !== 0 && item.battery_id !== '') : []
|
||||||
|
this.timeLine = Array.isArray(data.date_log) ? data.date_log : []
|
||||||
|
} finally {
|
||||||
|
this.detailLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async cancelBatteryActive () {
|
||||||
|
if (!this.selection.length) {
|
||||||
|
this.$message.error(this.$t(this.key('please_select_at_least_one_battery')))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await cancelTraceBatteryActive({ batterData: JSON.stringify(this.selection) })
|
||||||
|
this.$message.success(this.$t(this.key('cancel_success')))
|
||||||
|
this.loadDetail()
|
||||||
|
},
|
||||||
|
goBatteryTrace (batteryId) {
|
||||||
|
this.detailVisible = false
|
||||||
|
this.$router.push({ path: '/data_middleground/produce/traceability/battery', query: { battery_id: batteryId } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.search-bar { margin-bottom: -18px; }
|
||||||
|
.drawer-header { padding: 20px 24px; border-bottom: 1px solid #ebeef5; }
|
||||||
|
.detail-layout { display: grid; grid-template-columns: 320px minmax(0, 1fr); height: calc(100vh - 73px); }
|
||||||
|
.detail-timeline { overflow: auto; padding: 18px; background: #f5f7fa; border-right: 1px solid #ebeef5; }
|
||||||
|
.detail-main { min-width: 0; padding: 16px; }
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user