Compare commits

..

108 Commits

Author SHA1 Message Date
sheng
8a6702f48e 迁移实时采集监控 2026-06-23 16:32:16 +08:00
sheng
4b5096e673 迁移前置服务日志 2026-06-23 16:32:16 +08:00
sheng
2d96f9838b 迁移服务监控 2026-06-23 16:32:16 +08:00
sheng
5b607be575 迁移服务配置 2026-06-23 16:32:16 +08:00
sheng
562f8fd575 迁移车间点位管理 2026-06-23 16:32:15 +08:00
sheng
30fce5711d 迁移车间配置 2026-06-23 16:32:15 +08:00
sheng
6e51ce95f8 迁移环控数据查询 2026-06-23 16:31:54 +08:00
sheng
27c0b75479 迁移环控节点配置 2026-06-23 16:31:54 +08:00
sheng
c3c0c86de6 迁移节点设备映射 2026-06-23 16:31:54 +08:00
sheng
e6f373b572 迁移SCADA数据查询 2026-06-23 16:31:54 +08:00
sheng
1d890be85d 迁移SCADA节点配置 2026-06-23 16:31:42 +08:00
sheng
e11366ab0c chore: 清理旧测试文档并更新功能统计
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
1. 删除了20+份功能测试流程文档
2. 更新了功能统计文档,新增SCADA管理相关未迁移功能
3. 新增了后台接口地址环境变量配置
2026-06-23 15:48:47 +08:00
sheng
e60c004b3c feat: 新增多模块功能并完善功能清单
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
1. 新增BOM物料清单、班组管理、班次管理、排班日历、监控设置页面及对应路由
2. 新增相关业务API接口
3. 完善多语言国际化配置
4. 更新功能迁移状态清单,完成所有功能迁移
5. 新增各模块测试用例文档
2026-06-23 10:39:42 +08:00
sheng
8ef94ecf2e 迁移排班日历功能
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
2026-06-22 23:39:45 +08:00
sheng
e8e79fcb3f 迁移班次管理功能 2026-06-22 23:32:58 +08:00
sheng
c670488c80 迁移班组管理功能 2026-06-22 23:23:06 +08:00
sheng
cf90981c5d 迁移BOM物料清单功能 2026-06-22 23:10:00 +08:00
sheng
964cf0b6ad 迁移监控设置功能 2026-06-22 23:00:27 +08:00
sheng
8ee13f322f Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 22:46:57 +08:00
sheng
7cf7caf31f 新增工艺流程参数与补偿配置
Some checks failed
Release pipeline / Always run job (push) Has been cancelled
Release pipeline / publish (push) Has been cancelled
2026-06-22 22:44:45 +08:00
sheng
795d4d3b58 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 19:49:35 +08:00
sheng
c1e8626289 迁移质量管理UChart报表
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
2026-06-22 19:33:09 +08:00
sheng
dcc706308b 迁移质量管理CChart报表 2026-06-22 19:32:47 +08:00
sheng
f40ff30943 迁移质量管理NPChart报表 2026-06-22 19:32:25 +08:00
sheng
5d1fbcc508 迁移质量管理PChart报表 2026-06-22 19:32:03 +08:00
sheng
5a4001ddf7 迁移质量管理DPMO报表 2026-06-22 19:31:42 +08:00
sheng
9af7dbf668 迁移质量管理CPK报表 2026-06-22 19:31:20 +08:00
sheng
d984ea6107 迁移质量管理MAMS报表 2026-06-22 19:30:58 +08:00
sheng
f817714c56 迁移质量管理MAMR报表 2026-06-22 19:30:36 +08:00
sheng
f625f3063d 迁移质量管理MA报表 2026-06-22 19:30:15 +08:00
sheng
9c54df9600 迁移质量管理CUSUM报表 2026-06-22 19:29:53 +08:00
sheng
7c48d0b7c7 迁移质量管理EWMA报表 2026-06-22 19:29:32 +08:00
sheng
ffead080ad 迁移质量管理Levey-Jennings报表 2026-06-22 19:29:10 +08:00
sheng
2739415e35 迁移质量管理I-MR报表 2026-06-22 19:28:49 +08:00
sheng
54ba033db6 迁移质量管理XBar-S报表 2026-06-22 19:28:28 +08:00
sheng
9f349d325a 迁移质量管理XBar-R报表 2026-06-22 19:28:05 +08:00
sheng
cc7e06fdb1 迁移质量管理SPC渲染条件配置 2026-06-22 19:27:43 +08:00
sheng
15105bc5c7 迁移质量管理抽样方案配置 2026-06-22 19:27:19 +08:00
sheng
a9c88eb264 迁移质量管理检验项目 2026-06-22 19:26:52 +08:00
sheng
7125bc4884 迁移质量管理检测方案维护 2026-06-22 19:26:29 +08:00
sheng
19f8f18f85 迁移质量管理接收质量限 2026-06-22 19:26:07 +08:00
sheng
9675227702 迁移质量管理检验标准 2026-06-22 19:25:45 +08:00
sheng
e1111fe6df 迁移质量管理检验单管理 2026-06-22 19:25:23 +08:00
sheng
e9faea739a 迁移质量管理首巡检报表 2026-06-22 19:25:01 +08:00
sheng
be119060f0 迁移质量管理首巡检录入 2026-06-22 19:24:39 +08:00
sheng
1e2945d4a3 迁移质量管理首巡检项目配置 2026-06-22 19:24:18 +08:00
sheng
bf493321cb 迁移质量管理检验类别管理 2026-06-22 19:23:56 +08:00
sheng
92de685b9d Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 19:04:37 +08:00
sheng
2690204ec5 按组件规范重构设备模型页面
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
2026-06-22 19:03:44 +08:00
sheng
700da7c42a Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 18:47:03 +08:00
sheng
8eaa5f90ca 修正设备维修日志迁移状态
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
2026-06-22 18:26:25 +08:00
sheng
25bd394f96 迁移设备损耗品更换日志模块 2026-06-22 18:25:25 +08:00
sheng
80ef16f3e5 迁移设备损耗品寿命管理模块 2026-06-22 18:25:04 +08:00
sheng
86313d309f 迁移设备损耗品项目模块 2026-06-22 18:24:42 +08:00
sheng
6a664277a5 迁移设备损耗品类别模块 2026-06-22 18:24:21 +08:00
sheng
19050966b4 迁移设备维修日志模块 2026-06-22 18:24:00 +08:00
sheng
c58525b284 迁移设备维修管理模块 2026-06-22 18:23:39 +08:00
sheng
bf1365c1ea 迁移设备保养日志模块 2026-06-22 18:20:51 +08:00
sheng
8d1be42ce2 迁移设备保养详情模块 2026-06-22 18:19:57 +08:00
sheng
0740f82083 迁移设备保养项目模块 2026-06-22 18:19:35 +08:00
sheng
4714bbabc5 迁移设备点检日志模块 2026-06-22 18:19:14 +08:00
sheng
8e5127ea88 迁移设备点检记录模块 2026-06-22 18:18:52 +08:00
sheng
f2ffac4a06 迁移设备点检项目模块 2026-06-22 18:16:26 +08:00
sheng
41bbe3bf62 迁移设备信息模块 2026-06-22 18:12:51 +08:00
sheng
3412ae93a3 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 17:55:56 +08:00
sheng
6bb98edca5 chore(package): 更新node版本和js-cookie依赖
将node引擎要求调整为>=20.19.0,升级js-cookie到3.0.7版本,同步更新volta配置的node版本
2026-06-22 17:55:50 +08:00
sheng
c07f95a45e 迁移鹰眼模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
2026-06-22 17:51:29 +08:00
sheng
973c780bf6 迁移电池详情报表模块 2026-06-22 17:44:16 +08:00
sheng
6768eb9ead 迁移设备履历报表模块 2026-06-22 17:40:40 +08:00
sheng
a1716b0069 迁移电池追溯模块 2026-06-22 17:36:22 +08:00
sheng
15526ca6e4 迁移托盘追溯模块 2026-06-22 17:32:32 +08:00
sheng
d582f9779a Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 17:30:56 +08:00
sheng
2bd0e49bb6 fix: 修复依赖版本和接口路径错误
1. 将sass版本从^1.54.5改为固定1.54.5并更新lock文件
2. 修正团队管理页面的用户接口导入路径
2026-06-22 17:30:53 +08:00
sheng
264474eced 迁移电池曲线模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
2026-06-22 17:29:23 +08:00
sheng
0c6c4dc93c Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 17:24:20 +08:00
sheng
fe45390997 迁移正向追溯模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
2026-06-22 17:22:00 +08:00
sheng
a1e79dcdcf Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 17:00:18 +08:00
sheng
3c4a665dc3 修复反向追溯图谱迁移问题
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
2026-06-22 16:57:37 +08:00
sheng
94fcb85e5b Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 16:35:23 +08:00
sheng
7987d9a085 迁移反向追溯模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
- 新增数据中台反向追溯 V2 页面

- 新增反向追溯查询导出接口、旧路径路由和中英文文案

- 更新迁移任务列表并补充人工功能测试清单
2026-06-22 16:33:18 +08:00
sheng
5fa4043c69 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 16:27:16 +08:00
sheng
e3fd31e33d 迁移电池复投管理模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
- 新增计划与生产电池复投管理 V2 页面

- 新增复投验证接口、旧路径路由和中英文文案

- 更新迁移任务列表并补充人工功能测试清单
2026-06-22 16:26:05 +08:00
sheng
d7aee0f93a Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 16:17:23 +08:00
sheng
9863bf1113 迁移物料监控模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
- 新增计划与生产物料监控 V2 页面

- 新增 WIP 物料监控接口、旧路径路由和中英文文案

- 更新迁移任务列表中的物料监控状态
2026-06-22 16:12:59 +08:00
sheng
cffbe39e99 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 16:08:08 +08:00
sheng
6bb2d1285c 迁移托盘登录模块
Some checks failed
Release pipeline / Always run job (push) Has been cancelled
Release pipeline / publish (push) Has been cancelled
- 新增计划与生产托盘登录 V2 页面

- 新增托盘登录接口、旧路径路由和中英文文案

- 更新迁移任务列表中的托盘登录状态
2026-06-22 16:01:21 +08:00
sheng
37daca561d Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 15:51:53 +08:00
sheng
eb31da72e0 迁移设备监控模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
- 新增计划与生产设备监控 V2 页面

- 新增设备监控接口、路由和中英文文案

- 更新迁移任务列表中的设备监控状态
2026-06-22 15:50:19 +08:00
sheng
d66a934f06 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 15:29:58 +08:00
sheng
b942d24f2c 迁移预警中心模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
- 新增计划与生产预警中心 V2 页面

- 新增预警中心接口、路由和中英文文案

- 更新迁移任务列表中的预警中心状态
2026-06-22 15:24:59 +08:00
sheng
12abbd51a8 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 15:10:23 +08:00
sheng
8ef087676f feat: migrate process execution module
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
- add V2 process execution page for planning production monitoring

- add process execution API, route, and i18n entries

- update migration task list status for process execution
2026-06-22 15:07:38 +08:00
sheng
ed087a1c83 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 15:00:05 +08:00
sheng
45c2ea6e63 feat: migrate tray management module
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
- add V2 tray management page for planning production monitoring

- add tray management API, route, and i18n entries

- update migration task list status for tray management
2026-06-22 14:58:04 +08:00
sheng
05c98e30c0 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-22 14:27:28 +08:00
sheng
248557a293 feat(production-master-data): 新增班组管理功能模块
1. 新增班组管理路由配置,添加对应页面访问路径
2. 新建班组管理API接口文件,封装增删改查、导入导出等接口
3. 补充中英文多语言文案,适配页面所有交互文本
4. 新增班组管理页面组件,包含搜索、列表、新增/编辑、成员管理、导入导出全功能
5. 更新功能测试文档,补充35条班组管理测试用例并更新用例统计
6. 更新系统功能清单对照表,新增班组管理模块条目
2026-06-22 14:27:18 +08:00
sheng
b44e031e74 feat: migrate planning batch management modules
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
- migrate batch list, tray tracking, and batch defect report pages to V2

- add planning production APIs, workerman helper, routes, and i18n entries

- add markdown migration task list generated from the Webman function matrix
2026-06-22 14:13:01 +08:00
sheng
3f546564cc feat(production-master-data): 新增工艺流程管理完整模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
- 添加工艺流程、流程卡工序的全套CRUD及复制接口
- 新增工艺流程列表页与流程卡详情页的路由和页面组件
- 补充产品、工序分类、工序子类的全量查询接口
- 优化弹窗表单组件,支持字段禁用与focus/blur/change事件回调
- 修复工序单元页面的国际化调用和权限配置问题
- 补充中英文多语言国际化文案
- 新增该模块的功能测试文档
2026-06-04 17:07:15 +08:00
b6c362d906 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
2026-06-02 16:00:24 +08:00
76b480ece5 feat(production-master-data): add SPC采集配置功能
实现SPC数据采集配置页面,包含路由配置、API接口、多语言文案以及完整的增删改查功能,附带功能测试文档
2026-06-02 15:59:58 +08:00
sheng
76f9657fe9 style: 调整函数声明的空格格式,统一代码风格
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
对所有函数声明添加前后空格,保持代码排版一致性,提升可读性
2026-06-02 15:17:51 +08:00
e0d2c7c886 fix(pnpm): 修复@achrinza/node-ipc的安全版本问题
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
通过pnpm overrides将@achrinza/node-ipc强制升级到>=9.2.7版本,更新lockfile中的依赖版本记录。
2026-06-02 15:10:45 +08:00
sheng
df11ca0db6 docs: 更新项目环境搭建文档并添加volta配置
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
1. 补充Windows下Volta安装后的生效方式说明
2. 完善不同环境下Node版本安装指引
3. 新增volta命令无法识别的问题排查方案
4. 在package.json中添加volta的node版本配置
2026-06-02 14:42:33 +08:00
sheng
8b587163df chore: 配置项目开发环境并重构接口路由前缀
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled
1.  添加 .node-version 和 .npmrc 配置文件,使用 Volta 锁定 Node.js 18.16.0 版本,强制使用 pnpm 作为包管理器
2.  更新 package.json,添加 packageManager 和 engines 字段限制依赖安装版本,替换 npm 脚本为 pnpm
3.  重构设备管理模块的接口 BASE 路径和路由路径,统一命名为 device_model/device_category
4.  完善 README.md,新增环境要求、安装启动流程、常用命令和目录结构说明
2026-06-02 14:26:17 +08:00
sheng
f1e73f3319 Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2 2026-06-02 14:05:32 +08:00
sheng
ddc715e17c feat(production-master-data): add 异常不良管理功能
1. 新增设备类别API接口封装
2. 新增异常不良管理的CRUD、导入导出API
3. 添加异常不良管理页面路由与多语言配置
4. 新增文件工具类支持Excel读写下载
5. 实现完整的异常不良管理页面与导入弹窗
6. 新增功能测试流程文档
7. 安装xlsx依赖支持Excel操作
2026-06-02 14:05:15 +08:00
hui
87edf1c76f Merge branch 'master' of http://119.91.43.128:3001/sheng/mes-ui-d2
Some checks failed
Release pipeline / Always run job (push) Has been cancelled
Release pipeline / publish (push) Has been cancelled
2026-06-02 13:56:00 +08:00
hui
9100f65325 feat(设备类别管理功能模块): 2026-06-02 13:54:55 +08:00
175 changed files with 35139 additions and 4913 deletions

4
.env
View File

@@ -15,3 +15,7 @@ VUE_APP_I18N_FALLBACK_LOCALE=en
# element 颜色 # element 颜色
VUE_APP_ELEMENT_COLOR=#409EFF VUE_APP_ELEMENT_COLOR=#409EFF
# 后台接口地址(代理目标)
VUE_APP_BASE_URL=http://127.0.0.1:8787/background/

1
.node-version Normal file
View File

@@ -0,0 +1 @@
18.16.0

8
.npmrc Normal file
View File

@@ -0,0 +1,8 @@
# pnpm 配置
# 强制通过 pnpm 安装,禁止使用 npm / yarn
engine-strict=true
# 使用 lockfile 锁定依赖版本
lockfile=true
# 依赖提升模式(兼容旧项目)
shamefully-hoist=true
# 允许在 package.json 中通过 packageManager 字段指定包管理器版本

479
README.md
View File

@@ -1,6 +1,167 @@
# MES-UI # MES-UI
基于 [D2 Admin](https://github.com/d2-projects/d2-admin)Vue 2 + Element UI的企业级中后台前端项目。 基于 [D2 Admin](https://github.com/d2-projects/d2-admin)Vue 2.7 + Element UI的企业级 MES 中后台前端项目。
---
## 环境要求
本项目通过 **[Volta](https://volta.sh/)** 和 **[pnpm](https://pnpm.io/)** 锁定开发环境版本,确保团队成员使用完全一致的 Node.js 和包管理器版本。
| 工具 | 锁定版本 | 作用 |
| --- | --- | --- |
| **Node.js** | `18.16.0` | JavaScript 运行时(`.node-version` 文件) |
| **pnpm** | `10.33.0` | 包管理器(`package.json``packageManager` 字段) |
> 配置文件说明:
> - `.node-version` → Volta 读取,自动切换 Node.js 版本
> - `package.json` → `engines` 字段限制版本范围,`packageManager` 字段锁定 pnpm 版本
> - `.npmrc` → `engine-strict=true` 强制使用锁定版本,不满足则安装失败
---
## 从零开始安装到启动
### 第一步:安装 Volta如果尚未安装
Volta 会根据 `.node-version` 自动安装并切换到项目所需的 Node.js 版本。
**WindowsPowerShell**
```powershell
winget install Volta.Volta
```
> `winget` 安装完成后,**当前终端窗口不会自动加载 Volta**。选择以下任一方式生效:
>
> **方式一(关闭重开):** 关掉当前终端,重新打开一个 PowerShell / VS Code 终端。
>
> **方式二(不重启立即生效):** 在当前终端执行:
> ```powershell
> $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
> ```
>
> 生效后验证:
> ```powershell
> volta --version # 应输出版本号(如 2.0.2
> ```
**macOS / Linux**
```bash
curl https://get.volta.sh | bash
# 重新加载 shell 配置
source ~/.bashrc # 或 source ~/.zshrc
volta --version
```
> 更多安装方式请参考 [Volta 官方文档](https://docs.volta.sh/guide/getting-started)。
### 第二步:启用 pnpmVolta 内置支持,无需额外安装)
```bash
# Volta 会根据 package.json 中的 packageManager 字段
# 自动安装并使用 pnpm@10.33.0
volta install pnpm@10.33.0
# 验证版本
pnpm --version # 应输出 10.33.0
```
> 如果没有 Volta也可以手动安装 pnpm
> ```bash
> npm install -g pnpm@10.33.0
> ```
### 第三步:克隆项目并安装依赖
```bash
# 克隆仓库
git clone <仓库地址>
cd mes-ui
# Volta 检测到 .node-version 后,自动下载并安装 Node.js 18.16.0
# 如果你本机没有这个版本Volta 会提示 "Fetching node@18.16.0" —— 等它完成即可
node --version # 应输出 v18.16.0
# 安装依赖
pnpm install
```
> **没有安装 Node 18.16.0 怎么办?**
> **Volta 用户**:无需手动操作。进入项目目录时 Volta 会自动检测 `.node-version`,如果本机缺这个版本,它会**自动下载安装**,你只需等待几秒终端提示完成即可。
> **nvm 用户**:执行 `nvm install 18.16.0 && nvm use 18.16.0`。
> **手动安装**:去 [nodejs.org](https://nodejs.org/) 下载 Node.js 18.16.0 安装包。
>
> 装完后建议运行 `node --version` 确认输出 `v18.16.0`,否则 `pnpm install` 会因为 `engine-strict=true` 直接报错拒绝安装。
### 第四步:配置环境变量
在项目根目录创建 `.env.development.local` 文件(此文件已在 `.gitignore` 中,不会提交):
```bash
# 后台 API 地址
VUE_APP_API=http://你的后台服务器地址/
```
### 第五步:启动开发服务器
```bash
pnpm serve
```
浏览器会自动打开 `http://localhost:8080`(或终端提示的实际端口)。
---
## 常用命令速查
| 命令 | 说明 |
| --- | --- |
| `pnpm serve` | 启动开发服务器(自动打开浏览器) |
| `pnpm build` | 生产环境构建 |
| `pnpm build:preview` | 预发布环境构建 |
| `pnpm lint` | ESLint 代码检查并自动修复 |
| `pnpm test:unit` | 运行单元测试 |
| `pnpm install` | 安装依赖 |
| `pnpm add <包名>` | 添加新依赖(**禁止使用 npm install** |
| `pnpm remove <包名>` | 移除依赖 |
---
## 版本锁定机制
### Node.js 版本锁定Volta
```
项目根目录
├── .node-version → 18.16.0Volta 自动读取切换)
```
- Volta 进入项目目录时自动将 Node.js 切换为 `18.16.0`
- 如果该版本未安装Volta 会自动下载并安装
- 传统方案nvm也兼容`nvm use` 读取 `.nvmrc`,本项目同时提供 `.node-version` 供 Volta 使用
### pnpm 版本锁定
```
package.json
├── packageManager → pnpm@10.33.0Corepack / Volta 识别)
├── engines.pnpm → >=10.33.0 <11npm/pnpm install 时校验)
```
```ini
# .npmrc
engine-strict=true # 版本不满足 → 安装直接报错
```
### 依赖版本锁定
```
pnpm-lock.yaml → 锁定所有依赖的精确版本(等价于 npm 的 package-lock.json
```
> `pnpm-lock.yaml` **必须提交到 Git**,保证所有环境安装的依赖版本完全一致。
--- ---
@@ -12,234 +173,109 @@ mes-ui/
├── docs/ # 项目文档 ├── docs/ # 项目文档
├── public/ # 静态资源(不经过 webpack 处理) ├── public/ # 静态资源(不经过 webpack 处理)
├── src/ # PC 端业务源码 ├── src/ # PC 端业务源码
│ ├── api/ # 接口请求层 │ ├── api/ # 接口请求层(按业务模块拆分)
│ ├── assets/ # 静态资源(经 webpack 处理) │ ├── _service.js # axios 请求服务实例
│ │ └── production-master-data/ # 生产主数据模块 API
│ ├── assets/ # 静态资源(经 webpack 处理,含 SCSS / SVG
│ ├── components/ # 公共组件 │ ├── components/ # 公共组件
│ │ ├── page-table/ # CRUD 表格便捷组合体
│ │ └── page-dialog-form/ # 新增/编辑弹框组件
│ ├── composables/ # 可复用逻辑函数 │ ├── composables/ # 可复用逻辑函数
│ ├── layout/ # 页面布局 │ ├── useTableColumns.js # 列定义工厂
│ │ ├── useTableButtons.js # 按钮定义工厂
│ │ └── useI18n.js # i18n Mixin 工厂
│ ├── layout/ # 页面布局header-aside
│ ├── libs/ # 工具函数 │ ├── libs/ # 工具函数
│ ├── locales/ # 国际化语言包 │ ├── locales/ # 国际化语言包zh-chs / en
│ ├── menu/ # 菜单配置 │ ├── router/ # 路由配置modules/ 按业务模块拆分)
│ ├── plugin/ # Vue 插件
│ ├── router/ # 路由配置
│ ├── store/ # Vuex 状态管理 │ ├── store/ # Vuex 状态管理
── views/ # 业务页面 ── utils/ # 通用工具文件下载、Excel 读取等)
│ └── views/ # 业务页面(按模块三级目录组织)
│ ├── production-master-data/ # 生产主数据
│ ├── system-administration/ # 系统管理
│ ├── equipment-management/ # 设备管理
│ ├── quality-management/ # 质量管控
│ ├── planning-production/ # 计划生产
│ └── data-platform/ # 数据平台
├── src.mobile/ # 移动端业务源码 ├── src.mobile/ # 移动端业务源码
├── .browserslistrc # 浏览器兼容配置 ├── .browserslistrc # 浏览器兼容配置
├── .editorconfig # 编辑器统一配置 ├── .editorconfig # 编辑器统一配置
├── .env / .env.development / .env.preview # 环境变量 ├── .env / .env.development / .env.preview # 环境变量
├── .eslintignore / .eslintrc.js # ESLint 配置 ├── .eslintignore / .eslintrc.js # ESLint 配置
├── .gitignore # Git 忽略配置 ├── .gitignore # Git 忽略配置
├── .node-version # Volta Node.js 版本锁定18.16.0
├── .npmrc # pnpm 配置(版本强校验)
├── .postcssrc.js # PostCSS 配置 ├── .postcssrc.js # PostCSS 配置
├── babel.config.js # Babel 配置 ├── babel.config.js # Babel 配置
├── jest.config.js # Jest 单元测试配置 ├── jest.config.js # Jest 单元测试配置
├── jsconfig.json # VS Code 路径别名提示 ├── jsconfig.json # VS Code 路径别名提示
├── package.json # 项目依赖与脚本 ├── package.json # 项目依赖与脚本
├── pnpm-lock.yaml # pnpm 依赖锁定文件 ├── pnpm-lock.yaml # pnpm 依赖锁定文件(必须提交)
├── vue.config.js # Vue CLI 构建配置 ├── vue.config.js # Vue CLI 构建配置
└── README.md # 项目说明(本文件) └── README.md # 项目说明(本文件)
``` ```
--- ---
## 各目录详解 ## 项目文档
### `.github/`
GitHub 相关配置,包含 Issue 模板和 CI/CD 工作流(`workflows/`)。
### `docs/`
项目文档目录,存放变更日志、修复记录等 Markdown 文档。子目录 `image/` 存放文档引用的图片。
| 文档 | 说明 | | 文档 | 说明 |
|------|------| | --- | --- |
| [表格组件使用说明](./docs/表格组件使用说明.md) | page-table / page-dialog-form 完整使用手册 | | [表格组件使用说明](./docs/表格组件使用说明.md) | `page-table` + `page-dialog-form` 完整使用手册 |
| [SCT基础表格重构设计](./docs/SCT基础表格重构设计.md) | 旧 sct-base-table → 新架构的设计文档 | | [国际化规则](./docs/国际化规则.md) | i18n 使用规范与迁移指南 |
| [Sass废弃修复](./docs/Sass废弃修复.md) | Sass 废弃警告修复记录 | | [迁移提示词](./docs/迁移提示词.md) | 旧页面迁移到新方案的规则文档 |
| [SCT基础表格重构设计](./docs/SCT基础表格重构设计.md) | 旧 `sct-base-table` → 新架构设计文档 |
| [变更日志](./docs/变更日志.md) | 项目变更日志 | | [变更日志](./docs/变更日志.md) | 项目变更日志 |
### `public/` ---
**静态资源目录**,文件会原样复制到构建产物中,不经过 webpack 编译。 ## 关键目录说明
| 子目录 | 作用 | ### `src/components/` — 公共组件
|--------|------|
| `lib/UEditor/` | 百度 UEditor 富文本编辑器(第三方库,通过 `<script>` 直接引入) |
| `image/theme/` | 各主题的 Logo 图片和预览图chester / d2 / element / line / star / tomorrow-night-blue / violet |
| `image/loading/` | 页面加载动画的 SVG |
| `html/` | 独立 HTML 页面(如 demo.html |
| `index.html` | SPA 入口 HTML 模板 |
| `icon.ico` | 网站 Favicon |
### `src/` — PC 端源码
#### `src/api/`
**接口请求层**。封装后端 API 调用,按业务模块拆分为独立文件。
- `_service.js` — 基于 axios 的请求服务实例
- `_tools.js` — 请求工具函数
- `demo.js` — 演示接口
- `sys.user.js` — 用户相关接口
#### `src/assets/`
**静态资源**,会被 webpack 处理。
| 子目录 | 作用 |
|--------|------|
| `style/unit/color.scss` | 全局颜色变量定义 |
| `style/public.scss` | 全局 SCSS 变量、mixin、placeholder通过 `additionalData` 自动注入每个组件) |
| `style/public-class.scss` | 全局通用 CSS class间距、浮动、文字对齐等工具类 |
| `style/fixed/` | 第三方库样式覆盖element / markdown / n-progress / vue-splitpane 等) |
| `style/animate/` | 过渡动画样式vue-transition |
| `style/theme/` | 主题切换样式,每个主题独立目录,含 `index.scss` + `setting.scss` |
| `svg-icons/icons/` | SVG 图标文件,通过 `svg-sprite-loader` 生成雪碧图 |
| `svg-icons/index.js` | SVG 图标自动注册脚本 |
#### `src/components/`
**公共组件库**,所有可复用组件集中管理。
| 组件 | 作用 | | 组件 | 作用 |
|------|------| | --- | --- |
| `d2-container` | 页面容器组件,提供卡片、全屏、透明等多种布局变体 | | `page-table` | CRUD 表格组合体(按钮栏 + 表格 + 分页 + 高度自适应 + 帮助按钮) |
| `page-dialog-form` | 新增/编辑弹框(表单渲染 + 校验 + i18n 自动翻译) |
| `d2-container` | 页面容器(卡片、全屏、透明等多种布局变体) |
| `d2-container-frame` | iframe 嵌套外部页面容器 | | `d2-container-frame` | iframe 嵌套外部页面容器 |
| `d2-count-up` | 数字滚动动画组件 |
| `d2-highlight` | 代码高亮展示组件 |
| `d2-icon` / `d2-icon-svg` | 图标组件Font Awesome / SVG |
| `d2-icon-select` / `d2-icon-svg-select` | 图标选择器组件 |
| `d2-link-btn` | 链接式按钮 |
| `d2-markdown` | Markdown 渲染组件 |
| `d2-module-index-banner` | 首页 Banner 展示组件 |
| `d2-module-index-menu` | 首页菜单组件 |
| `d2-quill` | Quill 富文本编辑器封装 |
| `d2-scrollbar` | 自定义滚动条组件 |
| `d2-ueditor` | 百度 UEditor 编辑器封装 |
| `page-table` | **新** CRUD 表格便捷组合体(按钮栏 + 表格 + 分页 + 高度自适应 + 帮助按钮) |
| `page-dialog-form` | **新** 增删改查弹框组件(表单渲染 + 校验 + i18n |
> `page-table` 和 `page-dialog-form` 详细用法见[表格组件使用说明](./docs/表格组件使用说明.md) > `page-table` 和 `page-dialog-form` 是本项目核心组件,详细用法见 [表格组件使用说明](./docs/表格组件使用说明.md)
#### `src/composables/` ### `src/composables/` — 逻辑复用层
**可复用的逻辑函数Composition Utilities**。把散落在各页面中的重复 JS 逻辑抽成纯函数,组件只需调用即可。
| 文件 | 作用 | | 文件 | 作用 |
|------|------| | --- | --- |
| `useTableColumns.js` | 列定义工厂,消除手动分配 `idx` 序号;约定 `prop: '_actions'` 为操作列 | | `useTableColumns.js` | 列定义工厂,消除手动分配 `idx`约定 `prop: '_actions'` 为操作列 |
| `useTableButtons.js` | 按钮定义工厂,一键生成顶部工具栏 + 行内操作按钮,自动注入权限过滤 | | `useTableButtons.js` | 按钮工厂,一键生成工具栏 + 行内按钮,自动注入权限过滤 |
| `useI18n.js` | i18n Mixin 工厂,通过 `i18nMixin(prefix)` 注入 `key()` / `ckey()` 方法,消除每个页面手写 `T` 常量 | | `useI18n.js` | i18n Mixin 工厂,通过 `i18nMixin(prefix)` 注入 `key()` / `ckey()` |
| `useConfirmHandle.js` | 确认操作 Mixin封装 `$confirm` + API 调用 |
> **为什么放在 `src/` 根目录?** ### `src/router/modules/` — 路由模块
> composable 不是 UI 组件(不需要 `<template>`),也不属于 `libs/`(通用工具库)。它是介于**组件和逻辑之间**的中间层——被多个组件调用,但本身不渲染 DOM。放在 `src/` 根目录下与 `components/`、`views/` 同级,方便一眼看到项目的公共逻辑层。
#### `src/layout/` 按业务一级模块拆分examples:
**页面布局**。当前使用 `header-aside` 布局(顶栏 + 侧边栏 + 主内容区)。
| 子目录 | 作用 | | 文件 | 路由前缀 | 对应模块 |
|--------|------| | --- | --- | --- |
| `layout.vue` | 布局主文件 | | `production-master-data.js` | `/production_configuration` | 生产主数据 |
| `components/menu-side/` | 侧边菜单组件 | | `system-administration.js` | `/system_settings` | 系统管理 |
| `components/menu-header/` | 顶部导航菜单组件 | | `equipment-management.js` | `/equipment_management` | 设备管理 |
| `components/header-user/` | 顶栏用户信息下拉 | | `quality-management.js` | `/quality_management` | 质量管控 |
| `components/header-theme/` | 主题切换面板 | | `planning-production.js` | `/planning_production` | 计划生产 |
| `components/header-color/` | 主题色切换 | | `data-platform.js` | `/data_platform` | 数据平台 |
| `components/header-locales/` | 多语言切换 |
| `components/header-fullscreen/` | 全屏切换 |
| `components/header-search/` | 全局搜索 |
| `components/header-log/` | 操作日志 |
| `components/header-size/` | 字号大小切换 |
| `components/tabs/` | 多标签页栏 |
| `components/contextmenu/` | 标签页右键菜单 |
| `components/panel-search/` | 全局搜索面板 |
| `mixins/search.js` | 搜索 mixin |
| `mixins/` | 菜单相关 mixin |
#### `src/libs/` ### `src/api/` — 接口请求层
**工具函数库**。
- `util.js` — 通用工具方法
- `util.cookies.js` — Cookie 操作
- `util.db.js` — 本地存储(基于 lowdb
- `util.log.js` — 日志工具
- `util.import.development.js` / `util.import.production.js` — 开发/生产环境动态加载
#### `src/locales/` 按业务模块父目录存放,命名规则:`{模块名}.js`。例如:
**国际化i18n**。JSON 格式的语言文件,支持: - `production-master-data/device-category.js` → 设备类型
- `zh-chs` — 简体中文 - `production-master-data/factory-area.js` → 工厂区域
- `zh-cht` — 繁体中文
- `en` — 英文
- `ja` — 日文
- `mixin.js` — Vue 国际化 mixin
#### `src/menu/`
**菜单配置**。按业务模块拆分,定义侧边栏菜单结构。`modules/` 下按 demo-components、demo-playground、demo-plugins 组织。
#### `src/plugin/`
**Vue 插件**。
- `d2admin/` — D2Admin 核心插件(初始化全局功能)
- `error/` — 全局错误捕获
- `log/` — 通用日志输出
- `open/` — 新窗口打开工具
#### `src/router/`
**路由配置**。`modules/` 下按 components、playground、plugins 拆分路由表。`routes.js` 为汇总路由,`index.js` 创建 Vue Router 实例。
#### `src/store/`
**Vuex 状态管理**。`modules/d2admin/` 下按功能拆分为独立模块:
| 模块 | 作用 |
|------|------|
| `account` | 用户账户信息 |
| `color` | 主题色管理 |
| `db` | 本地数据库状态 |
| `fullscreen` | 全屏状态 |
| `gray` | 灰度模式 |
| `log` | 操作日志 |
| `menu` | 菜单状态(展开/收缩/激活项) |
| `page` | 页面缓存与标签页 |
| `releases` | 版本信息 |
| `search` | 全局搜索 |
| `size` | 字号大小 |
| `theme` | 主题切换 |
| `transition` | 页面过渡动画 |
| `ua` | 用户代理信息 |
| `user` | 用户登录态 |
#### `src/views/`
**页面视图**。
- `demo/` — 各功能展示页面(组件演示 / 插件演示 / 功能试验场)
- `system/` — 系统级页面(首页 / 登录 / 404 / 日志 / 重定向 / 刷新)
- `production-master-data/` — 生产配置模块(工厂区域 / 产线 / 工艺 / 产品 / 物料等)
> 页面按一级模块 → 二级模块 → 三级模块 三层目录组织,与 [后台Webman界面截图对照表](./后台Webman界面截图对照表.md) 中的英文命名一致。
#### `src/` 根文件
| 文件 | 作用 |
|------|------|
| `main.js` | PC 端应用入口 |
| `App.vue` | 根组件 |
| `setting.js` | 全局设置(站点标题等) |
| `i18n.js` | 国际化初始化 |
--- ---
### `src.mobile/` — 移动端源码 ## 构建配置文件
基于 Vant UI 的移动端适配版本,与 PC 端共用部分业务逻辑。
| 文件/目录 | 作用 |
|-----------|------|
| `main.js` | 移动端应用入口 |
| `App.vue` | 移动端根组件 |
| `vant.js` | Vant UI 按需引入配置 |
| `router/` | 移动端路由 |
| `store/` | 移动端状态管理 |
| `views/` | 移动端页面 |
---
### 构建配置文件
| 文件 | 作用 | | 文件 | 作用 |
|------|------| | --- | --- |
| `vue.config.js` | Vue CLI 构建配置多页入口、CDN、webpack 插件、主题色替换、SVG sprites、路径别名 | | `vue.config.js` | Vue CLI 构建配置多页入口、CDN、webpack 插件、主题色替换、SVG sprites、路径别名 |
| `babel.config.js` | Babel 转译配置 | | `babel.config.js` | Babel 转译配置 |
| `.postcssrc.js` | PostCSS 自动前缀等配置 | | `.postcssrc.js` | PostCSS 自动前缀等配置 |
@@ -249,22 +285,73 @@ GitHub 相关配置,包含 Issue 模板和 CI/CD 工作流(`workflows/`
| `.browserslistrc` | 目标浏览器范围 | | `.browserslistrc` | 目标浏览器范围 |
| `.editorconfig` | 编辑器缩进/换行风格统一 | | `.editorconfig` | 编辑器缩进/换行风格统一 |
### 环境变量文件 ---
| 文件 | 作用 | ## 环境变量
|------|------|
| 文件 | 说明 |
| --- | --- |
| `.env` | 所有环境通用变量 | | `.env` | 所有环境通用变量 |
| `.env.development` | 开发环境变量 | | `.env.development` | 开发环境变量 |
| `.env.development.local` | 本地开发环境变量(不提交 Git需自行创建 |
| `.env.preview` | 预发布环境变量 | | `.env.preview` | 预发布环境变量 |
--- ---
## 常用命令 ## 常见问题
| 命令 | 说明 | ### Q1`pnpm install` 报错 "Unsupported engine"
|------|------|
| `pnpm serve` | 启动开发服务器 | > 意思:本机 Node.js 或 pnpm 版本不在项目规定范围内install 被阻断。
| `pnpm build` | 生产环境构建 |
| `pnpm build:preview` | 预发布环境构建 | **先排查版本:**
| `pnpm lint` | ESLint 检查并自动修复 |
| `pnpm test:unit` | 运行单元测试 | ```bash
node --version # 项目要求 >=18.16.0 <19
pnpm --version # 项目要求 >=10.33.0 <11
```
**谁把版本不对:**
| 情景 | 解决办法 |
| --- | --- |
| **用 Volta**(推荐) | `volta install node@18.16.0` + `volta install pnpm@10.33.0`,进入项目目录自动生效 |
| **用 nvm** | `nvm install 18.16.0 && nvm use 18.16.0`,再 `npm install -g pnpm@10.33.0` |
| **手动安装** | 去 [nodejs.org](https://nodejs.org/) 下载 18.16.0,再 `npm install -g pnpm@10.33.0` |
| **已有正确版本但切换不生效** | 重新打开终端,确保 `volta` / `nvm` 已正确加载 |
> 版本验证通过后,重新执行 `pnpm install`。
### Q2启动后页面报跨域错误
`.env.development.local` 中确认 `VUE_APP_API` 指向正确的后台地址,且后台已开启 CORS。
### Q3端口被占用
默认端口为 `8080`。如需修改,在 `.env.development.local` 中添加:
```bash
PORT=8081
```
### Q4`volta` 命令无法识别 / "无法将 volta 项识别为 cmdlet"
> 原因:`winget install Volta.Volta` 不会自动刷新当前终端的 PATH 缓存。
**解决办法二选一:**
```powershell
# 方案 A关掉当前终端重新打开一个推荐最简单
# 方案 B在当前终端执行以下命令立即生效免重启
$env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
```
> 生效后 `volta --version` 应输出版本号。
### Q5依赖安装慢
pnpm 默认使用 npm registry。如需加速可配置镜像
```bash
pnpm config set registry https://registry.npmmirror.com
```

View File

@@ -1,153 +0,0 @@
# 功能测试流程文档 ——【计量单位】
> 本文档为【计量单位】功能迁移后的独立测试文档。测试人员请按以下流程逐步执行,对通过的用例在"通过"列打勾(`[x]`),未通过的用例在"问题记录"列描述具体现象(包括错误截图编号、操作步骤、实际/预期差异等)。
| 项目名称 | MES-UI生产主数据 → 物料模型 → 计量单位) |
| --- | --- |
| 文档版本 | v1.0 |
| 适用版本 | mes-ui 本次迁移版本 |
| 编写日期 | 2026-06-02 |
| 测试入口 | 菜单:生产主数据 → 物料模型 → 计量单位 |
| 关联文件 | [`src/views/production-master-data/material-model/material-unit/index.vue`](file:///d:/code/mes/mes-ui/src/views/production-master-data/material-model/material-unit/index.vue) [`src/api/production-master-data/material-unit.js`](file:///d:/code/mes/mes-ui/src/api/production-master-data/material-unit.js) [`src/router/modules/production-master-data.js`](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js) |
---
## 一、测试环境配置要求
| 项 | 要求 |
| --- | --- |
| 后台环境 | Webman + 已部署 `production_configuration/matetial_model/unit` 路由接口;数据库中存在 `unit` 数据表 |
| 前端环境 | `npm run serve` 启动 mes-ui 工程;浏览器推荐 Chrome 110+ |
| 登录账号 | 拥有 `生产主数据 / 计量单位` 菜单访问权限,且分配以下权限点:<br>· `…/unit/create`(新增)<br>· `…/unit/edit`(编辑)<br>· `…/unit/delete`(删除) |
| 网络要求 | 前后端网络互通;浏览器可访问 `VUE_APP_API` 域名 |
| 数据准备 | 至少 1 条已存在的单位数据;至少 1 个物料已引用本表(用于验证删除联动提示) |
| 浏览器工具 | 打开 DevToolsF12→ Network 与 Console方便抓取接口与异常 |
---
## 二、测试前置条件
1. 已成功登录系统,且侧边栏显示 **生产主数据 → 物料模型 → 计量单位** 菜单。
2. 浏览器语言分别切换为「简体中文 / English」时界面文字均能正确翻译`key is not defined` / 英文缺失等情况。
3. 列表默认展示接口 `production_configuration/matetial_model/unit/list` 返回的记录。
4. 浏览器缩放比例为 100%;分辨率建议 1440×900 及以上。
5. DevTools 关闭缓存Network → Disable cache避免旧 JS 资源影响测试。
---
## 三、测试用例
> **字段说明(数据库与表单共用)**
> - 编码 `code`:必填,长度 1~100文本
> - 名称 `name`:必填,长度 1~100文本
> - 备注 `remark`:选填,文本域(多行)
> - 列表展示字段:序号 / 编码 / 名称 / 备注 / 创建时间 / 操作
### 3.1 列表与搜索
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.1.1 | 进入"计量单位"菜单 | 列表正常加载,无报错;表头依次为:序号、单位编码、单位名称、备注、创建时间、操作 | | [ ] | |
| 3.1.2 | 在"单位编码"输入框输入关键字 → 点击「查询」 | 列表仅展示编码包含该关键字的记录 | | [ ] | |
| 3.1.3 | 在"单位名称"输入框输入关键字 → 点击「查询」 | 列表仅展示名称包含该关键字的记录 | | [ ] | |
| 3.1.4 | 编码 + 名称同时输入关键字 → 点击「查询」 | 列表为两条件 AND 过滤结果 | | [ ] | |
| 3.1.5 | 输入查询条件后点击「重置」 | 输入框清空,列表恢复为全量数据 | | [ ] | |
| 3.1.6 | 在搜索输入框按回车键 | 等同于点击「查询」,触发列表刷新 | | [ ] | |
| 3.1.7 | 列表分页:跳转到第 2 页 / 修改每页条数 | 列表请求参数 `page_no` / `page_size` 正确变化,数据正常刷新 | | [ ] | |
| 3.1.8 | 列表为空时(无数据) | 显示空状态占位(如"暂无数据"),不报错 | | [ ] | |
| 3.1.9 | 列表加载中 | 表格区域显示 loading 遮罩 | | [ ] | |
| 3.1.10 | 切换浏览器语言为 English 后刷新 | 表头、操作按钮、占位文字均显示英文 | | [ ] | |
### 3.2 新增
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.2.1 | 点击工具栏「新增」按钮 | 弹出标题为「新增单位」的对话框,显示空表单 | | [ ] | |
| 3.2.2 | 不填任何字段,点击「确定」 | 触发表单校验:「单位编码」「单位名称」下方红字提示必填 | | [ ] | |
| 3.2.3 | 仅填写编码(不填名称),点击「确定」 | 仅名称字段提示必填 | | [ ] | |
| 3.2.4 | 输入 101 字符的编码,点击「确定」 | 提示「长度在 1 到 100 个字符」 | | [ ] | |
| 3.2.5 | 正常填写:编码 `PCS`、名称 `个`、备注 `基本计数单位` → 点击「确定」 | 弹出"操作成功"提示,对话框自动关闭,列表自动刷新并出现该条记录 | | [ ] | |
| 3.2.6 | 必填项后存在前后空格时点击「确定」 | 提交数据带空格(视业务策略决定是否 trim列表正常展示 | | [ ] | |
| 3.2.7 | 新增弹框点击「取消」 | 对话框关闭,未提交任何数据 | | [ ] | |
| 3.2.8 | 新增弹框点击右上角 × | 对话框关闭,未提交任何数据 | | [ ] | |
| 3.2.9 | 在无 `unit/create` 权限的账号下访问 | 工具栏不显示「新增」按钮 | | [ ] | |
| 3.2.10 | 后端返回 `code 已存在` 错误 | 页面提示后端错误信息(如 `code 重复`),不崩溃 | | [ ] | |
### 3.3 编辑
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.3.1 | 在列表行点击「编辑」 | 弹出标题为「编辑单位」的对话框,且表单字段回显当前行数据 | | [ ] | |
| 3.3.2 | 清空编码,点击「确定」 | 提示编码必填 | | [ ] | |
| 3.3.3 | 修改名称为新值,备注保留为空,点击「确定」 | 提示操作成功,列表对应行的名称更新 | | [ ] | |
| 3.3.4 | 修改编码(与已有数据重复),点击「确定」 | 后端返回重复提示,页面友好显示 | | [ ] | |
| 3.3.5 | 编辑过程中点击「取消」 | 对话框关闭,原数据未发生变化 | | [ ] | |
| 3.3.6 | 无 `unit/edit` 权限的账号 | 行内不显示「编辑」按钮 | | [ ] | |
| 3.3.7 | 同时打开两个浏览器标签 A、B均进入编辑 | B 保存后 A 再次保存时数据为 B 已提交后的最新值(避免脏写) | | [ ] | |
### 3.4 删除
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.4.1 | 在列表行点击「删除」 | 弹出确认框,提示「确定要执行该操作吗?」 | | [ ] | |
| 3.4.2 | 确认框点击「取消」 | 关闭确认框,未删除 | | [ ] | |
| 3.4.3 | 确认框点击「确定」 | 提示操作成功,列表自动移除该记录 | | [ ] | |
| 3.4.4 | 删除被物料引用的单位 | 后端返回业务级提示,页面正确展示且不崩溃 | | [ ] | |
| 3.4.5 | 删除最后一页唯一一条记录后 | 列表自动回退到上一页(或显示空状态),无空白页 | | [ ] | |
| 3.4.6 | 无 `unit/delete` 权限的账号 | 行内不显示「删除」按钮 | | [ ] | |
| 3.4.7 | 网络断开时点击「删除」 | 提示网络异常,未误删数据 | | [ ] | |
### 3.5 权限与国际化
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.5.1 | 切换为仅有「查询」权限的账号 | 仅能查看列表与查询,无新增/编辑/删除按钮 | | [ ] | |
| 3.5.2 | 切换语言为 English刷新页面 | 表头、按钮、弹框标题、提示语全部为英文 | | [ ] | |
| 3.5.3 | 在中文下打开新增 → 切换为英文 | 弹框标题、表单 label 立即切换为英文(无需关闭弹框) | | [ ] | |
| 3.5.4 | 切换语言后,校验提示(如"长度在 1 到 100 个字符" | 同步显示对应语言 | | [ ] | |
### 3.6 异常与边界
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.6.1 | 后端返回 500 错误 | 列表显示空状态或错误提示,不白屏 | | [ ] | |
| 3.6.2 | 后端返回字段缺失 | 缺失字段列显示为空,不抛 JS 异常 | | [ ] | |
| 3.6.3 | 备注字段输入 5000 字符 | 提交成功,列表中可滚动展示(可截断) | | [ ] | |
| 3.6.4 | 在表单输入过程中按 ESC | 弹框关闭,表单状态重置 | | [ ] | |
| 3.6.5 | 列表行内操作按钮溢出 | 操作列固定在右侧且可滚动查看 | | [ ] | |
| 3.6.6 | DevTools Console 检查 | 全程无 `Vue warn`、未捕获 Promise 异常、key 缺失警告 | | [ ] | |
---
## 四、测试结果汇总
| 用例总数 | 通过 | 失败 | 阻塞 | 通过率 |
| --- | --- | --- | --- | --- |
| | | | | |
---
## 五、问题记录区
| 编号 | 用例编号 | 复现步骤 | 实际结果 | 严重程度 | 处理人 | 状态 | 备注 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | | | | | | | |
| 2 | | | | | | | |
| 3 | | | | | | | |
---
## 六、测试结论
| 项目 | 结论 |
| --- | --- |
| 功能完整性 | ☐ 满足 ☐ 部分缺失 ☐ 不满足 |
| 性能表现 | ☐ 良好 ☐ 一般 ☐ 差 |
| 权限控制 | ☐ 正确 ☐ 存在漏洞 |
| 国际化 | ☐ 完整 ☐ 部分缺失 ☐ 缺失 |
| 是否可发布 | ☐ 是 ☐ 否(请说明阻塞问题) |
测试人员签字__________________ 日期__________
---
*本测试流程文档为【计量单位】功能迁移版本专用,请独立归档保存。*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
# 工艺流程Process Routing迁移分析方案
## 一、旧代码研读范围
- 旧页面:`/home/james/WEBMAN-VUE-APP-develop/webman-vue-app/src/views/production_configuration/technology_model/technology_flow/process.vue`
- 旧 API`src/api/production_configuration/technology_model/technology_flow.js``technology_flow_process.js``calculation_script.js`
- 旧组件:`components/ProcessPlugin/ResultParam``TemperatureSupp``CalculationScript``components/technology/technology-flow-model.vue`
- V2 页面:`src/views/production-master-data/process-model/process-routing/index.vue``process-routing-card/index.vue`
## 二、旧模块接口清单
| 接口 | 方法 | method 参数 | platform | 主要参数 | 返回数据使用 |
| --- | --- | --- | --- | --- | --- |
| `production_configuration/technology_model/technology_flow/list` | GET | `production_configuration_technology_model_technology_flow_list` | background | `page_no``page_size``code``name``flow_category_id``product_model_id` | 工艺流程主列表 |
| `technology_flow/create` | POST | `production_configuration_technology_model_technology_flow_create` | background | `code``name``flow_category_id``product_model_id``remark` | 新增流程 |
| `technology_flow/edit` | PUT | `production_configuration_technology_model_technology_flow_edit` | background | `id` + 表单字段 | 编辑流程 |
| `technology_flow/delete` | DELETE | `production_configuration_technology_model_technology_flow_delete` | background | `id: []` | 删除流程 |
| `technology_flow/copy` | POST | `production_configuration_technology_model_technology_flow_copy` | background | `id: []` | 复制流程 |
| `technology_flow_process/all` | GET | `production_configuration_technology_model_technology_flow_process_all` | background | `flow_id` | `process``flow_data``is_binding_batch``technology_flow_operate_log` |
| `technology_flow_process/create` | POST | `..._create` | background | `flow_id``workingsubclass_id``code``name``pin_check` | 新增流程工序 |
| `technology_flow_process/edit` | PUT | `..._edit` | background | `id``flow_id`、表单字段 | 编辑流程工序 |
| `technology_flow_process/delete` | DELETE | `..._delete` | background | `id: []``flow_id` | 删除流程工序 |
| `technology_flow_process/move_up` | POST | `..._move_up` | background | `flow_id``move_id``quilt_move_id``move_sort``quilt_move_sort` | 上移排序 |
| `technology_flow_process/move_down` | POST | `..._move_down` | background | 同上 | 下移排序 |
| `technology_flow_process/set_setting` | POST | `..._set_setting` | background | `id``flow_id``setting: JSON.stringify(data)` | 保存设定值插件数据 |
| `technology_flow_process/get_optional_params_details` | GET | `..._get_optional_params_details` | background | `process_id``page_no``page_size``name``code` | 当前流程工序已绑定结果参数 |
| `technology_flow_process/get_all_workingsubclass_params` | GET | `..._get_all_workingsubclass_params` | background | `process_id` | 左右树:全部/已选结果参数 |
| `technology_flow_process/add_optional_params` | POST | `..._add_optional_params` | background | `process_id``optional_params: JSON.stringify(list)` | 保存结果参数绑定 |
| `technology_flow_process/get_step` | GET | `..._get_step` | background | 无 | 温度补偿工步下拉 |
| `technology_flow_process/get_temperature_list` | GET | `..._get_temperature_list` | background | `process_id` | `data``start_work_step_id``end_work_step_id` |
| `technology_flow_process/create_temperature` | POST | `..._create_temperature` | background | `process_id``start_work_step_id``end_work_step_id``temp_data` | 保存温度补偿 |
| `technology_flow_process/get_temperature_template` | POST | `..._get_temperature_template` | background | 无 | blob 模板文件 |
| `calculation_script/all` | GET | `production_configuration_technology_model_calculation_script_all` | background | `process_id` | 当前工序计算脚本列表 |
| `calculation_script/create` | POST | `..._create` | background | `process_id``code``name``interface_code``interface_position``status``remark``calculation_script_content` | 新增脚本 |
| `calculation_script/edit` | PUT | `..._edit` | background | `id` + 新增字段 | 编辑脚本 |
| `calculation_script/delete` | DELETE | `..._delete` | background | `id` | 删除脚本 |
## 三、旧组件清单
| 组件 | 功能 | 关键属性/状态 | 使用场景 |
| --- | --- | --- | --- |
| `SctBaseTable` | 工序明细表格,带上移/下移/操作列插槽 | `columns``data``buttontable``up/down/handle` 插槽 | 流程卡工序列表 |
| `SctBaseDialog` + `SctBaseForm` | 新增/编辑流程工序 | `workingsubclass_id``code``name``pin_check` | 工序新增编辑 |
| `TechnologyFlowModel` | 设定值插件容器 | `type``code``pluginData``rowData``processArr``is_binding_batch` | 依据工序插件类型编辑设定值 |
| `ResultParam` | 查看并绑定流程工序结果参数 | `flow_process_id``workingsubclass_id` | 结果参数弹窗 |
| `TemperatureSupp` | 温度补偿工步范围、补偿值维护、Excel 导入、模板下载 | `flow_process_id` | 化成/分容等工序温度补偿 |
| `CalculationScript` | 计算脚本列表、脚本新增/编辑/删除、接口位置配置 | `flow_process_id` | 流程工序计算脚本配置 |
| 日志抽屉 | 展示 `technology_flow_operate_log` | `nyr_date``week``username``action_name``process_name` | 查看流程卡操作日志 |
## 四、已迁移/本轮补齐内容
| 内容 | 现状 | 本轮处理 |
| --- | --- | --- |
| 工艺流程主表 CRUD/复制 | V2 已实现 | 保留 |
| 流程卡入口 | V2 已实现 | 保留旧路由 `/production_configuration/technology_model/technology_flow/:flow_id/process` |
| 流程卡工序新增/编辑/删除/排序 | V2 已实现 | 保留并修正按钮显示规则 |
| 设定值 | V2 已实现简化 JSON 版本 | 保留,继续使用 `set_setting` |
| 结果参数 | V2 误用工序单元参数组件,缺少 `flow_process_id` 绑定 | 新增流程卡专用 `result-param.vue`,按 `process_id` 查询与绑定 |
| 温度补偿 | V2 仅提示“暂未接入” | 新增 `temperature-compensation.vue`,恢复查询、工步范围、维护、导入和模板下载 |
| 计算脚本 | V2 按钮隐藏,仅提示“暂未接入” | 新增 `calculation-script.vue` 和 API恢复脚本 CRUD |
| 接口封装 | V2 缺少多个旧端点 | 补齐 `process-routing-card.js` 和新增 `calculation-script.js` |
| i18n | V2 缺少插件弹窗文本 | 补齐中英文语言项 |
## 五、迁移优先级与依赖
| 优先级 | 内容 | 依赖 | 原因 |
| --- | --- | --- | --- |
| P0 | 流程卡工序列表、排序、新增编辑删除 | `technology_flow_process/all/create/edit/delete/move_*` | 流程卡核心作业入口 |
| P0 | 结果参数按流程工序绑定 | `get_optional_params_details``get_all_workingsubclass_params``add_optional_params` | 生产下发/报表字段依赖结果参数 |
| P1 | 温度补偿 | `get_step``get_temperature_list``create_temperature``get_temperature_template``xlsx` 工具 | 化成/分容等工序需要温度修正 |
| P1 | 计算脚本 | `calculation_script/all/create/edit/delete` | 工序接口前后置计算依赖 |
| P2 | 设定值插件细分 UI | 旧 `TechnologyFlowModel` 各插件 | 当前 V2 为 JSON 维护,后续可恢复各插件可视化表单 |
| P2 | 日志详情“查看数据” | 操作日志明细接口待确认 | 旧页面仅展示入口文本,实际明细未完整实现 |
## 六、风险与后续建议
- 当前 V2 设定值仍是 JSON 编辑器,不是旧系统按插件类型渲染的专用表单;若业务用户依赖可视化配置,应后续迁移 `SelectionPlugin``FormationPlugin``OcvrPlugin` 等插件。
- 计算脚本编辑器使用文本域替代旧 Ace 包装组件,功能可提交,但代码高亮/格式化体验弱于旧系统。
- 温度补偿 Excel 导入按旧模板中文列名“温度 / 温度补偿值”解析,需要测试真实模板兼容性。

View File

@@ -0,0 +1,580 @@
# 功能测试文档 — 工艺流程 (Process Routing)
> 模块路径:生产配置 → 工艺模型 → **工艺流程**
> 路由:`/production_configuration/technology_model/technology_flow`
> API BASE`production_configuration/technology_model/technology_flow/`
> i18n 前缀:`page.production_master_data.process_model.process_routing`
> 文档版本v1.0
> 适用代码版本:基于 [process-routing/index.vue](file:///d:/code/mes/mes-ui/src/views/production-master-data/process-model/process-routing/index.vue)
> 测试目标:验证工艺流程页面的列表、查询、新增、编辑、删除、流程卡管理入口等核心操作
---
## 0. 文档使用说明
- 测试人员按顺序执行 **T-XX** 编号的测试用例
- 每个用例的 **预期结果** 描述必须与实际界面/接口响应一致才可标记为通过
- 失败的用例在 **问题描述** 列填写具体错误信息、截图编号、操作环境
- 所有"实际结果"必须如实记录,禁止空填或留白
- 整体测试结论位于文末 **测试总结**
---
## 1. 测试环境配置要求
| 项目 | 要求 |
| --- | --- |
| 操作系统 | Windows 10 / 11 64-bit |
| Node.js | **18.16.0**(与项目 `.node-version` / `package.json#engines` 一致) |
| 包管理器 | **pnpm 10.33.0**(与 `package.json#packageManager` 一致) |
| 浏览器 | Chrome 100+ / Edge 100+ |
| 后端服务 | Webman 后端已启动且数据库已初始化 |
| 数据库 | 工艺流程、流程类别、产品、权限等基础数据已存在 |
| 启动命令 | `pnpm install``pnpm dev`,访问 `http://localhost:8080` |
| 测试账号 | 具备「生产配置-工艺模型-工艺流程」读写权限的账号 |
| 浏览器语言 | 中英文均需验证(默认以中文为准) |
---
## 2. 测试前置条件
1. 已使用具备 `production_configuration/technology_model/technology_flow` 下 create / edit / delete 权限的账号登录
2. **流程类别** 至少存在 1 条数据(否则新增时的「流程类别」下拉为空)
3. **产品列表** 至少存在 1 条数据(否则新增时的「关联产品」下拉为空)
4. 浏览器开发者工具 Network 面板可正常观察接口请求
5. 已确认 `src/locales/zh-chs.json``src/locales/en.json` 中存在 `page.production_master_data.process_model.process_routing` 节点
---
## 3. 测试用例
### 3.1 列表与查询
#### T-01页面正常加载
- **操作步骤**
1. 登录系统
2. 进入「生产配置 → 工艺模型 → 工艺流程」
- **预期结果**
- URL 跳转到 `/production_configuration/technology_model/technology_flow`
- 页面顶部展示搜索区(编码、名称、查询、重置)
- 下方展示表格列:序号 / 工艺流程编码 / 工艺流程名称 / 流程类别 / 关联产品 / 版本 / 状态 / 备注 / 操作
- 工具栏右上角显示帮助按钮,鼠标悬停提示「工艺流程用于设置生产工艺流程并管理流程卡…」
- 控制台无报错
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-02默认分页加载列表
- **操作步骤**
1. 访问工艺流程页面
- **预期结果**
- 列表自动加载(无需手动点击查询)
- 加载时表格显示 loading 遮罩
- 加载完成显示数据行(如有数据)
- 分页组件显示「共 X 条」并按 10 条/页 显示
- 发起请求 `GET production_configuration/technology_model/technology_flow/list?method=production_master_data_process_model_process_routing_list&page_no=1&page_size=10&code=&name=`
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-03按编码精确搜索
- **操作步骤**
1. 在「工艺流程编码」输入框输入已存在的完整编码
2. 点击「查询」按钮
- **预期结果**
- 列表仅展示编码完全匹配的数据行
- 列表刷新到第 1 页
- 接口请求参数 `code=<已输入值>`
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-04按名称模糊搜索
- **操作步骤**
1. 在「工艺流程名称」输入框输入名称片段
2. 点击「查询」按钮
- **预期结果**
- 列表仅展示名称包含该片段的数据行
- 接口请求参数 `name=<已输入值>`
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-05按回车键触发查询
- **操作步骤**
1. 在「工艺流程编码」或「工艺流程名称」输入框聚焦
2. 输入关键字后按键盘 `Enter`
- **预期结果**
- 等同于点击「查询」按钮,触发列表刷新
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-06重置搜索条件
- **操作步骤**
1. 在搜索框输入任意条件并点击「查询」
2. 点击「重置」按钮
- **预期结果**
- 两个搜索输入框被清空
- 列表自动以空条件重新加载
- 页码回到第 1 页
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-07分页切换
- **操作步骤**
1. 准备超过 10 条工艺流程数据
2. 在分页器中切换到第 2 页或修改每页条数
- **预期结果**
- 列表按新页码 / 每页条数重新加载
- 接口请求参数 `page_no` / `page_size` 与界面一致
- 分页总数保持正确
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-08空数据展示
- **操作步骤**
1. 在没有任何工艺流程数据时访问页面,或筛选一个不存在的数据
- **预期结果**
- 表格区域显示 Element UI 空数据占位(无红色错误)
- 分页器显示「共 0 条」
- 不会弹出错误提示
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-09i18n 中文切换
- **操作步骤**
1. 在用户设置中切换语言到「简体中文」
2. 访问工艺流程页面
- **预期结果**
- 所有表头、按钮、提示文本为中文("工艺流程编码"、"新增"、"删除"等)
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-10i18n 英文切换
- **操作步骤**
1. 切换语言到「English」
2. 访问工艺流程页面
- **预期结果**
- 所有表头、按钮、提示文本为英文("Routing Code"、"Add"、"Delete" 等)
- 切换瞬间不需要刷新页面即生效
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
---
### 3.2 新增
#### T-11打开新增弹窗
- **操作步骤**
1. 点击工具栏的「新增」按钮
- **预期结果**
- 弹出标题为「新增工艺流程」的对话框(宽 40%
- 表单为空且编码字段可编辑
- 默认值:版本 = `1.0`、状态 = 启用
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-12新增必填校验 — 编码
- **操作步骤**
1. 打开新增弹窗
2. 编码留空,其它字段填写完整
3. 点击「确定」
- **预期结果**
- 编码输入框下方红色提示「请输入工艺流程编码」
- 接口未发起,弹窗保持打开
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-13新增必填校验 — 名称
- **操作步骤**
1. 打开新增弹窗
2. 名称留空,其它字段填写完整
3. 点击「确定」
- **预期结果**
- 名称输入框下方红色提示「请输入工艺流程名称」
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-14新增必填校验 — 流程类别
- **操作步骤**
1. 打开新增弹窗
2. 不选择「流程类别」
3. 点击「确定」
- **预期结果**
- 流程类别下拉框下方红色提示「请选择流程类别」
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-15新增必填校验 — 版本
- **操作步骤**
1. 打开新增弹窗
2. 清空「版本」(默认 `1.0`
3. 点击「确定」
- **预期结果**
- 版本输入框下方红色提示「请输入版本号」
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-16新增字段长度校验
- **操作步骤**
1. 打开新增弹窗
2. 在「编码」输入超过 100 个字符
3. 点击「确定」
- **预期结果**
- 编码输入框下方红色提示「长度在 1 到 100 个字符」
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-17新增成功
- **操作步骤**
1. 打开新增弹窗
2. 填写完整有效数据:编码、名称、流程类别、关联产品、版本、状态、备注
3. 点击「确定」
- **预期结果**
- 接口请求 `POST production_configuration/technology_model/technology_flow/create` 状态码 200`code=0`
- 页面弹出绿色提示「操作成功」
- 弹窗自动关闭
- 列表自动刷新,新数据展示在表格中
- 页码回到第 1 页
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-18新增重复编码
- **操作步骤**
1. 准备一个已存在的编码
2. 在新增弹窗中输入相同编码
3. 点击「确定」
- **预期结果**
- 后端返回业务错误
- 页面弹出红色错误提示(如「编码已存在」)
- 弹窗保持打开,用户可修改后重试
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-19取消新增
- **操作步骤**
1. 打开新增弹窗,填写部分数据
2. 点击「取消」按钮或右上角 X
- **预期结果**
- 弹窗关闭
- 不发起任何接口请求
- 再次打开新增弹窗,表单恢复为空(默认值除外)
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-20新增提交中重复点击防护
- **操作步骤**
1. 打开新增弹窗,填写有效数据
2. 连续快速点击「确定」两次
- **预期结果**
- 第一次点击后按钮进入 loading 状态,禁用重复提交
- 接口仅被调用 1 次
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
---
### 3.3 编辑
#### T-21打开编辑弹窗
- **操作步骤**
1. 在列表中点击某行的「编辑」按钮
- **预期结果**
- 弹出标题为「编辑工艺流程」的对话框
- 表单自动回填该行数据
- 「编码」字段为禁用状态(不可编辑)
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-22编辑必填校验
- **操作步骤**
1. 打开编辑弹窗
2. 清空「名称」
3. 点击「确定」
- **预期结果**
- 名称输入框下方红色提示「请输入工艺流程名称」
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-23编辑成功
- **操作步骤**
1. 打开编辑弹窗
2. 修改名称、流程类别、版本、状态、备注等字段
3. 点击「确定」
- **预期结果**
- 接口请求 `PUT production_configuration/technology_model/technology_flow/edit` 状态码 200`code=0`
- 页面弹出绿色提示「操作成功」
- 弹窗关闭
- 列表自动刷新,目标行显示新值
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-24编辑时编码不可修改
- **操作步骤**
1. 打开编辑弹窗
2. 尝试修改「编码」字段
- **预期结果**
- 编码字段处于禁用状态,无法输入
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-25取消编辑
- **操作步骤**
1. 打开编辑弹窗,修改若干字段
2. 点击「取消」按钮
- **预期结果**
- 弹窗关闭
- 不发起任何接口请求
- 列表数据保持原样
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
---
### 3.4 删除
#### T-26删除二次确认弹窗
- **操作步骤**
1. 点击某行的「删除」按钮
- **预期结果**
- 弹出警告提示「确定要删除该工艺流程吗?」
- 提示框有「确定」与「取消」两个按钮
- 列表中的该行仍存在
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-27删除 — 取消
- **操作步骤**
1. 点击「删除」按钮
2. 在确认弹窗中点击「取消」
- **预期结果**
- 弹窗关闭
- 不发起删除接口
- 数据行保持原样
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-28删除成功
- **操作步骤**
1. 点击「删除」按钮
2. 在确认弹窗中点击「确定」
- **预期结果**
- 接口请求 `DELETE production_configuration/technology_model/technology_flow/delete` 状态码 200`code=0`
- 页面弹出绿色提示「操作成功」
- 列表自动刷新,目标行消失
- 删除当前页最后一行时,自动跳转到合理页码
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-29删除 — 关联数据拒绝
- **操作步骤**
1. 准备一条已被产品、批次或其它模块引用的工艺流程
2. 尝试删除
- **预期结果**
- 后端返回业务错误
- 页面弹出红色错误提示(如「该工艺流程已被引用,不可删除」)
- 列表数据保持原样
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
---
### 3.5 流程卡管理入口
#### T-30流程卡管理按钮存在
- **操作步骤**
1. 在列表中查看任意一行的操作列
- **预期结果**
- 操作列包含「编辑」、「流程卡管理」、「删除」3 个按钮
- 「流程卡管理」按钮为黄色文字 + `el-icon-tickets` 图标
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-31点击流程卡管理
- **操作步骤**
1. 点击某行的「流程卡管理」按钮
- **预期结果**
- 当前阶段:弹出信息提示「流程卡管理: <工艺流程名称>」(占位实现)
- 后续阶段:应打开独立的流程卡管理抽屉 / 弹窗
- 不影响列表状态
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
---
### 3.6 权限控制
#### T-32无「新增」权限
- **操作步骤**
1. 使用无 `/production_configuration/technology_model/technology_flow/create` 权限的账号登录
2. 访问工艺流程页面
- **预期结果**
- 工具栏不显示「新增」按钮
- 列表正常加载
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-33无「编辑」权限
- **操作步骤**
1. 使用无 `…/edit` 权限的账号登录
2. 访问工艺流程页面
- **预期结果**
- 行内「编辑」按钮不显示
- 「新增」与「删除」按钮仍可正常显示(按权限配置)
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-34无「删除」权限
- **操作步骤**
1. 使用无 `…/delete` 权限的账号登录
2. 访问工艺流程页面
- **预期结果**
- 行内「删除」按钮不显示
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-35未登录访问
- **操作步骤**
1. 退出登录后,直接在地址栏访问 `/production_configuration/technology_model/technology_flow`
- **预期结果**
- 自动跳转到登录页
- 不会出现 404 或白屏
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
---
### 3.7 错误处理
#### T-36网络异常
- **操作步骤**
1. 在浏览器开发者工具 Network 面板中离线Offline
2. 刷新工艺流程页面
- **预期结果**
- 页面顶部红色错误提示(如「请求超时」或「网络异常」)
- loading 状态关闭
- 表格维持之前的数据或显示空数据
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-37后端 500 错误
- **操作步骤**
1. 模拟后端列表接口返回 500
2. 刷新工艺流程页面
- **预期结果**
- 拦截器统一弹出红色错误提示
- 表格显示空数据
- 控制台无未捕获异常
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
#### T-38参数缺失
- **操作步骤**
1. 模拟后端创建接口缺参返回 `code=500`
2. 在新增弹窗点击「确定」
- **预期结果**
- 拦截器弹出后端返回的错误信息
- 弹窗不关闭
- 表单数据保持原样
- **实际结果**__________
- **测试状态**:☐ 通过 ☐ 失败
- **问题描述**__________
---
## 4. 测试总结
| 维度 | 统计 |
| --- | --- |
| 用例总数 | 38 |
| 通过数 | __________ |
| 失败数 | __________ |
| 阻塞数 | __________ |
| 跳过数 | __________ |
| 整体结论 | ☐ 通过 ☐ 有条件通过 ☐ 不通过 |
### 4.1 遗留问题清单
| 编号 | 用例编号 | 严重程度 | 描述 | 负责人 | 计划修复日期 |
| --- | --- | --- | --- | --- | --- |
| 1 | | | | | |
| 2 | | | | | |
| 3 | | | | | |
### 4.2 测试执行信息
- **测试执行人**__________
- **测试起止时间**__________
- **测试环境版本**Node `18.16.0` / pnpm `10.33.0` / 浏览器 `__________`
- **后端版本 / 提交**__________
- **测试账号**__________
---
## 5. 附录
### 5.1 关键文件
| 类型 | 路径 |
| --- | --- |
| 主页面 | [process-routing/index.vue](file:///d:/code/mes/mes-ui/src/views/production-master-data/process-model/process-routing/index.vue) |
| API 文件 | [process-routing.js](file:///d:/code/mes/mes-ui/src/api/production-master-data/process-routing.js) |
| 路由模块 | [production-master-data.js](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js) |
| 中文语言包 | [zh-chs.json#process_routing](file:///d:/code/mes/mes-ui/src/locales/zh-chs.json) |
| 英文语言包 | [en.json#process_routing](file:///d:/code/mes/mes-ui/src/locales/en.json) |
### 5.2 关键接口
| 操作 | Method | URL |
| --- | --- | --- |
| 查询列表 | GET | `production_configuration/technology_model/technology_flow/list` |
| 新增 | POST | `production_configuration/technology_model/technology_flow/create` |
| 编辑 | PUT | `production_configuration/technology_model/technology_flow/edit` |
| 删除 | DELETE | `production_configuration/technology_model/technology_flow/delete` |
公共参数:`method=production_master_data_process_model_process_routing_<action>``platform=background`
### 5.3 字段说明
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| code | string | 是 | 工艺流程编码,唯一,创建后不可修改 |
| name | string | 是 | 工艺流程名称 |
| category_id | int | 是 | 流程类别 ID关联工艺流程类别 |
| product_id | int | 否 | 关联产品 ID关联产品列表 |
| version | string | 是 | 版本号,默认 `1.0` |
| status | int | 是 | 1=启用0=禁用 |
| remark | string | 否 | 备注 |

View File

@@ -0,0 +1,245 @@
# 质量管理功能测试任务列表
## 检验类别管理 (Inspection Type Management)
- [ ] 通过菜单或地址 /quality_control/first_inspection/category 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## 首巡检项目配置 (First Article Inspection Configuration)
- [ ] 通过菜单或地址 /quality_control/first_inspection/setting 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## 首巡检录入 (First Article Inspection Records)
- [ ] 通过菜单或地址 /quality_control/first_inspection/input 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## 首巡检报表 (First Article Inspection Reports)
- [ ] 通过菜单或地址 /quality_control/first_inspection/report 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
## 检验单管理 (Inspection Orders)
- [ ] 通过菜单或地址 /quality_control/xqc/inspection_order_manage 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## 检验标准 (Inspection Standards)
- [ ] 通过菜单或地址 /quality_control/xqc/inspection_standard 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## 接收质量限 (AQL Standards)
- [ ] 通过菜单或地址 /quality_control/xqc/aql_config 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## 检测方案维护 (Inspection Plans)
- [ ] 通过菜单或地址 /quality_control/xqc/inspection_plan 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## 检验项目 (Inspection Items)
- [ ] 通过菜单或地址 /quality_control/xqc/inspection_item 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## 抽样方案配置 (Sampling Plans)
- [ ] 通过菜单或地址 /quality_control/xqc/sampling_plan 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## SPC渲染条件配置 (SPC Configuration)
- [ ] 通过菜单或地址 /spc/management 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 新增一条测试数据,确认必填校验、提交成功提示和列表刷新正常。
- [ ] 编辑刚新增的数据,确认回显、保存和刷新后的字段值正确。
- [ ] 删除测试数据,确认二次确认弹窗、删除请求和列表刷新正常。
## XBar-R (XBar-R Chart)
- [ ] 通过菜单或地址 /spc/chart/XBar-R 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## XBar-S (XBar-S Chart)
- [ ] 通过菜单或地址 /spc/chart/XBar-S 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## I-MR (I-MR Chart)
- [ ] 通过菜单或地址 /spc/chart/I-MR 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## Levey-Jennings (Levey-Jennings Chart)
- [ ] 通过菜单或地址 /spc/chart/Levey-Jennings 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## EWMA (EWMA Chart)
- [ ] 通过菜单或地址 /spc/chart/EWMA 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## CUSUM (CUSUM Chart)
- [ ] 通过菜单或地址 /spc/chart/CUSUM 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## MA (Moving Average Chart)
- [ ] 通过菜单或地址 /spc/chart/MA 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## MAMR (Moving Average MR Chart)
- [ ] 通过菜单或地址 /spc/chart/MAMR 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## MAMS (Moving Average S Chart)
- [ ] 通过菜单或地址 /spc/chart/MAMS 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## CPK (Process Capability Index)
- [ ] 通过菜单或地址 /spc/chart/Cpk 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## DPMO (DPMO)
- [ ] 通过菜单或地址 /spc/chart/DPMO 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## PChart (P Chart)
- [ ] 通过菜单或地址 /spc/chart/PChart 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## NPChart (NP Chart)
- [ ] 通过菜单或地址 /spc/chart/NPChart 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## CChart (C Chart)
- [ ] 通过菜单或地址 /spc/chart/CChart 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。
## UChart (U Chart)
- [ ] 通过菜单或地址 /spc/chart/UChart 打开页面,确认页面标题、查询区和列表区域正常显示。
- [ ] 输入主要查询条件后点击查询,确认请求参数和列表刷新符合旧系统字段。
- [ ] 点击重置,确认查询条件清空并回到第一页。
- [ ] 切换分页大小和页码,确认列表数据、总数和加载状态正常。
- [ ] 对照旧系统同一查询条件,确认报表/只读列表的关键字段展示一致。
- [ ] 查询有样本数据的 SPC 项目,确认折线图区域和明细表同步展示,并且无样本时页面不报错。

105
docs/迁移任务列表.md Normal file
View File

@@ -0,0 +1,105 @@
# MES V2 迁移任务列表
> 根据 `后台Webman界面截图对照表.md` 生成。状态以当前 V2 项目中已落地的页面目录为准。
- 总功能数91
- 已迁移79
- 未迁移12
| 状态 | 一级模块 | 二级模块 | 三级模块 | 功能说明 | V2 目标路径 |
|:---:|---|---|---|---|---|
| ✅ | 系统设置 (System Administration) | 用户管理 (User Management) | 角色 (Role) | 设置用户组并分配权限(增删改查用户组,并实现权限分配功能) | `src/views/system-administration/user-management/role/` |
| ✅ | 系统设置 (System Administration) | 用户管理 (User Management) | 用户 (User) | 管理用户账号并绑定对应角色 | `src/views/system-administration/user-management/user/` |
| ✅ | 系统设置 (System Administration) | 菜单管理 (Menu Management) | 菜单配置 (Menu Configuration) | 系统菜单配置 | `src/views/system-administration/menu-management/menu-configuration/` |
| ✅ | 系统设置 (System Administration) | 系统助手 (System Utilities) | 操作日志 (Operation Logs) | 系统操作日志 | `src/views/system-administration/system-utilities/operation-logs/` |
| ✅ | 系统设置 (System Administration) | 系统助手 (System Utilities) | 接口日志 (API Logs) | 与设备对接流程交互日志(支持按 IP 和接口名称查询) | `src/views/system-administration/system-utilities/api-logs/` |
| ✅ | 系统设置 (System Administration) | 系统监控 (System Monitoring) | 监控设置 (Monitoring Configuration) | 系统监控配置 | `src/views/system-administration/system-monitoring/monitoring-configuration/` |
| ✅ | 生产配置 (Production Master Data) | 工厂模型 (Factory Model) | 产线设置 (Production Line) | 管理产线(支持增删改查) | `src/views/production-master-data/factory-model/production-line/` |
| ✅ | 生产配置 (Production Master Data) | 工厂模型 (Factory Model) | 工厂区域 (Factory Area) | 管理工厂区域(支持增删改查) | `src/views/production-master-data/factory-model/factory-area/` |
| ✅ | 生产配置 (Production Master Data) | 工艺模型 (Process Model) | 工艺流程类别 (Process Category) | 工艺流程类别的增删改查 | `src/views/production-master-data/process-model/process-category/` |
| ✅ | 生产配置 (Production Master Data) | 工艺模型 (Process Model) | 工序单元 (Process Step) | 配置工序单元(节点),支持参数预设 | `src/views/production-master-data/process-model/process-step/` |
| ✅ | 生产配置 (Production Master Data) | 工艺模型 (Process Model) | 工艺流程 (Process Routing) | 设置生产工艺流程并管理流程卡 | `src/views/production-master-data/process-model/process-routing/` |
| ✅ | 生产配置 (Production Master Data) | 产品管理 (Product Management) | 产品列表 (Product List) | 产品管理(增删改查) | `src/views/production-master-data/product-management/product-list/` |
| ✅ | 生产配置 (Production Master Data) | 产品管理 (Product Management) | 不良管理 (Defect Management) | 不良代码及描述管理,支持批量导入 | `src/views/production-master-data/product-model/product-ng-info/` |
| ✅ | 生产配置 (Production Master Data) | 物料模型 (Material Model) | 物料类别列表 (Material Category) | 区分原材料和半成品 | `src/views/production-master-data/material-model/material-category/` |
| ✅ | 生产配置 (Production Master Data) | 物料模型 (Material Model) | 物料信息管理 (Material Master) | 维护物料编码、名称、规格等属性 | `src/views/production-master-data/material-model/material-master/` |
| ✅ | 生产配置 (Production Master Data) | 物料模型 (Material Model) | BOM物料清单 (Bill of Materials) | 产品BOM管理 | `src/views/production-master-data/material-model/bill-of-materials/` |
| ✅ | 生产配置 (Production Master Data) | 物料模型 (Material Model) | 计量单位 (Unit of Measure) | 计量单位配置与管理 | `src/views/production-master-data/material-model/material-unit/` |
| ✅ | 生产配置 (Production Master Data) | SPC采集模型 (SPC Configuration) | SPC采集配置 (Data Collection Configuration) | 配置SPC采集参数 | `src/views/production-master-data/spc-configuration/data-collection-configuration/` |
| ✅ | 生产配置 (Production Master Data) | 班组模型 (Team Model) | 班组管理 (Team Management) | 管理生产班组 | `src/views/production-master-data/team-model/team-management/` |
| ✅ | 生产配置 (Production Master Data) | 班组模型 (Team Model) | 班次管理 (Shift Management) | 管理生产班次 | `src/views/production-master-data/team-model/shift-management/` |
| ✅ | 生产配置 (Production Master Data) | 班组模型 (Team Model) | 排班日历 (Scheduling Calendar) | 查看排班日历 | `src/views/production-master-data/team-model/scheduling-calendar/` |
| ✅ | 设备模型 (Equipment Management) | 设备类别 (Equipment Category) | 设备类别 (Equipment Category) | 管理设备类别 | `src/views/equipment-management/equipment-model/equipment-category/` |
| ✅ | 设备模型 (Equipment Management) | 设备信息 (Equipment Management) | 设备信息 (Equipment Registry) | 管理设备信息 | `src/views/equipment-management/equipment-model/equipment-registry/` |
| ✅ | 设备模型 (Equipment Management) | 设备点检 (Inspection Management) | 设备点检项目 (Inspection Items) | 点检项目管理 | `src/views/equipment-management/inspection-management/inspection-items/` |
| ✅ | 设备模型 (Equipment Management) | 设备点检 (Inspection Management) | 设备点检记录 (Inspection Records) | 点检记录管理 | `src/views/equipment-management/inspection-management/inspection-records/` |
| ✅ | 设备模型 (Equipment Management) | 设备点检 (Inspection Management) | 设备点检日志 (Inspection Logs) | 点检日志查询 | `src/views/equipment-management/inspection-management/inspection-logs/` |
| ✅ | 设备模型 (Equipment Management) | 设备保养 (Maintenance Management) | 设备保养项目 (Maintenance Items) | 保养项目管理 | `src/views/equipment-management/maintenance-management/maintenance-items/` |
| ✅ | 设备模型 (Equipment Management) | 设备保养 (Maintenance Management) | 设备保养详情 (Maintenance Details) | 保养详情管理 | `src/views/equipment-management/maintenance-management/maintenance-details/` |
| ✅ | 设备模型 (Equipment Management) | 设备保养 (Maintenance Management) | 设备保养日志 (Maintenance Logs) | 保养日志查询 | `src/views/equipment-management/maintenance-management/maintenance-logs/` |
| ✅ | 设备模型 (Equipment Management) | 设备维修 (Repair Management) | 设备维修管理 (Repair Management) | 维修管理 | `src/views/equipment-management/repair-management/repair-management/` |
| ✅ | 设备模型 (Equipment Management) | 设备维修 (Repair Management) | 设备维修日志 (Repair Logs) | 维修日志查询 | `src/views/equipment-management/repair-management/repair-logs/` |
| ✅ | 设备模型 (Equipment Management) | 设备损耗品 (Consumables Management) | 设备损耗品类别 (Consumables Category) | 损耗品分类管理 | `src/views/equipment-management/consumables-management/consumables-category/` |
| ✅ | 设备模型 (Equipment Management) | 设备损耗品 (Consumables Management) | 设备损耗品项目 (Consumables Items) | 损耗品项目管理 | `src/views/equipment-management/consumables-management/consumables-items/` |
| ✅ | 设备模型 (Equipment Management) | 设备损耗品 (Consumables Management) | 设备损耗品寿命管理 (Consumables Lifecycle Management) | 寿命管理 | `src/views/equipment-management/consumables-management/consumables-lifecycle/` |
| ✅ | 设备模型 (Equipment Management) | 设备损耗品 (Consumables Management) | 设备损耗品更换日志 (Replacement Logs) | 更换记录查询 | `src/views/equipment-management/consumables-management/replacement-logs/` |
| ✅ | 计划与生产 (Planning & Production) | 生产批次管理 (Batch Management) | 批次列表 (Batch List) | 批次管理 | `src/views/planning-production/batch-management/batch-list/` |
| ✅ | 计划与生产 (Planning & Production) | 生产批次管理 (Batch Management) | 批次托盘 (Tray Tracking) | 托盘跟踪与操作 | `src/views/planning-production/batch-management/tray-tracking/` |
| ✅ | 计划与生产 (Planning & Production) | 生产批次管理 (Batch Management) | 生产批次不良报表 (Batch Defect Report) | 不良报表 | `src/views/planning-production/batch-management/batch-defect-report/` |
| ✅ | 计划与生产 (Planning & Production) | 预警中心 (Alert Center) | 预警中心 (Alert Center) | 预警中心 | `src/views/planning-production/alert-center/` |
| ✅ | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 物料监控 (Material Monitoring) | 物料监控 | `src/views/planning-production/production-monitoring/material-monitoring/` |
| ✅ | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 电池复投管理 (Rework Management) | 返工管理 | `src/views/planning-production/production-monitoring/rework-management/` |
| ✅ | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 托盘管理 (Tray Management) | 托盘管理 | `src/views/planning-production/production-monitoring/tray-management/` |
| ✅ | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 托盘登录 (Tray Registration) | 托盘登记 | `src/views/planning-production/production-monitoring/tray-registration/` |
| ✅ | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 设备监控 (Equipment Monitoring) | 设备监控 | `src/views/planning-production/production-monitoring/equipment-monitoring/` |
| ✅ | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 电池工序管理 (Process Execution) | 工序管理 | `src/views/planning-production/production-monitoring/process-execution/` |
| ✅ | 质量管理 (Quality Management) | 过程控制 (Process Control) | 检验类别管理 (Inspection Type Management) | | `src/views/quality-management/process-control/inspection-type-management/` |
| ✅ | 质量管理 (Quality Management) | 过程控制 (Process Control) | 首巡检项目配置 (First Article Inspection Configuration) | | `src/views/quality-management/process-control/first-article-inspection-configuration/` |
| ✅ | 质量管理 (Quality Management) | 过程控制 (Process Control) | 首巡检录入 (First Article Inspection Records) | | `src/views/quality-management/process-control/first-article-inspection-records/` |
| ✅ | 质量管理 (Quality Management) | 过程控制 (Process Control) | 首巡检报表 (First Article Inspection Reports) | | `src/views/quality-management/process-control/first-article-inspection-reports/` |
| ✅ | 质量管理 (Quality Management) | 检验控制 (Inspection Management) | 检验单管理 (Inspection Orders) | | `src/views/quality-management/inspection-management/inspection-orders/` |
| ✅ | 质量管理 (Quality Management) | 检验控制 (Inspection Management) | 检验标准 (Inspection Standards) | | `src/views/quality-management/inspection-management/inspection-standards/` |
| ✅ | 质量管理 (Quality Management) | 检验控制 (Inspection Management) | 接收质量限 (AQL Standards) | | `src/views/quality-management/inspection-management/aql-standards/` |
| ✅ | 质量管理 (Quality Management) | 检验控制 (Inspection Management) | 检测方案维护 (Inspection Plans) | | `src/views/quality-management/inspection-management/inspection-plans/` |
| ✅ | 质量管理 (Quality Management) | 检验控制 (Inspection Management) | 检验项目 (Inspection Items) | | `src/views/quality-management/inspection-management/inspection-items/` |
| ✅ | 质量管理 (Quality Management) | 检验控制 (Inspection Management) | 抽样方案配置 (Sampling Plans) | | `src/views/quality-management/inspection-management/sampling-plans/` |
| ✅ | 质量管理 (Quality Management) | SPC统计过程控制 (SPC Control) | SPC渲染条件配置 (SPC Configuration) | | `src/views/quality-management/spc-control/spc-configuration/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | XBar-R (XBar-R Chart) | | `src/views/quality-management/spc-variable-charts/xbar-r/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | XBar-S (XBar-S Chart) | | `src/views/quality-management/spc-variable-charts/xbar-s/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | I-MR (I-MR Chart) | | `src/views/quality-management/spc-variable-charts/i-mr/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | Levey-Jennings (Levey-Jennings Chart) | | `src/views/quality-management/spc-variable-charts/levey-jennings/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | EWMA (EWMA Chart) | | `src/views/quality-management/spc-variable-charts/ewma/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | CUSUM (CUSUM Chart) | | `src/views/quality-management/spc-variable-charts/cusum/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | MA (Moving Average Chart) | | `src/views/quality-management/spc-variable-charts/ma/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | MAMR (Moving Average MR Chart) | | `src/views/quality-management/spc-variable-charts/mamr/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | MAMS (Moving Average S Chart) | | `src/views/quality-management/spc-variable-charts/mams/` |
| ✅ | 质量管理 (Quality Management) | SPC计量型报表 (SPC Variable Charts) | CPK (Process Capability Index) | | `src/views/quality-management/spc-variable-charts/cpk/` |
| ✅ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | DPMO (DPMO) | | `src/views/quality-management/spc-attribute-charts/dpmo/` |
| ✅ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | PChart (P Chart) | | `src/views/quality-management/spc-attribute-charts/pchart/` |
| ✅ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | NPChart (NP Chart) | | `src/views/quality-management/spc-attribute-charts/npchart/` |
| ✅ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | CChart (C Chart) | | `src/views/quality-management/spc-attribute-charts/cchart/` |
| ✅ | 质量管理 (Quality Management) | SPC计数型报表 (SPC Attribute Charts) | UChart (U Chart) | | `src/views/quality-management/spc-attribute-charts/uchart/` |
| ✅ | 数据中台 (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) | 电池曲线 (Battery Curve) | 电池曲线 | `src/views/data-platform/traceability/battery-curve/` |
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 托盘追溯 (Tray Traceability) | 托盘追溯 | `src/views/data-platform/traceability/tray/` |
| ✅ | 数据中台 (Data Platform) | 基础追溯 (Traceability) | 电池追溯 (Battery Traceability) | 电池追溯 | `src/views/data-platform/traceability/battery/` |
| ✅ | 数据中台 (Data Platform) | 生产报表 (Production Reports) | 设备履历报表 (Equipment History Report) | 设备履历报表 | `src/views/data-platform/production-reports/equipment-history/` |
| ✅ | 数据中台 (Data Platform) | 生产报表 (Production Reports) | 电池详情报表 (Battery Detail Report) | 电池详情报表 | `src/views/data-platform/production-reports/battery-detail/` |
| ✅ | 数据中台 (Data Platform) | 相关性分析 (Correlation Analysis) | 鹰眼 (Hawkeye) | 鹰眼 | `src/views/data-platform/correlation-analysis/hawkeye/` |
| ⬜ | SCADA管理 (SCADA Management) | SCADA管理 (SCADA Management) | SCADA节点配置 (SCADA Node Configuration) | SCADA节点配置功能 | `src/views/scada_manage/basic_configuration/scada_configure` |
| ⬜ | SCADA管理 (SCADA Management) | SCADA管理 (SCADA Management) | SCADA数据查询 (SCADA Data Query) | SCADA数据查询功能 | `src/views/scada_manage/basic_configuration/scada_query` |
| ⬜ | SCADA管理 (SCADA Management) | SCADA管理 (SCADA Management) | 节点设备映射 (Node Device Mapping) | 节点设备映射功能 | `src/views/scada_manage/basic_configuration/node_mapping_device_code` |
| ⬜ | SCADA管理 (SCADA Management) | 环控管理 (Environmental Control Management) | 环控节点配置 (Environmental Control Node Configuration) | 环控节点配置功能 | `src/views/scada_manage/basic_configuration/ems_configure` |
| ⬜ | SCADA管理 (SCADA Management) | 环控管理 (Environmental Control Management) | 环控数据查询 (Environmental Control Data Query) | 环控数据查询功能 | `src/views/scada_manage/basic_configuration/ems_query` |
| ⬜ | SCADA管理 (SCADA Management) | 环控管理 (Environmental Control Management) | 车间配置 (Workshop Configuration) | 车间配置功能 | `src/views/scada_manage/workshop_manage/workshop_config` |
| ⬜ | SCADA管理 (SCADA Management) | 环控管理 (Environmental Control Management) | 车间点位管理 (Workshop Point Management) | 车间点位管理功能 | `src/views/scada_manage/workshop_manage/workshop_point` |
| ⬜ | SCADA管理 (SCADA Management) | 前置采集管理 (Pre-acquisition Management) | 服务配置 (Service Configuration) | 服务配置功能 | `src/views/scada_manage/lecpserver/edgeserverconfigure` |
| ⬜ | SCADA管理 (SCADA Management) | 前置采集管理 (Pre-acquisition Management) | 服务监控 (Service Monitoring) | 服务监控功能 | `src/views/scada_manage/lecpserver/edgeservermonitor` |
| ⬜ | SCADA管理 (SCADA Management) | 前置采集管理 (Pre-acquisition Management) | 前置服务日志 (Pre-service Logs) | 前置服务日志功能 | `src/views/scada_manage/lecpserver/edgeserver_log` |
| ⬜ | SCADA管理 (SCADA Management) | 前置采集管理 (Pre-acquisition Management) | 实时采集监控 (Real-time Gathering Monitoring) | 实时采集监控功能 | `src/views/scada_manage/device_gather/battery_device_gather_monitor` |
## 状态说明
-V2 项目中已存在对应页面,或本轮已完成转换并通过构建验证。
- ⬜:尚未确认/尚未转换到 V2 页面目录。

View File

@@ -12,7 +12,7 @@
- 使用 `useTableColumns()` 生成列定义(不再手动分配 idx - 使用 `useTableColumns()` 生成列定义(不再手动分配 idx
- 使用 `useTableButtons()` 生成工具栏按钮和行内按钮(不再分开写 buttonList / tableButtonList - 使用 `useTableButtons()` 生成工具栏按钮和行内按钮(不再分开写 buttonList / tableButtonList
- 使用 `i18nMixin(prefix)` 注入 `key()``ckey()` 方法 - 使用 `i18nMixin(prefix)` 注入 `key()``ckey()` 方法
- 参考文档:[表格组件使用说明.md](file:///d:/code/mes/mes-ui/docs/表格组件使用说明.md) - 参考文档:[表格组件使用说明.md](/docs/表格组件使用说明.md)
- 参考示例:`src/views/production-master-data/factory-model/factory-area/index.vue` - 参考示例:`src/views/production-master-data/factory-model/factory-area/index.vue`
#### 1.1 特殊弹出框组件迁移(重要) #### 1.1 特殊弹出框组件迁移(重要)
@@ -47,7 +47,7 @@ setTimeout 1000ms 关闭 → 保留(树渲染需要等待),但抽取
``` ```
##### 第三步:按标准实现 ##### 第三步:按标准实现
参考 [表格组件使用说明.md 第 13 节](file:///d:/code/mes/mes-ui/docs/表格组件使用说明.md#13-特殊弹出框组件规范),严格遵循: 参考 [表格组件使用说明.md 第 13 节](/docs/表格组件使用说明.md#13-特殊弹出框组件规范),严格遵循:
| 规则 | 说明 | | 规则 | 说明 |
|------|------| |------|------|
@@ -59,9 +59,9 @@ setTimeout 1000ms 关闭 → 保留(树渲染需要等待),但抽取
##### 第四步:完整案例 ##### 第四步:完整案例
参考角色权限分配抽屉的完整迁移: 参考角色权限分配抽屉的完整迁移:
- 旧代码:`D:\code\company\SCTMES_MES_V5\vue-app\src\views\system_settings\user_management\role\components\PageMain\index.vue``dialogVisibleGive` + `el-tree` - 旧代码:`/home/james/WEBMAN-VUE-APP-develop/src/views/system_settings/user_management/role/components/PageMain/index.vue``dialogVisibleGive` + `el-tree`
- 新代码:[`src/views/system-administration/user-management/role/components/PermDrawer/index.vue`](file:///d:/code/mes/mes-ui/src/views/system-administration/user-management/role/components/PermDrawer/index.vue) - 新代码:[`/home/james/WEBMAN-VUE-APP-V2-develop/mes-ui-d2/src/views/system-administration/user-management/role/components/PermDrawer/index.vue`](/src/views/system-administration/user-management/role/components/PermDrawer/index.vue)
- 主页面引用:[`src/views/system-administration/user-management/role/index.vue`](file:///d:/code/mes/mes-ui/src/views/system-administration/user-management/role/index.vue#L79-L87) - 主页面引用:[`src/views/system-administration/user-management/role/index.vue`](/src/views/system-administration/user-management/role/index.vue#L79-L87)
--- ---
@@ -70,10 +70,10 @@ setTimeout 1000ms 关闭 → 保留(树渲染需要等待),但抽取
- `data()` 中用 `this.key('xxx')` 传完整 i18n key**不要用 `k()` 提前翻译**(翻译由 page-table / page-dialog-form 内部处理,切换语言自动响应) - `data()` 中用 `this.key('xxx')` 传完整 i18n key**不要用 `k()` 提前翻译**(翻译由 page-table / page-dialog-form 内部处理,切换语言自动响应)
- 模板搜索区用 `$t(key('xxx'))`,公共 key 用 `$t(ckey('xxx'))` - 模板搜索区用 `$t(key('xxx'))`,公共 key 用 `$t(ckey('xxx'))`
- 语言包必须中英文同步添加(`zh-chs.json` + `en.json` - 语言包必须中英文同步添加(`zh-chs.json` + `en.json`
- 参考文档:[国际化规则.md](file:///d:/code/mes/mes-ui/docs/国际化规则.md) - 参考文档:[国际化规则.md](/docs/国际化规则.md)
#### 3. 文件夹命名(重要) #### 3. 文件夹命名(重要)
- 文件夹名称遵循 [后台Webman界面截图对照表](file:///d:/code/mes/mes-ui/后台Webman界面截图对照表.md) 的 snake_case/kebab-case 命名 - 文件夹名称遵循 [后台Webman界面截图对照表](/后台Webman界面截图对照表.md) 的 snake_case/kebab-case 命名
- 目录示例:`src/views/production-master-data/factory-model/factory-area/` - 目录示例:`src/views/production-master-data/factory-model/factory-area/`
- 路由模块:`src/router/modules/production-master-data.js` - 路由模块:`src/router/modules/production-master-data.js`
- API 文件:`src/api/production-master-data/factory-area.js` - API 文件:`src/api/production-master-data/factory-area.js`
@@ -121,7 +121,7 @@ setTimeout 1000ms 关闭 → 保留(树渲染需要等待),但抽取
| `page.data_middleground.basic_traceability.*` | `page.data_platform.traceability.*` | | `page.data_middleground.basic_traceability.*` | `page.data_platform.traceability.*` |
#### 6. 处理流程(每迁移一个页面执行以下步骤) #### 6. 处理流程(每迁移一个页面执行以下步骤)
1. **确定对照表位置**:从 [后台Webman界面截图对照表](file:///d:/code/mes/mes-ui/后台Webman界面截图对照表.md) 找到对应行的英文名,转换为 snake_case 1. **确定对照表位置**:从 [后台Webman界面截图对照表](/后台Webman界面截图对照表.md) 找到对应行的英文名,转换为 snake_case
2. **创建目录结构**`src/views/{一级snake_case}/{二级snake_case}/{三级snake_case}/` 2. **创建目录结构**`src/views/{一级snake_case}/{二级snake_case}/{三级snake_case}/`
3. **创建 API 文件**`src/api/{一级snake_case}/{二级snake_case}/{三级snake_case}.js`BASE URL 暂用旧值 3. **创建 API 文件**`src/api/{一级snake_case}/{二级snake_case}/{三级snake_case}.js`BASE URL 暂用旧值
4. **添加路由模块**`src/router/modules/{一级snake_case}.js`path 暂用旧值 4. **添加路由模块**`src/router/modules/{一级snake_case}.js`path 暂用旧值
@@ -141,7 +141,7 @@ setTimeout 1000ms 关闭 → 保留(树渲染需要等待),但抽取
- **Columns**`useTableColumns([...])` - **Columns**`useTableColumns([...])`
- **Methods**`fetchData / onSearch / onReset / onPageChange / openAdd / openEdit / onDialogSubmit / handleDelete` - **Methods**`fetchData / onSearch / onReset / onPageChange / openAdd / openEdit / onDialogSubmit / handleDelete`
- **i18n key 模板**(每个页面至少要有这些):`search / reset / add / edit / delete / operation / add_title / edit_title / code / name / remark / enter_code / enter_name / remark_length / operation_success / confirm / cancel / tip / confirm_delete` - **i18n key 模板**(每个页面至少要有这些):`search / reset / add / edit / delete / operation / add_title / edit_title / code / name / remark / enter_code / enter_name / remark_length / operation_success / confirm / cancel / tip / confirm_delete`
- **搜索条件过多时**参考 [表格组件使用说明.md - 场景 8](file:///d:/code/mes/mes-ui/docs/表格组件使用说明.md#场景-8折叠式搜索区搜索条件过多时),使用 `searchExpanded` + `v-show` 实现折叠式搜索区,需额外添加 i18n key`expand`(展开更多/Expand、`collapse`(收起/Collapse - **搜索条件过多时**参考 [表格组件使用说明.md - 场景 8](/docs/表格组件使用说明.md#场景-8折叠式搜索区搜索条件过多时),使用 `searchExpanded` + `v-show` 实现折叠式搜索区,需额外添加 i18n key`expand`(展开更多/Expand、`collapse`(收起/Collapse
#### 6.1 依赖安装 #### 6.1 依赖安装
@@ -150,7 +150,7 @@ setTimeout 1000ms 关闭 → 保留(树渲染需要等待),但抽取
1. **包管理器**:本项目使用 **pnpm**,安装命令为 `pnpm add <package-name>` 1. **包管理器**:本项目使用 **pnpm**,安装命令为 `pnpm add <package-name>`
2. **安装前检查**:先在 `package.json` 中确认依赖是否已存在,避免重复安装 2. **安装前检查**:先在 `package.json` 中确认依赖是否已存在,避免重复安装
3. **版本兼容**:确保安装的包支持 Vue 2.x本项目为 Vue 2.7 3. **版本兼容**:确保安装的包支持 Vue 2.x本项目为 Vue 2.7
4. **参考标准**[表格组件使用说明.md - 第 11 节](file:///d:/code/mes/mes-ui/docs/表格组件使用说明.md#11-依赖安装规范) 4. **参考标准**[表格组件使用说明.md - 第 11 节](/docs/表格组件使用说明.md#11-依赖安装规范)
--- ---

View File

@@ -1,10 +1,15 @@
{ {
"name": "d2-admin", "name": "d2-admin",
"version": "1.25.0", "version": "1.25.0",
"packageManager": "pnpm@10.33.0",
"engines": {
"node": ">=20.19.0",
"pnpm": ">=10.33.0 <11"
},
"scripts": { "scripts": {
"serve": "vue-cli-service serve --open", "serve": "vue-cli-service serve --open",
"start": "npm run serve", "start": "pnpm run serve",
"dev": "npm run serve", "dev": "pnpm run serve",
"build": "vue-cli-service build --report", "build": "vue-cli-service build --report",
"build:preview": "NODE_OPTIONS=--max_old_space_size=4096 vue-cli-service build --mode preview", "build:preview": "NODE_OPTIONS=--max_old_space_size=4096 vue-cli-service build --mode preview",
"lint": "vue-cli-service lint --fix", "lint": "vue-cli-service lint --fix",
@@ -20,6 +25,7 @@
"core-js": "^3.24.1", "core-js": "^3.24.1",
"countup.js": "^2.3.2", "countup.js": "^2.3.2",
"dayjs": "^1.11.5", "dayjs": "^1.11.5",
"echarts": "5.1.0",
"element-ui": "^2.15.9", "element-ui": "^2.15.9",
"faker": "^4.1.0", "faker": "^4.1.0",
"flex.css": "^1.1.7", "flex.css": "^1.1.7",
@@ -27,7 +33,7 @@
"github-markdown-css": "^4.0.0", "github-markdown-css": "^4.0.0",
"highlight.js": "^10.7.3", "highlight.js": "^10.7.3",
"hotkeys-js": "^3.9.5", "hotkeys-js": "^3.9.5",
"js-cookie": "^2.2.1", "js-cookie": "3.0.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
"marked": "^2.1.3", "marked": "^2.1.3",
@@ -35,6 +41,7 @@
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"qs": "^6.11.0", "qs": "^6.11.0",
"quill": "^1.3.7", "quill": "^1.3.7",
"relation-graph": "2.2.11",
"screenfull": "^5.2.0", "screenfull": "^5.2.0",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"ua-parser-js": "^0.8.1", "ua-parser-js": "^0.8.1",
@@ -46,7 +53,8 @@
"vue-router": "^3.6.2", "vue-router": "^3.6.2",
"vue-splitpane": "^1.0.6", "vue-splitpane": "^1.0.6",
"vue-ueditor-wrap": "^2.5.6", "vue-ueditor-wrap": "^2.5.6",
"vuex": "^3.6.2" "vuex": "^3.6.2",
"xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@d2-projects/vue-filename-injector": "^1.1.1", "@d2-projects/vue-filename-injector": "^1.1.1",
@@ -71,7 +79,7 @@
"eslint-plugin-vue": "^6.2.2", "eslint-plugin-vue": "^6.2.2",
"less": "^3.13.1", "less": "^3.13.1",
"less-loader": "^7.3.0", "less-loader": "^7.3.0",
"sass": "^1.54.5", "sass": "1.54.5",
"sass-loader": "~10.5.2", "sass-loader": "~10.5.2",
"svg-sprite-loader": "^4.3.0", "svg-sprite-loader": "^4.3.0",
"text-loader": "^0.0.1", "text-loader": "^0.0.1",
@@ -88,5 +96,13 @@
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/d2-projects/d2-admin.git" "url": "https://github.com/d2-projects/d2-admin.git"
},
"volta": {
"node": "20.19.0"
},
"pnpm": {
"overrides": {
"@achrinza/node-ipc": ">=9.2.7"
}
} }
} }

849
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View 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)
})
}

View 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') })
}

View File

@@ -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)
})
}

View File

@@ -0,0 +1,27 @@
import { request } from '@/api/_service'
const BASE = 'data_middleground/basic_traceability/reverse_direction_traceability/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getBackwardTraceabilityData (data) {
return request({
url: BASE + 'get_data',
method: 'get',
params: apiParams('data_middleground_basic_traceability_reverse_direction_traceability_get_data', data)
})
}
export function exportBackwardTraceabilityTree (data) {
return request({
url: BASE + 'export_tree',
method: 'post',
data: apiParams('data_middleground_basic_traceability_reverse_direction_traceability_export_tree', data)
})
}

View File

@@ -0,0 +1,35 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/produce/traceability/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getBatteryCurveRecords (data) {
return request({
url: BASE + 'batterytraceability',
method: 'get',
params: apiParams('planning_production_produce_traceability_batterytraceability', data)
})
}
export function getBatteryCurveData (data) {
return request({
url: BASE + 'curveread',
method: 'get',
params: apiParams('planning_production_produce_traceability_batterycurveread', data)
})
}
export function exportBatteryCurveData (data) {
return request({
url: BASE + 'export',
method: 'get',
params: apiParams('planning_production_produce_traceability_export', data)
})
}

View 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)
})
}

View File

@@ -0,0 +1,27 @@
import { request } from '@/api/_service'
const BASE = 'data_middleground/basic_traceability/positive_direction_traceability/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getForwardTraceabilityList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('data_middleground_basic_traceability_positive_direction_traceability_list', data)
})
}
export function createForwardTraceabilityExportTask (data) {
return request({
url: BASE + 'export_task',
method: 'post',
data: apiParams('data_middleground_basic_traceability_positive_direction_traceability_export_task', data)
})
}

View 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)
})
}

View File

@@ -0,0 +1,29 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_consumables/device_consumables_category/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_consumables_device_consumables_category_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_consumables_device_consumables_category_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('device_management_device_consumables_device_consumables_category_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_consumables_device_consumables_category_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('device_management_device_consumables_device_consumables_category_delete', data) })
}
export function createExportTask (data) {
return request({ url: BASE + 'data_export_task', method: 'post', data: params('device_management_device_consumables_device_consumables_category_data_export_task', data) })
}

View File

@@ -0,0 +1,29 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_consumables/device_consumables_items/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_consumables_device_consumables_items_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_consumables_device_consumables_items_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('device_management_device_consumables_device_consumables_items_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_consumables_device_consumables_items_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('device_management_device_consumables_device_consumables_items_delete', data) })
}
export function createExportTask (data) {
return request({ url: BASE + 'data_export_task', method: 'post', data: params('device_management_device_consumables_device_consumables_items_data_export_task', data) })
}

View File

@@ -0,0 +1,29 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_consumables/device_consumables_lifetime_management/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_consumables_device_consumables_lifetime_management_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_consumables_device_consumables_lifetime_management_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('device_management_device_consumables_device_consumables_lifetime_management_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_consumables_device_consumables_lifetime_management_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('device_management_device_consumables_device_consumables_lifetime_management_delete', data) })
}
export function createExportTask (data) {
return request({ url: BASE + 'data_export_task', method: 'post', data: params('device_management_device_consumables_device_consumables_lifetime_management_data_export_task', data) })
}

View File

@@ -0,0 +1,51 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/device_model/device_category/'
function apiParams (method, data = {}) {
return {
method: `equipment_management_equipment_model_equipment_category_${method}`,
platform: 'background',
...data
}
}
export function getEquipmentCategoryList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('list', data)
})
}
export function getEquipmentCategoryALL (data) {
return request({
url: BASE + 'all',
method: 'get',
params: apiParams('all', data)
})
}
export function createEquipmentCategory (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('create', data)
})
}
export function editEquipmentCategory (data) {
return request({
url: BASE + 'edit',
method: 'put',
data: apiParams('edit', data)
})
}
export function deleteEquipmentCategory (data) {
return request({
url: BASE + 'delete',
method: 'delete',
data: apiParams('delete', data)
})
}

View File

@@ -0,0 +1,33 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/device_model/device_management/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('production_configuration_device_model_device_management_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('production_configuration_device_model_device_management_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('production_configuration_device_model_device_management_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('production_configuration_device_model_device_management_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('production_configuration_device_model_device_management_delete', data) })
}
export function batchDeleteItems (data) {
return request({ url: BASE + 'batch_delete', method: 'delete', data: params('production_configuration_device_model_device_management_batch_delete', data) })
}
export function createExportTask (data) {
return request({ url: BASE + 'device_data_export_task', method: 'post', data: params('production_configuration_device_model_device_management_device_export_task', data) })
}

View File

@@ -0,0 +1,29 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_check/device_check_items/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_check_device_check_items_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_check_device_check_items_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('device_management_device_check_device_check_items_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_check_device_check_items_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('device_management_device_check_device_check_items_delete', data) })
}
export function batchDeleteItems (data) {
return request({ url: BASE + 'batch_delete', method: 'delete', data: params('device_management_device_check_device_check_items_batch_delete', data) })
}

View File

@@ -0,0 +1,13 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_check/device_check_items_log/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_check_device_check_items_log_list', data) })
}
export function createExportTask (data) {
return request({ url: BASE + 'export', method: 'post', data: params('device_management_device_check_device_check_items_log_export', data) })
}

View File

@@ -0,0 +1,17 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_check/device_check_record/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_check_device_check_record_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create_record', method: 'post', data: params('device_management_device_check_device_check_create_record', data) })
}
export function editItem (data) {
return request({ url: BASE + 'create_record', method: 'put', data: params('device_management_device_check_device_check_create_record', data) })
}

View File

@@ -0,0 +1,25 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_maintain/device_maintain_items_details/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_maintain_device_maintain_items_details_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_maintain_device_maintain_items_details_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('device_management_device_maintain_device_maintain_items_details_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_maintain_device_maintain_items_details_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('device_management_device_maintain_device_maintain_items_details_delete', data) })
}

View File

@@ -0,0 +1,29 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_maintain/device_maintain_items/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_maintain_device_maintain_items_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_maintain_device_maintain_items_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('device_management_device_maintain_device_maintain_items_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_maintain_device_maintain_items_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('device_management_device_maintain_device_maintain_items_delete', data) })
}
export function batchDeleteItems (data) {
return request({ url: BASE + 'batch_delete', method: 'delete', data: params('device_management_device_maintain_device_maintain_items_batch_delete', data) })
}

View File

@@ -0,0 +1,21 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_maintain/device_maintain_items_log/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_maintain_device_maintain_items_log_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_maintain_device_maintain_items_log_list', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_maintain_device_maintain_items_log_edit', data) })
}
export function createExportTask (data) {
return request({ url: BASE + 'export', method: 'post', data: params('device_management_device_maintain_device_maintain_items_log_export', data) })
}

View File

@@ -0,0 +1,29 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_repair/device_repair_log/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_repair_device_repair_log_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_repair_device_repair_log_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('device_management_device_repair_device_repair_log_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_repair_device_repair_log_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('device_management_device_repair_device_repair_log_delete', data) })
}
export function createExportTask (data) {
return request({ url: BASE + 'create_data_export_task', method: 'post', data: params('device_management_device_repair_device_repair_log_create_data_export_task', data) })
}

View File

@@ -0,0 +1,29 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_repair/device_repair_management/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_repair_device_repair_management_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_repair_device_repair_management_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('device_management_device_repair_device_repair_management_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_repair_device_repair_management_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('device_management_device_repair_device_repair_management_delete', data) })
}
export function createExportTask (data) {
return request({ url: BASE + 'create_data_export_task', method: 'post', data: params('device_management_device_repair_device_repair_management_create_data_export_task', data) })
}

View File

@@ -0,0 +1,29 @@
import { request } from '@/api/_service'
const BASE = 'device_management/device_consumables/device_consumables_replace_log/'
function params (method, data = {}) { return { method, platform: 'background', ...data } }
export function getAll (data) {
return request({ url: BASE + 'all', method: 'get', params: params('device_management_device_consumables_device_consumables_replace_log_all', data) })
}
export function getList (data) {
return request({ url: BASE + 'list', method: 'get', params: params('device_management_device_consumables_device_consumables_replace_log_list', data) })
}
export function createItem (data) {
return request({ url: BASE + 'create', method: 'post', data: params('device_management_device_consumables_device_consumables_replace_log_create', data) })
}
export function editItem (data) {
return request({ url: BASE + 'edit', method: 'put', data: params('device_management_device_consumables_device_consumables_replace_log_edit', data) })
}
export function deleteItem (data) {
return request({ url: BASE + 'delete', method: 'delete', data: params('device_management_device_consumables_device_consumables_replace_log_delete', data) })
}
export function createExportTask (data) {
return request({ url: BASE + 'data_export_task', method: 'post', data: params('device_management_device_consumables_device_consumables_replace_log_data_export_task', data) })
}

View File

@@ -0,0 +1,59 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/produce/pin_check/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getPincheckWorkstation (data) {
return request({
url: BASE + 'workstation',
method: 'get',
params: apiParams('planning_production_produce_pincheck_workstation', data)
})
}
export function getDevicePinCheckList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('planning_production_produce_pincheck_list', data)
})
}
export function getPinCheckDetail (data) {
return request({
url: BASE + 'detail',
method: 'get',
params: apiParams('planning_production_produce_pincheck_detail', data)
})
}
export function setPinCheckClean (data) {
return request({
url: BASE + 'clean',
method: 'get',
params: apiParams('planning_production_produce_pincheck_clean', data)
})
}
export function setPinCheckCleanSingle (data) {
return request({
url: BASE + 'clean_single_channel',
method: 'get',
params: apiParams('planning_production_produce_pincheck_clean_single_channel', data)
})
}
export function getWorkstationSearch (data) {
return request({
url: BASE + 'search',
method: 'get',
params: apiParams('planning_production_produce_pincheck_search', data)
})
}

View File

@@ -0,0 +1,27 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/production_batch_management/report/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getBatchDefectReport (data) {
return request({
url: BASE + 'bad',
method: 'get',
params: apiParams('planning_production_production_batch_management_batch_bad', data)
})
}
export function exportBatchDefectReport (data) {
return request({
url: BASE + 'export',
method: 'get',
params: apiParams('planning_production_production_batch_management_batch_export', data)
})
}

View File

@@ -0,0 +1,59 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/production_batch_management/batch/'
function apiParams (method, data = {}) {
return {
method: `planning_production_production_batch_management_batch_${method}`,
platform: 'background',
...data
}
}
export function getBatchAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: apiParams('all', data)
})
}
export function getBatchList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('list', data)
})
}
export function createBatch (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('create', data)
})
}
export function editBatch (data) {
return request({
url: BASE + 'edit',
method: 'put',
data: apiParams('edit', data)
})
}
export function deleteBatch (data) {
return request({
url: BASE + 'delete',
method: 'delete',
data: apiParams('delete', data)
})
}
export function getProcessBatch (data) {
return request({
url: BASE + 'getProcessBatch',
method: 'post',
data: apiParams('getProcessBatch', data)
})
}

View File

@@ -0,0 +1,43 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/production_batch_management/batch_tray/'
function apiParams (method, data = {}) {
return {
method: `planning_production_production_batch_management_batch_tray_${method}`,
platform: 'background',
...data
}
}
export function getBatchTrayList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('list', data)
})
}
export function trayUnbinding (data) {
return request({
url: BASE + 'unbinding',
method: 'get',
params: apiParams('unbinding', data)
})
}
export function trayInactivity (data) {
return request({
url: BASE + 'inactivity',
method: 'get',
params: apiParams('inactivity', data)
})
}
export function getBatteryParam (data) {
return request({
url: BASE + 'get_battery_param',
method: 'get',
params: apiParams('get_battery_param', data)
})
}

View File

@@ -0,0 +1,19 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/device_model/device_management/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getDeviceAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: apiParams('production_configuration_device_model_device_management_all', data)
})
}

View File

@@ -0,0 +1,27 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/wip/wip_management/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getWipDataList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('planning_production_wip_wip_management_list', data)
})
}
export function createWipData (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('planning_production_wip_wip_management_create', data)
})
}

View File

@@ -0,0 +1,27 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/produce/change_battery_process/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function verifyBatteryProcessInfo (data) {
return request({
url: BASE + 'verify',
method: 'get',
params: apiParams('planning_production_produce_change_battery_process_verify', data)
})
}
export function changeBatteryProcess (data) {
return request({
url: BASE + 'change',
method: 'get',
params: apiParams('planning_production_produce_change_battery_process_change', data)
})
}

View File

@@ -0,0 +1,19 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/produce/set_battery_rebatch/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function verifyBatteryRebatchInfo (data) {
return request({
url: BASE + 'verify',
method: 'get',
params: apiParams('planning_production_produce_change_battery_process_verify', data)
})
}

View File

@@ -0,0 +1,67 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/produce/tray_manage/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getTrayManageInfo (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('planning_production_produce_traymanage_list', data)
})
}
export function changeFlowProcess (data) {
return request({
url: BASE + 'change',
method: 'put',
params: apiParams('planning_production_produce_traymanage_change', data)
})
}
export function inactiveTray (data) {
return request({
url: BASE + 'inactive',
method: 'put',
params: apiParams('planning_production_produce_traymanage_inactive', data)
})
}
export function trayNg (data) {
return request({
url: BASE + 'ng',
method: 'put',
params: apiParams('planning_production_produce_traymanage_ng', data)
})
}
export function rangeNgAndRC (data) {
return request({
url: BASE + 'ngrc',
method: 'put',
params: apiParams('planning_production_produce_traymanage_ngrc', data)
})
}
export function rangeFx (data) {
return request({
url: BASE + 'fenx',
method: 'put',
params: apiParams('planning_production_produce_traymanage_fenx', data)
})
}
export function cleanTrayNg (data) {
return request({
url: BASE + 'clean',
method: 'put',
params: apiParams('planning_production_produce_traymanage_clean', data)
})
}

View File

@@ -0,0 +1,51 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/produce/tray_login/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getBatchAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: apiParams('planning_production_produce_traylogin_all', data)
})
}
export function getBatchTrayFormatAll (data) {
return request({
url: BASE + 'trayformat_all',
method: 'get',
params: apiParams('planning_production_produce_trayformat_all', data)
})
}
export function createBatchTrayFormat (data) {
return request({
url: BASE + 'create',
method: 'get',
params: apiParams('planning_production_produce_trayformat_create', data)
})
}
export function deleteBatchTrayFormat (data) {
return request({
url: BASE + 'delete',
method: 'get',
params: apiParams('planning_production_produce_trayformat_delete', data)
})
}
export function checkBatteryid (data) {
return request({
url: BASE + 'check_batteryid',
method: 'get',
params: apiParams('planning_production_produce_trayformat_check_batteryid', data)
})
}

View File

@@ -0,0 +1,56 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/matetial_model/bom/'
const RELATION_BASE = 'production_configuration/matetial_model/bom_relationship/'
function apiParams (method, data = {}) {
return {
method: `production_configuration_matetial_model_bom_${method}`,
platform: 'background',
...data
}
}
function relationApiParams (method, data = {}) {
return {
method: `production_configuration_matetial_model_bom_relationship_${method}`,
platform: 'background',
...data
}
}
export function getBomAll (data) {
return request({ url: BASE + 'all', method: 'get', params: apiParams('all', data) })
}
export function getBomList (data) {
return request({ url: BASE + 'list', method: 'get', params: apiParams('list', data) })
}
export function createBom (data) {
return request({ url: BASE + 'create', method: 'post', data: apiParams('create', data) })
}
export function editBom (data) {
return request({ url: BASE + 'edit', method: 'put', data: apiParams('edit', data) })
}
export function deleteBom (data) {
return request({ url: BASE + 'delete', method: 'delete', data: apiParams('delete', data) })
}
export function getBomRelationshipList (data) {
return request({ url: RELATION_BASE + 'list', method: 'get', params: relationApiParams('list', data) })
}
export function createBomRelationship (data) {
return request({ url: RELATION_BASE + 'create', method: 'post', data: relationApiParams('create', data) })
}
export function editBomRelationship (data) {
return request({ url: RELATION_BASE + 'edit', method: 'put', data: relationApiParams('edit', data) })
}
export function deleteBomRelationship (data) {
return request({ url: RELATION_BASE + 'delete', method: 'delete', data: relationApiParams('delete', data) })
}

View File

@@ -0,0 +1,51 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/technology_model/calculation_script/'
function apiParams (method, data = {}) {
return {
method: `production_configuration_technology_model_calculation_script_${method}`,
platform: 'background',
...data
}
}
export function getCalculationScriptAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: apiParams('all', data)
})
}
export function getCalculationScriptList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('list', data)
})
}
export function createCalculationScript (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('create', data)
})
}
export function editCalculationScript (data) {
return request({
url: BASE + 'edit',
method: 'put',
data: apiParams('edit', data)
})
}
export function deleteCalculationScript (data) {
return request({
url: BASE + 'delete',
method: 'delete',
data: apiParams('delete', data)
})
}

View File

@@ -0,0 +1,43 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/spc_configuration/binding_scada_node/'
function apiParams (method, data = {}) {
return {
method: `production_master_data_spc_configuration_data_collection_configuration_${method}`,
platform: 'background',
...data
}
}
export function getDataCollectionConfigList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('list', data)
})
}
export function createDataCollectionConfig (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('create', data)
})
}
export function editDataCollectionConfig (data) {
return request({
url: BASE + 'edit',
method: 'put',
data: apiParams('edit', data)
})
}
export function deleteDataCollectionConfig (data) {
return request({
url: BASE + 'delete',
method: 'delete',
data: apiParams('delete', data)
})
}

View File

@@ -0,0 +1,19 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/device_model/device_category/'
function apiParams (method, data = {}) {
return {
method: `production_configuration_device_model_device_category_${method}`,
platform: 'background',
...data
}
}
export function getDeviceCategoryAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: apiParams('all', data)
})
}

View File

@@ -18,6 +18,18 @@ export function getProcessCategoryList (data) {
}) })
} }
export function getProcessCategoryAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: {
method: 'production_configuration_technology_model_technology_flow_category_all',
platform: 'background',
...data
}
})
}
export function createProcessCategory (data) { export function createProcessCategory (data) {
return request({ return request({
url: BASE + 'create', url: BASE + 'create',

View File

@@ -0,0 +1,132 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/technology_model/technology_flow_process/'
function apiParams (method, data = {}) {
return {
method: `production_configuration_technology_model_technology_flow_process_${method}`,
platform: 'background',
...data
}
}
export function getProcessCardAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: apiParams('all', data)
})
}
export function createProcessCard (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('create', data)
})
}
export function editProcessCard (data) {
return request({
url: BASE + 'edit',
method: 'put',
data: apiParams('edit', data)
})
}
export function deleteProcessCard (data) {
return request({
url: BASE + 'delete',
method: 'delete',
data: apiParams('delete', data)
})
}
export function setSetting (data) {
return request({
url: BASE + 'set_setting',
method: 'post',
data: apiParams('set_setting', data)
})
}
export function moveUp (data) {
return request({
url: BASE + 'move_up',
method: 'post',
data: apiParams('move_up', data)
})
}
export function moveDown (data) {
return request({
url: BASE + 'move_down',
method: 'post',
data: apiParams('move_down', data)
})
}
export function getOptionalParamsDetails (data) {
return request({
url: BASE + 'get_optional_params_details',
method: 'get',
params: apiParams('get_optional_params_details', data)
})
}
export function getOptionalAllParamsDetails (data) {
return request({
url: BASE + 'get_optional_all_params_details',
method: 'get',
params: apiParams('get_optional_all_params_details', data)
})
}
export function getStep (data) {
return request({
url: BASE + 'get_step',
method: 'get',
params: apiParams('get_step', data)
})
}
export function getTemperatureList (data) {
return request({
url: BASE + 'get_temperature_list',
method: 'get',
params: apiParams('get_temperature_list', data)
})
}
export function createTemperature (data) {
return request({
url: BASE + 'create_temperature',
method: 'post',
data: apiParams('create_temperature', data)
})
}
export function getTemperatureTemplate (data) {
return request({
url: BASE + 'get_temperature_template',
method: 'post',
responseType: 'blob',
data: apiParams('get_temperature_template', data)
})
}
export function getAllWorkingsubclassParams (data) {
return request({
url: BASE + 'get_all_workingsubclass_params',
method: 'get',
params: apiParams('get_all_workingsubclass_params', data)
})
}
export function addOptionalParams (data) {
return request({
url: BASE + 'add_optional_params',
method: 'post',
data: apiParams('add_optional_params', data)
})
}

View File

@@ -0,0 +1,51 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/technology_model/technology_flow/'
function apiParams (method, data = {}) {
return {
method: `production_master_data_process_model_process_routing_${method}`,
platform: 'background',
...data
}
}
export function getProcessRoutingList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('list', data)
})
}
export function createProcessRouting (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('create', data)
})
}
export function editProcessRouting (data) {
return request({
url: BASE + 'edit',
method: 'put',
data: apiParams('edit', data)
})
}
export function deleteProcessRouting (data) {
return request({
url: BASE + 'delete',
method: 'delete',
data: apiParams('delete', data)
})
}
export function copyProcessRouting (data) {
return request({
url: BASE + 'copy',
method: 'post',
data: apiParams('copy', data)
})
}

View File

@@ -18,6 +18,18 @@ export function getProcessStepList (data) {
}) })
} }
export function getWorkingsubclassAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: {
method: 'production_configuration_technology_model_technology_flow_workingsubclass_all',
platform: 'background',
...data
}
})
}
export function createProcessStep (data) { export function createProcessStep (data) {
return request({ return request({
url: BASE + 'create', url: BASE + 'create',

View File

@@ -2,7 +2,7 @@ import { request } from '@/api/_service'
const BASE = 'production_configuration/product_model/battery_model/' const BASE = 'production_configuration/product_model/battery_model/'
function apiParams(method, data = {}) { function apiParams (method, data = {}) {
return { return {
method: `production_master_data_product_management_product_list_${method}`, method: `production_master_data_product_management_product_list_${method}`,
platform: 'background', platform: 'background',
@@ -10,7 +10,7 @@ function apiParams(method, data = {}) {
} }
} }
export function getProductList(data) { export function getProductList (data) {
return request({ return request({
url: BASE + 'list', url: BASE + 'list',
method: 'get', method: 'get',
@@ -18,7 +18,19 @@ export function getProductList(data) {
}) })
} }
export function createProduct(data) { export function getProductBatteryAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: {
method: 'production_configuration_product_model_battery_model_all',
platform: 'background',
...data
}
})
}
export function createProduct (data) {
return request({ return request({
url: BASE + 'create', url: BASE + 'create',
method: 'post', method: 'post',
@@ -26,7 +38,7 @@ export function createProduct(data) {
}) })
} }
export function editProduct(data) { export function editProduct (data) {
return request({ return request({
url: BASE + 'edit', url: BASE + 'edit',
method: 'put', method: 'put',
@@ -34,7 +46,7 @@ export function editProduct(data) {
}) })
} }
export function deleteProduct(data) { export function deleteProduct (data) {
return request({ return request({
url: BASE + 'delete', url: BASE + 'delete',
method: 'delete', method: 'delete',

View File

@@ -0,0 +1,76 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/product_model/product_ng_info/'
function apiParams (method, data = {}) {
return {
method: `production_configuration_product_model_product_ng_info_${method}`,
platform: 'background',
...data
}
}
export function getProductNgInfoAll (data) {
return request({
url: BASE + 'all',
method: 'get',
params: { ...data }
})
}
export function getProductNgInfoList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: { ...data }
})
}
export function createProductNgInfo (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('create', data)
})
}
export function editProductNgInfo (data) {
return request({
url: BASE + 'edit',
method: 'put',
data: apiParams('edit', data)
})
}
export function deleteProductNgInfo (data) {
return request({
url: BASE + 'delete',
method: 'delete',
data: apiParams('delete', data)
})
}
export function getImportTemplate (data) {
return request({
url: BASE + 'get_import_template',
method: 'post',
responseType: 'blob',
data: apiParams('get_import_template', data)
})
}
export function productNgInfoImport (data) {
return request({
url: BASE + 'import',
method: 'post',
data: apiParams('import', data)
})
}
export function productNgInfoExportTask (data) {
return request({
url: BASE + 'product_ng_info_export_task',
method: 'post',
data: apiParams('product_ng_info_export_task', data)
})
}

View File

@@ -0,0 +1,17 @@
import { request } from '@/api/_service'
const BASE = 'system_settings/organization/production_shift_management/'
function params (method, data = {}) {
return { method: `system_settings_organization_production_shift_management_${method}`, platform: 'background', ...data }
}
export function getShiftAll (data) { return request({ url: BASE + 'all', method: 'get', params: params('all', data) }) }
export function getShiftList (data) { return request({ url: BASE + 'list', method: 'get', params: params('list', data) }) }
export function createShift (data) { return request({ url: BASE + 'create', method: 'post', data: params('create', data) }) }
export function editShift (data) { return request({ url: BASE + 'edit', method: 'put', data: params('edit', data) }) }
export function deleteShift (data) { return request({ url: BASE + 'delete', method: 'delete', data: params('delete', data) }) }
export function getShiftImportTemplate (data) { return request({ url: BASE + 'get_import_template', method: 'post', responseType: 'blob', data: params('get_import_template', data) }) }
export function importShiftData (data) { return request({ url: BASE + 'data_import', method: 'post', data: params('data_import', data) }) }
export function exportShiftTask (data) { return request({ url: BASE + 'data_export_task', method: 'post', data: params('data_export_task', data) }) }
export function getShiftCalendarByDateRange (data) { return request({ url: BASE + 'get_shift_by_date_range', method: 'get', params: params('get_shift_by_date_range', data) }) }

View File

@@ -0,0 +1,24 @@
import { request } from '@/api/_service'
const BASE = 'system_settings/organization/production_team_manage/'
const MEMBER_BASE = 'system_settings/organization/production_members_manage/'
function params (method, data = {}) {
return { method: `system_settings_organization_production_team_manage_${method}`, platform: 'background', ...data }
}
function memberParams (method, data = {}) {
return { method: `system_settings_organization_production_members_manage_${method}`, platform: 'background', ...data }
}
export function getTeamAll (data) { return request({ url: BASE + 'all', method: 'get', params: params('all', data) }) }
export function getTeamList (data) { return request({ url: BASE + 'list', method: 'get', params: params('list', data) }) }
export function createTeam (data) { return request({ url: BASE + 'create', method: 'post', data: params('create', data) }) }
export function editTeam (data) { return request({ url: BASE + 'edit', method: 'put', data: params('edit', data) }) }
export function deleteTeam (data) { return request({ url: BASE + 'delete', method: 'delete', data: params('delete', data) }) }
export function getTeamImportTemplate (data) { return request({ url: BASE + 'get_import_template', method: 'post', responseType: 'blob', data: params('get_import_template', data) }) }
export function importTeamData (data) { return request({ url: BASE + 'data_import', method: 'post', data: params('data_import', data) }) }
export function exportTeamTask (data) { return request({ url: BASE + 'data_export_task', method: 'post', data: params('data_export_task', data) }) }
export function getTeamMemberList (data) { return request({ url: MEMBER_BASE + 'list', method: 'get', params: memberParams('list', data) }) }
export function deleteTeamMember (data) { return request({ url: MEMBER_BASE + 'delete', method: 'delete', data: memberParams('delete', data) }) }

View File

@@ -0,0 +1,15 @@
import { request } from '@/api/_service'
const BASE = 'production_configuration/workerman/'
export function sendWorkerman (data) {
return request({
url: BASE + 'send',
method: 'get',
params: {
method: 'planning_production_produce_traymanage_send_workerman',
module: 'api',
...data
}
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/xqc/aql_config/'
function apiParams (data = {}, method = 'quality_control_xqc_aql_config_list', platform = 'background') {
return { method, platform, ...data }
}
export function fetchAqlStandardsList (data = {}) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams(data, 'quality_control_xqc_aql_config_list', 'background')
})
}
export function createAqlStandards (data = {}) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams(data, 'quality_control_xqc_aql_config_create', 'api')
})
}
export function editAqlStandards (data = {}) {
return request({
url: BASE + 'edit',
method: 'post',
data: apiParams(data, 'quality_control_xqc_aql_config_edit', 'api')
})
}
export function deleteAqlStandards (data = {}) {
return request({
url: BASE + 'delete',
method: 'post',
data: apiParams(data, 'quality_control_xqc_aql_config_delete', 'api')
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/first_inspection/first_inspection/'
function apiParams (data = {}, method = 'get.setting.list', platform = 'api') {
return { method, platform, ...data }
}
export function fetchFirstArticleInspectionConfigurationList (data = {}) {
return request({
url: BASE + 'list',
method: 'post',
data: apiParams(data, 'get.setting.list', 'api')
})
}
export function createFirstArticleInspectionConfiguration (data = {}) {
return request({
url: BASE + 'add',
method: 'post',
data: apiParams(data, 'add.setting', 'admin')
})
}
export function editFirstArticleInspectionConfiguration (data = {}) {
return request({
url: BASE + 'update',
method: 'post',
data: apiParams(data, 'set.setting', 'admin')
})
}
export function deleteFirstArticleInspectionConfiguration (data = {}) {
return request({
url: BASE + 'delete',
method: 'post',
data: apiParams(data, 'del.setting.list', 'admin')
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/first_inspection/first_inspection/'
function apiParams (data = {}, method = 'get.setting.item.list', platform = 'api') {
return { method, platform, ...data }
}
export function fetchFirstArticleInspectionRecordsList (data = {}) {
return request({
url: BASE + 'item/list',
method: 'post',
data: apiParams(data, 'get.setting.item.list', 'api')
})
}
export function createFirstArticleInspectionRecords (data = {}) {
return request({
url: BASE + 'item/add',
method: 'post',
data: apiParams(data, 'add.setting.item', 'admin')
})
}
export function editFirstArticleInspectionRecords (data = {}) {
return request({
url: BASE + 'item/update',
method: 'post',
data: apiParams(data, 'set.setting.item', 'admin')
})
}
export function deleteFirstArticleInspectionRecords (data = {}) {
return request({
url: BASE + 'item/delete',
method: 'post',
data: apiParams(data, 'del.setting.item', 'admin')
})
}

View File

@@ -0,0 +1,15 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/first_inspection/first_inspection/'
function apiParams (data = {}, method = 'get.setting.item.list', platform = 'api') {
return { method, platform, ...data }
}
export function fetchFirstArticleInspectionReportsList (data = {}) {
return request({
url: BASE + 'item/list',
method: 'post',
data: apiParams(data, 'get.setting.item.list', 'api')
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/xqc/inspection_item/'
function apiParams (data = {}, method = 'get.quality.inspection_item.list', platform = 'api') {
return { method, platform, ...data }
}
export function fetchInspectionItemsList (data = {}) {
return request({
url: BASE + 'list',
method: 'post',
data: apiParams(data, 'get.quality.inspection_item.list', 'api')
})
}
export function createInspectionItems (data = {}) {
return request({
url: BASE + 'add',
method: 'post',
data: apiParams(data, 'add.quality.inspection_item.add', 'api')
})
}
export function editInspectionItems (data = {}) {
return request({
url: BASE + 'set',
method: 'post',
data: apiParams(data, 'set.quality.inspection_item.set', 'api')
})
}
export function deleteInspectionItems (data = {}) {
return request({
url: BASE + 'del',
method: 'post',
data: apiParams(data, 'del.quality.inspection_item.del', 'api')
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/xqc/inspection_order_manage/'
function apiParams (data = {}, method = 'xqc.inspection_order_manage.list', platform = 'api') {
return { method, platform, ...data }
}
export function fetchInspectionOrdersList (data = {}) {
return request({
url: BASE + 'list',
method: 'post',
data: apiParams(data, 'xqc.inspection_order_manage.list', 'api')
})
}
export function createInspectionOrders (data = {}) {
return request({
url: BASE + 'save',
method: 'post',
data: apiParams(data, 'xqc.inspection_order_manage.save', 'api')
})
}
export function editInspectionOrders (data = {}) {
return request({
url: BASE + 'save',
method: 'post',
data: apiParams(data, 'xqc.inspection_order_manage.save', 'api')
})
}
export function deleteInspectionOrders (data = {}) {
return request({
url: BASE + 'revoke',
method: 'post',
data: apiParams(data, 'xqc.inspection_order_manage.revoke', 'api')
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/xqc/inspection_plan/'
function apiParams (data = {}, method = 'quality_control_xqc_inspection_plan_list', platform = 'background') {
return { method, platform, ...data }
}
export function fetchInspectionPlansList (data = {}) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams(data, 'quality_control_xqc_inspection_plan_list', 'background')
})
}
export function createInspectionPlans (data = {}) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams(data, 'quality_control_xqc_inspection_plan_create', 'background')
})
}
export function editInspectionPlans (data = {}) {
return request({
url: BASE + 'edit',
method: 'post',
data: apiParams(data, 'quality_control_xqc_inspection_plan_edit', 'background')
})
}
export function deleteInspectionPlans (data = {}) {
return request({
url: BASE + 'delete',
method: 'post',
data: apiParams(data, 'quality_control_xqc_inspection_plan_delete', 'background')
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/xqc/inspection_standard/'
function apiParams (data = {}, method = 'get.quality.inspection_standard.list', platform = 'api') {
return { method, platform, ...data }
}
export function fetchInspectionStandardsList (data = {}) {
return request({
url: BASE + 'list',
method: 'post',
data: apiParams(data, 'get.quality.inspection_standard.list', 'api')
})
}
export function createInspectionStandards (data = {}) {
return request({
url: BASE + 'add',
method: 'post',
data: apiParams(data, 'add.quality.inspection_standard.add', 'api')
})
}
export function editInspectionStandards (data = {}) {
return request({
url: BASE + 'set',
method: 'post',
data: apiParams(data, 'set.quality.inspection_standard.set', 'api')
})
}
export function deleteInspectionStandards (data = {}) {
return request({
url: BASE + 'del',
method: 'post',
data: apiParams(data, 'del.quality.inspection_standard.del', 'api')
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/first_inspection/first_inspection/'
function apiParams (data = {}, method = 'get.quality.category.list', platform = 'api') {
return { method, platform, ...data }
}
export function fetchInspectionTypeManagementList (data = {}) {
return request({
url: BASE + 'category/list',
method: 'post',
data: apiParams(data, 'get.quality.category.list', 'api')
})
}
export function createInspectionTypeManagement (data = {}) {
return request({
url: BASE + 'category/add',
method: 'post',
data: apiParams(data, 'add.quality.category', 'api')
})
}
export function editInspectionTypeManagement (data = {}) {
return request({
url: BASE + 'category/update',
method: 'post',
data: apiParams(data, 'set.quality.category', 'api')
})
}
export function deleteInspectionTypeManagement (data = {}) {
return request({
url: BASE + 'category/delete',
method: 'post',
data: apiParams(data, 'del.quality.category', 'api')
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'quality_control/xqc/sampling_plan/'
function apiParams (data = {}, method = 'xqc.sampling_plan_config.list', platform = 'api') {
return { method, platform, ...data }
}
export function fetchSamplingPlansList (data = {}) {
return request({
url: BASE + 'list',
method: 'post',
data: apiParams(data, 'xqc.sampling_plan_config.list', 'api')
})
}
export function createSamplingPlans (data = {}) {
return request({
url: BASE + 'save',
method: 'post',
data: apiParams(data, 'xqc.sampling_plan_config.save', 'api')
})
}
export function editSamplingPlans (data = {}) {
return request({
url: BASE + 'save',
method: 'post',
data: apiParams(data, 'xqc.sampling_plan_config.save', 'api')
})
}
export function deleteSamplingPlans (data = {}) {
return request({
url: BASE + 'delete',
method: 'post',
data: apiParams(data, 'xqc.sampling_plan_config.delete', 'api')
})
}

View File

@@ -0,0 +1,39 @@
import { request } from '@/api/_service'
const BASE = 'spc/manager/'
function apiParams (data = {}, method = 'get.spc.create', platform = 'admin') {
return { method, platform, ...data }
}
export function fetchSpcConfigurationList (data = {}) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams(data, 'get.spc.create', 'admin')
})
}
export function createSpcConfiguration (data = {}) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams(data, 'set.spc.create', 'admin')
})
}
export function editSpcConfiguration (data = {}) {
return request({
url: BASE + 'edit',
method: 'put',
data: apiParams(data, 'set.spc.edit', 'admin')
})
}
export function deleteSpcConfiguration (data = {}) {
return request({
url: BASE + 'delete',
method: 'post',
data: apiParams(data, 'set.spc.delete', 'admin')
})
}

View File

@@ -0,0 +1,24 @@
import { request } from '@/api/_service'
const BASE = 'spc/manager/'
function apiParams (data = {}, method = 'get.spc.all') {
return { method, platform: 'admin', ...data }
}
export function getSpcConfigAll (data = {}) {
return request({
url: BASE + 'all',
method: 'get',
params: apiParams(data, 'get.spc.all')
})
}
export function getNodeSubgroupSamplesData (data = {}) {
return request({
baseURL: process.env.VUE_APP_HSLSERVER_API,
url: '',
method: 'post',
data
})
}

View File

@@ -0,0 +1,10 @@
import { request } from '@/api/_service'
const BASE = 'scada_manage/device_gather_management/management/'
export function getDeviceGatherManagementAll (data) {
return request({ url: BASE + 'all', method: 'post', data: { method: 'scada_manage_device_gather_management_management_all', platform: 'background', ...data } })
}
export function deviceDataExportTask (data) {
return request({ url: BASE + 'device_data_export_task', method: 'post', data: { method: 'scada_manage_device_gather_management_management_device_data_export_task', platform: 'background', ...data } })
}

View File

@@ -0,0 +1,108 @@
import axios from 'axios'
import { request } from '@/api/_service'
const EDGE_BASE = process.env.VUE_APP_HSLSERVER_API
const EDGE_PASSWORD = process.env.VUE_APP_HSLSERVER_PASSWORD
function edgeUrl (query = '') {
return query ? `${EDGE_BASE}?${query}` : EDGE_BASE
}
export function edgeGet (url) {
return axios({ method: 'get', url }).then(res => res.data)
}
export function edgePost (url, data) {
return axios({ method: 'post', url, data }).then(res => res.data)
}
export function addNode (data) { return edgePost(EDGE_BASE, data) }
export function updateNode (data) { return edgePost(EDGE_BASE, data) }
export function removeNode (data) { return edgePost(EDGE_BASE, data) }
export function getQueryNode (data = {}) {
const params = new URLSearchParams()
params.set('query', 'nodes')
params.set('currentPage', data.currentPage || data.page_no || 1)
params.set('pageSize', data.pageSize || data.page_size || 10)
params.set('working_subclass', data.working_subclass || '')
params.set('category', data.categoryName || data.category || '')
return edgeGet(edgeUrl(params.toString()))
}
export function getQueryWorkingSubclasses (data = {}) {
const params = new URLSearchParams()
params.set('query', 'working_subclasses')
if (data.device_code) params.set('device_code', data.device_code)
return edgeGet(edgeUrl(params.toString()))
}
export function getQueryCodes (workingSubclass, deviceCode) {
const params = new URLSearchParams()
params.set('query', 'codes')
params.set('working_subclass', workingSubclass || '')
if (deviceCode) params.set('device_code', deviceCode)
return edgeGet(edgeUrl(params.toString()))
}
export function getNodeValue () { return edgeGet(edgeUrl('query=nodes_value')) }
export function getNodeAll () { return edgeGet(edgeUrl('query=nodes_all')) }
export function getNodeMappingDeviceList (data = {}) {
const params = new URLSearchParams()
params.set('query', 'node_mapping_device_list')
if (data.page_no !== undefined) params.set('currentPage', data.page_no)
if (data.page_size !== undefined) params.set('pageSize', data.page_size)
if (data.scada_data_capture_node_code) params.set('scada_data_capture_node_code', data.scada_data_capture_node_code)
if (data.device_code) params.set('device_code', data.device_code)
return edgeGet(edgeUrl(params.toString()))
}
export function addNodeMappingDevice (data) { return edgePost(EDGE_BASE, data) }
export function editNodeMappingDevice (data) { return edgePost(EDGE_BASE, data) }
export function delNodeMappingDevice (data) { return edgePost(EDGE_BASE, data) }
export function getExecLog (data = {}) {
const params = new URLSearchParams()
params.set('query', 'get_exec_log')
params.set('page_no', data.page_no || 1)
params.set('page_size', data.page_size || 10)
if (data.create_date) params.set('create_date', data.create_date)
if (data.command) params.set('command', data.command)
if (data.server_name) params.set('server_name', data.server_name)
if (data.device_name) params.set('device_name', data.device_name)
return edgeGet(edgeUrl(params.toString()))
}
export function getQueryNodeData (data = {}) {
const params = new URLSearchParams()
params.set('query', 'node_data')
params.set('startRow', data.startRow || 0)
params.set('endRow', data.endRow || 50)
params.set('working_subclass', data.workingSubclass || data.working_subclass || '')
params.set('wipCode', data.wipCode || '')
params.set('start_time', data.startTime || '')
params.set('end_time', data.endTime || '')
params.set('dedup', data.dedup === undefined ? 1 : data.dedup)
if (data.code) params.set('code', Array.isArray(data.code) ? data.code.join(',') : data.code)
if (data.device_code) params.set('device_code', data.device_code)
return edgeGet(edgeUrl(params.toString()))
}
export function queryServers () { return request({ url: `${EDGE_BASE}?query=servers` }) }
export function addServer (data) { return request({ url: EDGE_BASE, method: 'post', data }) }
export function updateServer (data) { return request({ url: EDGE_BASE, method: 'post', data }) }
export function removeServer (data) { return request({ url: EDGE_BASE, method: 'post', data }) }
export function setServerExec (data) { return request({ url: EDGE_BASE, method: 'post', data }) }
export function verifyServer (url) {
return request({ auth: { username: 'admin', password: EDGE_PASSWORD }, method: 'post', url: `${url}/Admin/ServerSettingsRequest` })
}
export function modifyServer (url, data) {
return request({ auth: { username: 'admin', password: EDGE_PASSWORD }, method: 'post', url: `${url}/Admin/ServerSettingsModify`, data })
}
export function getServeDeviceMonitoring (url) {
return request({ auth: { username: 'admin', password: EDGE_PASSWORD }, url: `${url}/Edge/DeviceData?data=/` })
}

View File

@@ -0,0 +1,19 @@
import { request } from '@/api/_service'
const BASE = 'scada_manage/workshop/config/'
export function getWorkshopConfigAll (data) {
return request({ url: BASE + 'all', method: 'get', params: { method: 'scada_manage_workshop_config_all', platform: 'background', ...data } })
}
export function getWorkshopConfigList (data) {
return request({ url: BASE + 'list', method: 'get', params: { method: 'scada_manage_workshop_config_list', platform: 'background', ...data } })
}
export function createWorkshopConfig (data) {
return request({ url: BASE + 'create', method: 'post', data: { method: 'scada_manage_workshop_config_create', platform: 'background', ...data } })
}
export function editWorkshopConfig (data) {
return request({ url: BASE + 'edit', method: 'put', data: { method: 'scada_manage_workshop_config_edit', platform: 'background', ...data } })
}
export function deleteWorkshopConfig (data) {
return request({ url: BASE + 'delete', method: 'delete', data: { method: 'scada_manage_workshop_config_delete', platform: 'background', ...data } })
}

View File

@@ -0,0 +1,25 @@
import { request } from '@/api/_service'
const BASE = 'scada_manage/workshop/point/'
export function getWorkshopPointAll (data) {
return request({ url: BASE + 'all', method: 'get', params: { method: 'scada_manage_workshop_point_all', platform: 'background', ...data } })
}
export function getWorkshopPointList (data) {
return request({ url: BASE + 'list', method: 'get', params: { method: 'scada_manage_workshop_point_list', platform: 'background', ...data } })
}
export function createWorkshopPoint (data) {
return request({ url: BASE + 'create', method: 'post', data: { method: 'scada_manage_workshop_point_create', platform: 'background', ...data } })
}
export function editWorkshopPoint (data) {
return request({ url: BASE + 'edit', method: 'put', data: { method: 'scada_manage_workshop_point_edit', platform: 'background', ...data } })
}
export function deleteWorkshopPoint (data) {
return request({ url: BASE + 'delete', method: 'delete', data: { method: 'scada_manage_workshop_point_delete', platform: 'background', ...data } })
}
export function importExcel (data) {
return request({ url: BASE + 'import', method: 'post', data: { method: 'scada_manage_workshop_point_delete', platform: 'background', ...data } })
}
export function getWorkshopNodeAll (data) {
return request({ url: BASE + 'node_all', method: 'get', data: { method: 'scada_manage_workshop_point_node_all', platform: 'background', ...data } })
}

View File

@@ -0,0 +1,43 @@
import { request } from '@/api/_service'
const BASE = 'system_settings/system_monitor/setting/'
function apiParams (method, data = {}) {
return {
method: `system_settings_system_monitoring_setting_${method}`,
platform: 'background',
...data
}
}
export function getMonitoringConfigurationList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('list', data)
})
}
export function createMonitoringConfiguration (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('create', data)
})
}
export function editMonitoringConfiguration (data) {
return request({
url: BASE + 'edit',
method: 'post',
data: apiParams('edit', data)
})
}
export function deleteMonitoringConfiguration (data) {
return request({
url: BASE + 'delete',
method: 'post',
data: apiParams('delete', data)
})
}

View File

@@ -68,7 +68,10 @@
:type="col.inputType || 'text'" :type="col.inputType || 'text'"
:autosize="col.autosize" :autosize="col.autosize"
:clearable="col.clearable !== false" :clearable="col.clearable !== false"
:disabled="!!col.disabled"
:style="col.style" :style="col.style"
@focus="handleFieldEvent(col, 'focus', $event)"
@blur="handleFieldEvent(col, 'blur', $event)"
/> />
<!-- ===== 下拉选择类型 ===== --> <!-- ===== 下拉选择类型 ===== -->
<!-- <!--
@@ -83,6 +86,9 @@
:clearable="col.clearable !== false" :clearable="col.clearable !== false"
:style="col.style" :style="col.style"
:filterable="col.filterable !== false" :filterable="col.filterable !== false"
:disabled="!!col.disabled"
@change="handleFieldEvent(col, 'change', $event)"
@focus="handleFieldEvent(col, 'focus', $event)"
> >
<el-option <el-option
v-for="opt in col.options" v-for="opt in col.options"
@@ -255,6 +261,14 @@ export default {
} }
}, },
methods: { methods: {
handleFieldEvent (col, eventName, value) {
if (!col) return
const handlerKey = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1)
const handler = col[handlerKey]
if (typeof handler === 'function') {
handler(value, col, this.formData)
}
},
/** /**
* 点击确定按钮 * 点击确定按钮
* 1. 调用 el-form 的 validate 方法验证所有字段 * 1. 调用 el-form 的 validate 方法验证所有字段

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,54 @@ export default {
name: `${pre}index`, name: `${pre}index`,
meta: { ...meta, title: '数据中台', root: '/data_middleground' }, meta: { ...meta, title: '数据中台', root: '/data_middleground' },
component: _import('system/function/module-index') component: _import('system/function/module-index')
},
{
path: 'basic_traceability/reverse_direction_traceability',
name: `${pre}basic_traceability-reverse_direction_traceability`,
meta: { ...meta, cache: true, title: '反向追溯' },
component: _import('data-platform/traceability/backward')
},
{
path: 'basic_traceability/positive_direction_traceability',
name: `${pre}basic_traceability-positive_direction_traceability`,
meta: { ...meta, cache: true, title: '正向追溯' },
component: _import('data-platform/traceability/forward')
},
{
path: 'produce/traceability/curve',
name: `${pre}traceability-curve`,
meta: { ...meta, cache: true, title: '电池曲线' },
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-')
} }

View File

@@ -13,6 +13,90 @@ export default {
name: `${pre}index`, name: `${pre}index`,
meta: { ...meta, title: '设备管理', root: '/device_management' }, meta: { ...meta, title: '设备管理', root: '/device_management' },
component: _import('system/function/module-index') component: _import('system/function/module-index')
},
{
path: 'device_model/device_category',
name: `${pre}equipment_model-equipment_category`,
meta: { ...meta, cache: true, title: '设备类别' },
component: _import('equipment-management/equipment-model/equipment-category')
},
{
path: 'device_model/device_management',
name: `${pre}equipment_model-equipment_registry`,
meta: { ...meta, cache: true, title: '设备信息' },
component: _import('equipment-management/equipment-model/equipment-registry')
},
{
path: 'device_check/device_check_items',
name: `${pre}device_check-device_check_items`,
meta: { ...meta, cache: true, title: '设备点检项目' },
component: _import('equipment-management/inspection-management/inspection-items')
},
{
path: 'device_check/device_check_record',
name: `${pre}device_check-device_check_record`,
meta: { ...meta, cache: true, title: '设备点检记录' },
component: _import('equipment-management/inspection-management/inspection-records')
},
{
path: 'device_check/device_check_items_log',
name: `${pre}device_check-device_check_items_log`,
meta: { ...meta, cache: true, title: '设备点检日志' },
component: _import('equipment-management/inspection-management/inspection-logs')
},
{
path: 'device_maintain/device_maintain_items',
name: `${pre}device_maintain-device_maintain_items`,
meta: { ...meta, cache: true, title: '设备保养项目' },
component: _import('equipment-management/maintenance-management/maintenance-items')
},
{
path: 'device_maintain/device_maintain_items_details',
name: `${pre}device_maintain-device_maintain_items_details`,
meta: { ...meta, cache: true, title: '设备保养详情' },
component: _import('equipment-management/maintenance-management/maintenance-details')
},
{
path: 'device_maintain/device_maintain_items_log',
name: `${pre}device_maintain-device_maintain_items_log`,
meta: { ...meta, cache: true, title: '设备保养日志' },
component: _import('equipment-management/maintenance-management/maintenance-logs')
},
{
path: 'device_repair/device_repair_management',
name: `${pre}device_repair-device_repair_management`,
meta: { ...meta, cache: true, title: '设备维修管理' },
component: _import('equipment-management/repair-management/repair-management')
},
{
path: 'device_repair/device_repair_log',
name: `${pre}device_repair-device_repair_log`,
meta: { ...meta, cache: true, title: '设备维修日志' },
component: _import('equipment-management/repair-management/repair-logs')
},
{
path: 'device_consumables/device_consumables_category',
name: `${pre}device_consumables-device_consumables_category`,
meta: { ...meta, cache: true, title: '设备损耗品类别' },
component: _import('equipment-management/consumables-management/consumables-category')
},
{
path: 'device_consumables/device_consumables_items',
name: `${pre}device_consumables-device_consumables_items`,
meta: { ...meta, cache: true, title: '设备损耗品项目' },
component: _import('equipment-management/consumables-management/consumables-items')
},
{
path: 'device_consumables/device_consumables_lifetime_management',
name: `${pre}device_consumables-device_consumables_lifetime_management`,
meta: { ...meta, cache: true, title: '设备损耗品寿命管理' },
component: _import('equipment-management/consumables-management/consumables-lifecycle')
},
{
path: 'device_consumables/device_consumables_replace_log',
name: `${pre}device_consumables-device_consumables_replace_log`,
meta: { ...meta, cache: true, title: '设备损耗品更换日志' },
component: _import('equipment-management/consumables-management/replacement-logs')
} }
])('device_management-') ])('device_management-')
} }

View File

@@ -13,6 +13,66 @@ export default {
name: `${pre}index`, name: `${pre}index`,
meta: { ...meta, title: '计划与生产', root: '/planning_production' }, meta: { ...meta, title: '计划与生产', root: '/planning_production' },
component: _import('system/function/module-index') component: _import('system/function/module-index')
},
{
path: 'production_batch_management/batch',
name: `${pre}production_batch_management-batch`,
meta: { ...meta, cache: true, title: '批次列表' },
component: _import('planning-production/batch-management/batch-list')
},
{
path: 'production_batch_management/batch_tray',
name: `${pre}production_batch_management-batch_tray`,
meta: { ...meta, cache: true, title: '批次托盘' },
component: _import('planning-production/batch-management/tray-tracking')
},
{
path: 'production_batch_management/bad',
name: `${pre}production_batch_management-bad`,
meta: { ...meta, cache: true, title: '生产批次不良报表' },
component: _import('planning-production/batch-management/batch-defect-report')
},
{
path: 'wip/wip_management/mock',
name: `${pre}wip-wip_management-mock`,
meta: { ...meta, cache: true, title: '物料监控' },
component: _import('planning-production/production-monitoring/material-monitoring')
},
{
path: 'produce/report/pin_check',
name: `${pre}report-pin_check`,
meta: { ...meta, cache: true, title: '预警中心' },
component: _import('planning-production/alert-center')
},
{
path: 'produce/monitor/set_battery_rebatch',
name: `${pre}monitor-set_battery_rebatch`,
meta: { ...meta, cache: true, title: '电池复投管理' },
component: _import('planning-production/production-monitoring/rework-management')
},
{
path: 'produce/monitor/tray_manage',
name: `${pre}monitor-tray_manage`,
meta: { ...meta, cache: true, title: '托盘管理' },
component: _import('planning-production/production-monitoring/tray-management')
},
{
path: 'produce/monitor/tray_login',
name: `${pre}monitor-tray_login`,
meta: { ...meta, cache: true, title: '托盘登录' },
component: _import('planning-production/production-monitoring/tray-registration')
},
{
path: 'produce/monitor/device',
name: `${pre}monitor-device`,
meta: { ...meta, cache: true, title: '设备监控' },
component: _import('planning-production/production-monitoring/equipment-monitoring')
},
{
path: 'produce/monitor/change_battery_process',
name: `${pre}monitor-change_battery_process`,
meta: { ...meta, cache: true, title: '电池工序管理' },
component: _import('planning-production/production-monitoring/process-execution')
} }
])('planning_production-') ])('planning_production-')
} }

View File

@@ -38,6 +38,18 @@ export default {
meta: { ...meta, cache: true, title: '工序单元' }, meta: { ...meta, cache: true, title: '工序单元' },
component: _import('production-master-data/process-model/process-step') component: _import('production-master-data/process-model/process-step')
}, },
{
path: 'technology_model/technology_flow',
name: `${pre}technology_model-technology_flow`,
meta: { ...meta, cache: true, title: '工艺流程' },
component: _import('production-master-data/process-model/process-routing')
},
{
path: 'technology_model/technology_flow/:flow_id/process',
name: `${pre}technology_model-technology_flow-process`,
meta: { ...meta, cache: false, title: '工艺详情' },
component: _import('production-master-data/process-model/process-routing-card')
},
{ {
path: 'product_model/battery_model', path: 'product_model/battery_model',
name: `${pre}product_management-product_list`, name: `${pre}product_management-product_list`,
@@ -56,11 +68,35 @@ export default {
meta: { ...meta, cache: true, title: '物料信息管理' }, meta: { ...meta, cache: true, title: '物料信息管理' },
component: _import('production-master-data/material-model/material-master') component: _import('production-master-data/material-model/material-master')
}, },
{
path: 'matetial_model/bom',
name: `${pre}material_model-bill_of_materials`,
meta: { ...meta, cache: true, title: 'BOM物料清单' },
component: _import('production-master-data/material-model/bill-of-materials')
},
{ {
path: 'matetial_model/unit', path: 'matetial_model/unit',
name: `${pre}material_model-material_unit`, name: `${pre}material_model-material_unit`,
meta: { ...meta, cache: true, title: '计量单位' }, meta: { ...meta, cache: true, title: '计量单位' },
component: _import('production-master-data/material-model/material-unit') component: _import('production-master-data/material-model/material-unit')
},
{
path: 'product_model/product_ng_info',
name: `${pre}product_model-product_ng_info`,
meta: { ...meta, cache: true, title: '异常不良管理' },
component: _import('production-master-data/product-model/product-ng-info')
},
{
path: 'spc_configuration/binding_scada_node',
name: `${pre}spc_configuration-data_collection_configuration`,
meta: { ...meta, cache: true, title: 'SPC采集配置' },
component: _import('production-master-data/spc-configuration/data-collection-configuration')
},
{
path: 'team_model/team_management',
name: `${pre}team_model-team_management`,
meta: { ...meta, cache: true, title: '班组管理' },
component: _import('production-master-data/team-model/team-management')
} }
])('production_configuration-') ])('production_configuration-')
} }

View File

@@ -13,6 +13,66 @@ export default {
name: `${pre}index`, name: `${pre}index`,
meta: { ...meta, title: '质量管理', root: '/quality_control' }, meta: { ...meta, title: '质量管理', root: '/quality_control' },
component: _import('system/function/module-index') component: _import('system/function/module-index')
},
{
path: 'first_inspection/category',
name: `${pre}process-control-inspection-type-management`,
meta: { ...meta, cache: true, title: '检验类别管理' },
component: _import('quality-management/process-control/inspection-type-management')
},
{
path: 'first_inspection/setting',
name: `${pre}process-control-first-article-inspection-configuration`,
meta: { ...meta, cache: true, title: '首巡检项目配置' },
component: _import('quality-management/process-control/first-article-inspection-configuration')
},
{
path: 'first_inspection/input',
name: `${pre}process-control-first-article-inspection-records`,
meta: { ...meta, cache: true, title: '首巡检录入' },
component: _import('quality-management/process-control/first-article-inspection-records')
},
{
path: 'first_inspection/report',
name: `${pre}process-control-first-article-inspection-reports`,
meta: { ...meta, cache: true, title: '首巡检报表' },
component: _import('quality-management/process-control/first-article-inspection-reports')
},
{
path: 'xqc/inspection_order_manage',
name: `${pre}inspection-management-inspection-orders`,
meta: { ...meta, cache: true, title: '检验单管理' },
component: _import('quality-management/inspection-management/inspection-orders')
},
{
path: 'xqc/inspection_standard',
name: `${pre}inspection-management-inspection-standards`,
meta: { ...meta, cache: true, title: '检验标准' },
component: _import('quality-management/inspection-management/inspection-standards')
},
{
path: 'xqc/aql_config',
name: `${pre}inspection-management-aql-standards`,
meta: { ...meta, cache: true, title: '接收质量限' },
component: _import('quality-management/inspection-management/aql-standards')
},
{
path: 'xqc/inspection_plan',
name: `${pre}inspection-management-inspection-plans`,
meta: { ...meta, cache: true, title: '检测方案维护' },
component: _import('quality-management/inspection-management/inspection-plans')
},
{
path: 'xqc/inspection_item',
name: `${pre}inspection-management-inspection-items`,
meta: { ...meta, cache: true, title: '检验项目' },
component: _import('quality-management/inspection-management/inspection-items')
},
{
path: 'xqc/sampling_plan',
name: `${pre}inspection-management-sampling-plans`,
meta: { ...meta, cache: true, title: '抽样方案配置' },
component: _import('quality-management/inspection-management/sampling-plans')
} }
])('quality_control-') ])('quality_control-')
} }

View File

@@ -0,0 +1,23 @@
import layoutHeaderAside from '@/layout/header-aside'
const meta = { auth: true }
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
export default {
path: '/scada_manage',
component: layoutHeaderAside,
children: (pre => [
{ path: 'index', name: `${pre}index`, meta: { ...meta, cache: true, title: '采集管理', root: '/scada_manage' }, component: _import('system/function/module-index') },
{ path: 'basic_configuration/scadaconfigure', name: `${pre}basic_configuration-scadaconfigure`, meta: { ...meta, cache: true, title: 'SCADA节点配置' }, component: _import('scada_manage/basic_configuration/scada_configure') },
{ path: 'basic_configuration/scadaquery', name: `${pre}basic_configuration-scadaquery`, meta: { ...meta, cache: true, title: 'SCADA数据查询' }, component: _import('scada_manage/basic_configuration/scada_query') },
{ path: 'basic_configuration/node_mapping_device_code', name: `${pre}basic_configuration-node_mapping_device_code`, meta: { ...meta, cache: true, title: '节点设备映射' }, component: _import('scada_manage/basic_configuration/node_mapping_device_code') },
{ path: 'basic_configuration/ems_configure', name: `${pre}basic_configuration-ems_configure`, meta: { ...meta, cache: true, title: '环控节点配置' }, component: _import('scada_manage/basic_configuration/ems_configure') },
{ path: 'basic_configuration/ems_query', name: `${pre}basic_configuration-ems_query`, meta: { ...meta, cache: true, title: '环控数据查询' }, component: _import('scada_manage/basic_configuration/ems_query') },
{ path: 'workshop_manage/workshop_config', name: `${pre}workshop_manage-workshop_config`, meta: { ...meta, cache: true, title: '车间配置' }, component: _import('scada_manage/workshop_manage/workshop_config') },
{ path: 'workshop_manage/workshop_point', name: `${pre}workshop_manage-workshop_point`, meta: { ...meta, cache: true, title: '车间点位管理' }, component: _import('scada_manage/workshop_manage/workshop_point') },
{ path: 'lecpserver/edgeserverconfigure', name: `${pre}lecpserver-edgeserverconfigure`, meta: { ...meta, cache: true, title: '服务配置' }, component: _import('scada_manage/lecpserver/edgeserverconfigure') },
{ path: 'lecpserver/edgeervermonitor', name: `${pre}lecpserver-edgeervermonitor`, meta: { ...meta, cache: true, title: '服务监控' }, component: _import('scada_manage/lecpserver/edgeservermonitor') },
{ path: 'lecpserver/edgeserver_log', name: `${pre}lecpserver-edgeserver_log`, meta: { ...meta, cache: true, title: '前置服务日志' }, component: _import('scada_manage/lecpserver/edgeserver_log') },
{ path: 'device_gather/battery_device_gather_monitor', name: `${pre}device_gather-battery_device_gather_monitor`, meta: { ...meta, cache: true, title: '实时采集监控' }, component: _import('scada_manage/device_gather/battery_device_gather_monitor') }
])('scada_manage-')
}

View File

@@ -0,0 +1,114 @@
import layoutHeaderAside from '@/layout/header-aside'
const meta = { auth: true }
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
export default {
path: '/spc',
component: layoutHeaderAside,
children: (pre => [
{
path: 'index',
name: `${pre}index`,
meta: { ...meta, title: 'SPC统计过程控制', root: '/spc' },
component: _import('system/function/module-index')
},
{
path: 'management',
name: `${pre}spc-control-spc-configuration`,
meta: { ...meta, cache: true, title: 'SPC渲染条件配置' },
component: _import('quality-management/spc-control/spc-configuration')
},
{
path: 'chart/XBar-R',
name: `${pre}spc-variable-charts-xbar-r`,
meta: { ...meta, cache: true, title: 'XBar-R' },
component: _import('quality-management/spc-variable-charts/xbar-r')
},
{
path: 'chart/XBar-S',
name: `${pre}spc-variable-charts-xbar-s`,
meta: { ...meta, cache: true, title: 'XBar-S' },
component: _import('quality-management/spc-variable-charts/xbar-s')
},
{
path: 'chart/I-MR',
name: `${pre}spc-variable-charts-i-mr`,
meta: { ...meta, cache: true, title: 'I-MR' },
component: _import('quality-management/spc-variable-charts/i-mr')
},
{
path: 'chart/Levey-Jennings',
name: `${pre}spc-variable-charts-levey-jennings`,
meta: { ...meta, cache: true, title: 'Levey-Jennings' },
component: _import('quality-management/spc-variable-charts/levey-jennings')
},
{
path: 'chart/EWMA',
name: `${pre}spc-variable-charts-ewma`,
meta: { ...meta, cache: true, title: 'EWMA' },
component: _import('quality-management/spc-variable-charts/ewma')
},
{
path: 'chart/CUSUM',
name: `${pre}spc-variable-charts-cusum`,
meta: { ...meta, cache: true, title: 'CUSUM' },
component: _import('quality-management/spc-variable-charts/cusum')
},
{
path: 'chart/MA',
name: `${pre}spc-variable-charts-ma`,
meta: { ...meta, cache: true, title: 'MA' },
component: _import('quality-management/spc-variable-charts/ma')
},
{
path: 'chart/MAMR',
name: `${pre}spc-variable-charts-mamr`,
meta: { ...meta, cache: true, title: 'MAMR' },
component: _import('quality-management/spc-variable-charts/mamr')
},
{
path: 'chart/MAMS',
name: `${pre}spc-variable-charts-mams`,
meta: { ...meta, cache: true, title: 'MAMS' },
component: _import('quality-management/spc-variable-charts/mams')
},
{
path: 'chart/Cpk',
name: `${pre}spc-variable-charts-cpk`,
meta: { ...meta, cache: true, title: 'CPK' },
component: _import('quality-management/spc-variable-charts/cpk')
},
{
path: 'chart/DPMO',
name: `${pre}spc-attribute-charts-dpmo`,
meta: { ...meta, cache: true, title: 'DPMO' },
component: _import('quality-management/spc-attribute-charts/dpmo')
},
{
path: 'chart/PChart',
name: `${pre}spc-attribute-charts-pchart`,
meta: { ...meta, cache: true, title: 'PChart' },
component: _import('quality-management/spc-attribute-charts/pchart')
},
{
path: 'chart/NPChart',
name: `${pre}spc-attribute-charts-npchart`,
meta: { ...meta, cache: true, title: 'NPChart' },
component: _import('quality-management/spc-attribute-charts/npchart')
},
{
path: 'chart/CChart',
name: `${pre}spc-attribute-charts-cchart`,
meta: { ...meta, cache: true, title: 'CChart' },
component: _import('quality-management/spc-attribute-charts/cchart')
},
{
path: 'chart/UChart',
name: `${pre}spc-attribute-charts-uchart`,
meta: { ...meta, cache: true, title: 'UChart' },
component: _import('quality-management/spc-attribute-charts/uchart')
}
])('spc-')
}

View File

@@ -49,6 +49,30 @@ export default {
name: `${pre}system_assistant-problem_help`, name: `${pre}system_assistant-problem_help`,
meta: { ...meta, cache: true, title: '问题帮助' }, meta: { ...meta, cache: true, title: '问题帮助' },
component: _import('system-administration/system-utilities/problem-help') component: _import('system-administration/system-utilities/problem-help')
},
{
path: 'system_monitoring/setting',
name: `${pre}system_monitoring-setting`,
meta: { ...meta, cache: true, title: '监控设置' },
component: _import('system-administration/system-monitoring/monitoring-configuration')
},
{
path: 'organization/production_team_manage',
name: `${pre}organization-production_team_manage`,
meta: { ...meta, cache: true, title: '班组管理' },
component: _import('production-master-data/team-model/team-management')
},
{
path: 'organization/production_shift_management',
name: `${pre}organization-production_shift_management`,
meta: { ...meta, cache: true, title: '班次管理' },
component: _import('production-master-data/team-model/shift-management')
},
{
path: 'organization/production_shift_calender',
name: `${pre}organization-production_shift_calender`,
meta: { ...meta, cache: true, title: '排班日历' },
component: _import('production-master-data/team-model/scheduling-calendar')
} }
])('system_settings-') ])('system_settings-')
} }

View File

@@ -5,6 +5,8 @@ import planningProduction from './modules/planning-production'
import dataPlatform from './modules/data-platform' import dataPlatform from './modules/data-platform'
import systemAdministration from './modules/system-administration' import systemAdministration from './modules/system-administration'
import equipmentManagement from './modules/equipment-management' import equipmentManagement from './modules/equipment-management'
import spcQualityManagement from './modules/spc-quality-management'
import scadaManagement from './modules/scada-management'
// 由于懒加载页面太多的话会造成webpack热更新太慢所以开发环境不使用懒加载只有生产环境使用懒加载 // 由于懒加载页面太多的话会造成webpack热更新太慢所以开发环境不使用懒加载只有生产环境使用懒加载
const _import = require('@/libs/util.import.' + process.env.NODE_ENV) const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
@@ -59,7 +61,9 @@ const frameIn = [
planningProduction, planningProduction,
dataPlatform, dataPlatform,
systemAdministration, systemAdministration,
equipmentManagement equipmentManagement,
spcQualityManagement,
scadaManagement
] ]
/** /**

41
src/utils/file.js Normal file
View File

@@ -0,0 +1,41 @@
import * as XLSX from 'xlsx'
export function downloadRename (blob, fileType, filename) {
const typesMap = {
xlsx: 'application/vnd.ms-excel',
xls: 'application/vnd.ms-excel',
pdf: 'application/pdf',
doc: 'application/msword',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
csv: 'text/csv'
}
const type = typesMap[fileType] || ''
const newBlob = new Blob([blob], { type })
const href = URL.createObjectURL(newBlob)
const a = document.createElement('a')
a.href = href
a.download = filename + '.' + fileType
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(href)
}
export function readExcel (file) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsBinaryString(file)
reader.onload = ev => {
try {
const data = ev.target.result
const workbook = XLSX.read(data, { type: 'binary' })
const firstSheet = workbook.Sheets[workbook.SheetNames[0]]
const list = XLSX.utils.sheet_to_json(firstSheet)
resolve(list)
} catch (e) {
reject(e)
}
}
reader.onerror = () => reject(new Error('文件读取失败'))
})
}

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -0,0 +1,407 @@
<template>
<d2-container>
<template #header>
<div class="search-bar">
<el-form ref="form" :inline="true" :model="form" size="mini">
<el-form-item :label="$t(key('battery_id'))" prop="battery_id">
<el-input
v-model="form.battery_id"
:placeholder="$t(key('enter_battery_id'))"
clearable
style="width:260px"
@keyup.enter.native="fetchData"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" :disabled="loading" :loading="loading" @click="fetchData">
{{ $t(key('query')) }}
</el-button>
<el-button icon="el-icon-download" :disabled="loading" @click="exportTree">
{{ $t(key('export')) }}
</el-button>
<el-button icon="el-icon-refresh" :disabled="loading" @click="resetForm">
{{ $t(key('reset')) }}
</el-button>
</el-form-item>
</el-form>
</div>
</template>
<div v-loading="loading" class="traceability-page">
<el-empty v-if="!hasGraph" :description="$t(key('enter_battery_id'))" />
<RelationGraph
v-else
ref="graphRef"
class="trace-graph"
:options="currentGraphOptions"
:on-node-click="onNodeClick"
>
<template #graph-plug>
<div class="trace-panel">
<div class="trace-panel__label">{{ $t(key('view_type')) }}:</div>
<el-radio-group v-model="currentCase" size="small" @change="renderGraph">
<el-radio-button label="horizontal">{{ $t(key('horizontal')) }}</el-radio-button>
<el-radio-button label="vertical">{{ $t(key('vertical')) }}</el-radio-button>
</el-radio-group>
<div class="trace-panel__label trace-panel__label--spaced">{{ $t(key('position')) }}:</div>
<div class="trace-panel__row">
<el-input v-model.trim="locator.item_code" size="small" clearable :placeholder="$t(key('enter_item_code'))" />
<el-button size="small" type="primary" @click="locateNode('item_code', locator.item_code)">
{{ $t(key('confirm_position')) }}
</el-button>
</div>
<div class="trace-panel__row">
<el-input v-model.trim="locator.item_batch" size="small" clearable :placeholder="$t(key('enter_item_batch'))" />
<el-button size="small" type="primary" @click="locateNode('item_batch', locator.item_batch)">
{{ $t(key('confirm_position')) }}
</el-button>
</div>
<div class="trace-panel__row">
<el-input
v-model.trim="locator.workingsubclass_code"
size="small"
clearable
:placeholder="$t(key('enter_work_unit'))"
/>
<el-button size="small" type="primary" @click="locateNode('workingsubclass_code', locator.workingsubclass_code, true)">
{{ $t(key('confirm_position')) }}
</el-button>
</div>
</div>
</template>
<template #node="{ node }">
<div class="trace-node" :class="{ 'is-active': activeNodeId === String(node.id) }">
<div class="trace-node__tag">
<el-tag type="warning" effect="dark" size="small">{{ node.data.type_name || '-' }}</el-tag>
</div>
<div class="trace-node__title">{{ node.text || node.id }}</div>
<div class="trace-node__body">
<div class="trace-node__line">{{ $t(key('item_name')) }}: {{ node.data.item_name || '-' }}</div>
<div class="trace-node__line">{{ $t(key('item_code')) }}: {{ node.data.item_code || '-' }}</div>
<div class="trace-node__line">{{ $t(key('item_batch')) }}: {{ node.data.item_batch || '-' }}</div>
<div class="trace-node__line">{{ $t(key('work_unit')) }}: {{ node.data.workingsubclass_code || '-' }}</div>
<div class="trace-node__line">{{ $t(key('work_unit_name')) }}: {{ node.data.workingsubclass_name || '-' }}</div>
<div class="trace-node__line">{{ $t(key('process_code')) }}: {{ node.data.process_code || '-' }}</div>
<div class="trace-node__line">{{ $t(key('start_time')) }}: {{ node.data.start_time || '-' }}</div>
<div class="trace-node__line">{{ $t(key('finish_time')) }}: {{ node.data.finish_time || '-' }}</div>
<div class="trace-node__line">{{ $t(key('inspection_person')) }}: {{ node.data.inspection_person || '-' }}</div>
<div class="trace-node__line">{{ $t(key('inspection_time')) }}: {{ node.data.inspection_time || '-' }}</div>
<div class="trace-node__line">{{ $t(key('quality_person')) }}: {{ node.data.quality_person || '-' }}</div>
<div class="trace-node__line">{{ $t(key('quality_time')) }}: {{ node.data.quality_time || '-' }}</div>
<div class="trace-node__line">{{ $t(key('device_name')) }}: {{ node.data.device_name || '-' }}</div>
<div class="trace-node__line">{{ $t(key('device_code')) }}: {{ node.data.device_code || '-' }}</div>
</div>
</div>
</template>
</RelationGraph>
</div>
</d2-container>
</template>
<script>
import RelationGraph from 'relation-graph'
import { i18nMixin } from '@/composables/useI18n'
import {
exportBackwardTraceabilityTree,
getBackwardTraceabilityData
} from '@/api/data-platform/traceability/backward'
export default {
name: 'data-platform-traceability-backward',
components: {
RelationGraph
},
mixins: [i18nMixin('page.data_platform.traceability.backward')],
data () {
return {
loading: false,
form: {
battery_id: this.$route.params.battery_id || ''
},
traceData: {},
currentCase: 'horizontal',
activeNodeId: '',
locator: {
item_code: '',
item_batch: '',
workingsubclass_code: ''
}
}
},
computed: {
graph () {
return this.traceData.relation_graph || {}
},
hasGraph () {
return Array.isArray(this.graph.nodes) && this.graph.nodes.length > 0
},
horizontalOptions () {
return {
backgroundImageNoRepeat: true,
defaultNodeBorderWidth: 0,
defaultLineShape: 3,
defaultNodeShape: 0,
defaultJunctionPoint: 'tb',
defaultNodeWidth: 330,
defaultNodeHeight: 430,
defaultFocusRootNode: false,
downloadImageFileName: this.downloadName,
defaultLineColor: 'rgba(0, 186, 189, 1)',
defaultNodeColor: 'rgba(0, 206, 209, 1)',
moveToCenterWhenResize: true,
defaultLineMarker: {
markerWidth: 12,
markerHeight: 12,
refX: '10',
refY: 6,
data: 'M2,2 L10,6 L2,10 L6,6 L2,2'
},
layouts: [
{
label: this.$t(this.key('center')),
layoutName: 'tree',
layoutClassName: 'seeks-layout-center',
centerOffset_x: 0,
centerOffset_y: 0,
distance_coefficient: 1,
layoutDirection: 'v',
from: 'top',
min_per_width: 560,
max_per_width: 1100,
min_per_height: 560,
max_per_height: 1100
}
]
}
},
verticalOptions () {
return {
defaultNodeShape: 1,
defaultNodeBorderWidth: 0,
defaultNodeWidth: 330,
defaultNodeHeight: 430,
defaultLineShape: 3,
defaultJunctionPoint: 'lr',
defaultFocusRootNode: false,
downloadImageFileName: this.downloadName,
defaultLineColor: 'rgba(0, 186, 189, 1)',
defaultNodeColor: 'rgba(0, 206, 209, 1)',
moveToCenterWhenResize: true,
layout: {
label: this.$t(this.key('center')),
layoutName: 'tree',
layoutClassName: 'seeks-layout-center',
defaultNodeShape: 0,
defaultLineShape: 1,
from: 'left',
min_per_width: 660,
max_per_width: 1100,
min_per_height: 560,
max_per_height: 700
},
defaultLineMarker: {
markerWidth: 12,
markerHeight: 12,
refX: 6,
refY: 6,
data: 'M2,2 L10,6 L2,10 L6,6 L2,2'
}
}
},
currentGraphOptions () {
return this.currentCase === 'horizontal' ? this.horizontalOptions : this.verticalOptions
},
downloadName () {
const firstNode = this.graph.nodes && this.graph.nodes[0]
return firstNode && firstNode.text ? firstNode.text : this.$t(this.key('reverse'))
}
},
mounted () {
if (this.form.battery_id) this.fetchData()
},
methods: {
ensureBatteryInput () {
if (this.form.battery_id) return true
this.$message.error(this.$t(this.key('enter_battery_id')))
return false
},
async fetchData () {
if (!this.ensureBatteryInput()) return
this.loading = true
try {
const res = await getBackwardTraceabilityData({ ...this.form })
const data = res && res.data ? res.data : res
this.traceData = data || {}
const firstNode = this.graph.nodes && this.graph.nodes[0]
this.activeNodeId = firstNode ? String(firstNode.id) : ''
this.$nextTick(this.renderGraph)
} finally {
this.loading = false
}
},
resetForm () {
this.form.battery_id = ''
this.traceData = {}
this.activeNodeId = ''
this.locator = {
item_code: '',
item_batch: '',
workingsubclass_code: ''
}
},
renderGraph () {
if (!this.hasGraph || !this.$refs.graphRef) return
this.$refs.graphRef.setOptions(this.currentGraphOptions)
this.$refs.graphRef.setJsonData(this.graph)
},
onNodeClick (nodeObject) {
this.activeNodeId = String(nodeObject.id)
},
locateNode (field, value, onlyFinalProduct = false) {
if (!value || !this.hasGraph) return
const target = this.graph.nodes.find(node => {
const data = node.data || {}
if (String(data[field] || '') !== String(value)) return false
if (!onlyFinalProduct) return true
return data.type_name === '制成品' && Number(data.status) === -1
})
if (!target) {
this.$message.warning(this.$t(this.key('node_not_found')))
return
}
this.activeNodeId = String(target.id)
this.$nextTick(() => {
const graphInstance = this.$refs.graphRef && this.$refs.graphRef.getInstance()
if (!graphInstance) return
graphInstance.setZoom(200)
graphInstance.focusNodeById(target.id)
})
},
async exportTree () {
if (!this.ensureBatteryInput()) return
if (!this.hasGraph) {
this.$message.warning(this.$t(this.key('query_before_export')))
return
}
const res = await exportBackwardTraceabilityTree({
...this.form,
tree_list: JSON.stringify(this.graph.nodes || [])
})
const data = res && res.data ? res.data : res
this.downloadExport(data)
},
downloadExport (url) {
if (!url) return
const link = document.createElement('a')
link.href = url
link.download = `${this.form.battery_id}${this.$t(this.key('reverse'))}`
link.target = '_blank'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
}
}
</script>
<style lang="scss" scoped>
.search-bar {
margin-bottom: -18px;
}
.traceability-page {
height: calc(100vh - 170px);
min-height: 620px;
}
.trace-graph {
width: 100%;
height: 100%;
min-height: 620px;
border: 1px solid #ebeef5;
background: #f7f9fb;
}
.trace-panel {
position: absolute;
top: 12px;
left: 12px;
z-index: 20;
width: 360px;
padding: 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background: rgba(255, 255, 255, 0.94);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}
.trace-panel__label {
margin-bottom: 8px;
color: #606266;
font-size: 13px;
font-weight: 600;
}
.trace-panel__label--spaced {
margin-top: 14px;
}
.trace-panel__row {
display: grid;
grid-template-columns: minmax(0, 1fr) 92px;
gap: 8px;
margin-top: 10px;
}
.trace-node {
position: relative;
width: 330px;
height: 410px;
padding: 28px 12px 12px;
color: #303133;
cursor: pointer;
text-align: left;
}
.trace-node.is-active .trace-node__body {
border-color: #f56c6c;
box-shadow: 0 0 0 2px rgba(245, 108, 108, 0.18);
}
.trace-node__tag {
position: absolute;
top: 0;
left: 0;
}
.trace-node__title {
min-height: 22px;
margin-bottom: 8px;
padding-right: 8px;
font-size: 16px;
font-weight: 600;
line-height: 1.4;
word-break: break-word;
}
.trace-node__body {
height: 340px;
overflow: auto;
padding: 10px;
border: 1px solid transparent;
border-radius: 8px;
background: #fff;
color: #555;
}
.trace-node__line {
min-height: 24px;
padding: 4px 0;
border-bottom: 1px solid #efefef;
line-height: 1.4;
white-space: normal;
word-break: break-all;
}
</style>

View File

@@ -0,0 +1,275 @@
<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="fetchRecords"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" :disabled="loading" @click="fetchRecords">
{{ $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>
<div v-loading="loading" class="curve-page">
<aside class="curve-list">
<el-empty v-if="records.length === 0" :description="$t(key('data_empty'))" :image-size="80" />
<el-menu v-else :default-active="String(activeIndex)" @select="selectRecord">
<el-menu-item v-for="(row, index) in records" :key="row.id || index" :index="String(index)">
<div class="curve-list__item">
<div>{{ $t(key('batch_name')) }}: {{ row.batch || '-' }}</div>
<div>{{ $t(key('process_name')) }}: {{ row.process_name || '-' }}</div>
<div>{{ $t(key('upload_time')) }}: {{ row.end_time || '-' }}</div>
</div>
</el-menu-item>
</el-menu>
</aside>
<main class="curve-content">
<div class="curve-toolbar">
<el-form :inline="true" size="mini" label-width="80px">
<el-form-item :label="$t(key('device_code'))">
<el-input :value="activeRecord.device_code || ''" disabled style="width:180px" />
</el-form-item>
<el-form-item :label="$t(key('battery_no'))">
<el-input :value="activeRecord.channel || ''" disabled style="width:180px" />
</el-form-item>
<el-form-item>
<el-button type="danger" icon="el-icon-download" :disabled="!activeRecord.id" @click="exportAllData">
{{ $t(key('export_all_data')) }}
</el-button>
<el-button type="warning" icon="el-icon-view" :disabled="!activeRecord.url" @click="viewFullTrayCurve">
{{ $t(key('view_full_tray_curve')) }}
</el-button>
</el-form-item>
</el-form>
</div>
<div ref="chart" class="curve-chart" />
</main>
</div>
</d2-container>
</template>
<script>
import * as echarts from 'echarts'
import { i18nMixin } from '@/composables/useI18n'
import {
exportBatteryCurveData,
getBatteryCurveData,
getBatteryCurveRecords
} from '@/api/data-platform/traceability/battery-curve'
export default {
name: 'data-platform-traceability-battery-curve',
mixins: [i18nMixin('page.data_platform.traceability.battery_curve')],
data () {
return {
loading: false,
chartLoading: false,
search: {
battery_id: ''
},
records: [],
activeIndex: -1,
chart: null,
xAxis: [],
seriesData: []
}
},
computed: {
activeRecord () {
return this.records[this.activeIndex] || {}
}
},
mounted () {
this.initChart()
window.addEventListener('resize', this.resizeChart)
},
beforeDestroy () {
window.removeEventListener('resize', this.resizeChart)
if (this.chart) this.chart.dispose()
},
methods: {
responseData (res) {
return res && res.data !== undefined ? res.data : res
},
initChart () {
this.chart = echarts.init(this.$refs.chart)
this.drawChart()
},
resizeChart () {
if (this.chart) this.chart.resize()
},
async fetchRecords () {
this.loading = true
try {
const res = await getBatteryCurveRecords({
...this.search,
action: 'battery_traceability_search'
})
const data = this.responseData(res)
this.records = Array.isArray(data) ? data : []
this.activeIndex = this.records.length ? 0 : -1
if (this.activeIndex > -1) {
await this.loadCurve(this.activeRecord.id)
} else {
this.xAxis = []
this.seriesData = []
this.drawChart()
}
} finally {
this.loading = false
}
},
resetSearch () {
this.search.battery_id = ''
this.records = []
this.activeIndex = -1
this.xAxis = []
this.seriesData = []
this.drawChart()
},
selectRecord (index) {
this.activeIndex = Number(index)
this.loadCurve(this.activeRecord.id)
},
async loadCurve (id) {
if (!id) return
this.chartLoading = true
if (this.chart) this.chart.showLoading()
try {
const res = await getBatteryCurveData({ id, action: 'batterycurveread' })
const data = this.responseData(res) || {}
if (Array.isArray(data)) {
this.xAxis = []
this.seriesData = []
} else {
this.xAxis = data.tm || []
this.seriesData = [
{ lineStyle: { normal: { width: 1 } }, name: this.$t(this.key('current')), type: 'line', symbolSize: 0, data: data.A || [] },
{ lineStyle: { normal: { width: 1 } }, yAxisIndex: 1, name: this.$t(this.key('voltage')), type: 'line', symbolSize: 0, data: data.V || [] },
{ lineStyle: { normal: { width: 1 } }, yAxisIndex: 2, name: this.$t(this.key('capacity')), type: 'bar', symbolSize: 0, data: data.C || [] }
]
}
this.drawChart()
} finally {
this.chartLoading = false
if (this.chart) this.chart.hideLoading()
}
},
drawChart () {
if (!this.chart) return
const option = {
title: { text: this.$t(this.key('data_chart')) },
tooltip: { trigger: 'axis' },
legend: {
data: [this.$t(this.key('current')), this.$t(this.key('voltage')), this.$t(this.key('capacity'))],
selected: {
[this.$t(this.key('current'))]: true,
[this.$t(this.key('voltage'))]: true,
[this.$t(this.key('capacity'))]: false
}
},
toolbox: { feature: { saveAsImage: {} } },
dataZoom: [
{ type: 'inside', xAxisIndex: [0], realtime: false },
{ type: 'inside', yAxisIndex: [0], realtime: false }
],
xAxis: { type: 'category', boundaryGap: false, data: this.xAxis, scale: false },
yAxis: [
{ type: 'value', scale: false, name: this.$t(this.key('current')), axisLabel: { formatter: '{value} mA' } },
{ type: 'value', scale: false, name: this.$t(this.key('voltage')), axisLabel: { formatter: '{value} mV' } },
{ type: 'value', scale: false, name: this.$t(this.key('capacity')), axisLabel: { formatter: '{value} mAH' }, position: 'right', offset: 80 }
],
series: this.seriesData
}
this.chart.setOption(option, true)
},
viewFullTrayCurve () {
if (this.activeRecord.url) window.location.href = this.activeRecord.url
},
async exportAllData () {
if (!this.activeRecord.id) return
const res = await exportBatteryCurveData({
id: this.activeRecord.id,
action: 'export_all_data'
})
const data = this.responseData(res)
if (!data) return
const link = document.createElement('a')
link.href = process.env.VUE_APP_PRO_PUBLIC_URL ? process.env.VUE_APP_PRO_PUBLIC_URL + data : data
link.target = '_blank'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
}
}
</script>
<style lang="scss" scoped>
.search-bar {
margin-bottom: -18px;
}
.curve-page {
display: grid;
grid-template-columns: 260px minmax(0, 1fr);
height: calc(100vh - 170px);
min-height: 560px;
border: 1px solid #ebeef5;
}
.curve-list {
overflow: auto;
border-right: 1px solid #ebeef5;
background: #fff;
}
.curve-list__item {
width: 100%;
overflow: hidden;
line-height: 1.5;
white-space: normal;
word-break: break-all;
}
.curve-list ::v-deep .el-menu-item {
height: auto;
min-height: 86px;
padding: 8px 12px !important;
line-height: 1.5;
}
.curve-content {
min-width: 0;
padding: 12px;
background: #fafafa;
}
.curve-toolbar {
margin-bottom: 12px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
}
.curve-chart {
height: calc(100% - 64px);
min-height: 430px;
border: 1px solid #ebeef5;
background: #fff;
}
</style>

View 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>

View File

@@ -0,0 +1,217 @@
<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('item_code'))" prop="item_code">
<el-input
v-model.trim="search.item_code"
:placeholder="$t(key('enter_item_code'))"
clearable
style="width:220px"
@keyup.enter.native="onSearch"
/>
</el-form-item>
<el-form-item :label="$t(key('item_batch'))" prop="item_batch">
<el-input
v-model.trim="search.item_batch"
:placeholder="$t(key('enter_item_batch'))"
clearable
style="width:220px"
@keyup.enter.native="onSearch"
/>
</el-form-item>
<el-form-item :label="$t(key('item_id'))" prop="item_id">
<el-input
v-model.trim="search.item_id"
:placeholder="$t(key('enter_item_id'))"
clearable
style="width:220px"
@keyup.enter.native="onSearch"
/>
</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-download" :disabled="loading" @click="exportTask">
{{ $t(key('export')) }}
</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="pagedData"
:loading="loading"
:toolbar-buttons="[]"
:row-buttons="rowButtons"
: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 {
createForwardTraceabilityExportTask,
getForwardTraceabilityList
} from '@/api/data-platform/traceability/forward'
export default {
name: 'data-platform-traceability-forward',
components: { PageTable },
mixins: [i18nMixin('page.data_platform.traceability.forward')],
data () {
return {
loading: false,
search: {
item_code: '',
item_batch: '',
item_id: ''
},
tableData: [],
pagination: {
current: 1,
size: 10,
total: 0
}
}
},
computed: {
columns () {
return useTableColumns([
{ prop: 'battery_id', label: this.key('battery_id'), minWidth: 170, showOverflowTooltip: true },
{ prop: 'batch', label: this.key('batch_id'), minWidth: 150, showOverflowTooltip: true },
{ prop: 'item_name', label: this.key('item_name'), minWidth: 180, showOverflowTooltip: true },
{ prop: 'item_code', label: this.key('item_code'), minWidth: 170, showOverflowTooltip: true },
{ prop: 'item_batch', label: this.key('item_batch'), minWidth: 170, showOverflowTooltip: true },
{ prop: 'device_code', label: this.key('device_code'), minWidth: 150, showOverflowTooltip: true },
{ prop: 'workingsubclass', label: this.key('work_unit'), minWidth: 150, showOverflowTooltip: true },
{ prop: 'process_code', label: this.key('process_code'), minWidth: 150, showOverflowTooltip: true },
{ prop: 'finish_time', label: this.key('finish_time'), minWidth: 170, showOverflowTooltip: true }
], {
selectionWidth: 0,
indexWidth: 55,
operationWidth: 120
})
},
rowButtons () {
return [
{
key: 'reverse',
label: this.key('reverse'),
type: 'primary',
icon: 'el-icon-position',
size: 'mini',
onClick: this.goBackwardTraceability
}
]
},
pagedData () {
const start = (this.pagination.current - 1) * this.pagination.size
return this.tableData.slice(start, start + this.pagination.size)
}
},
created () {
this.fetchData()
},
methods: {
rowKey (row) {
return row.id || `${row.battery_id || ''}-${row.item_code || ''}-${row.item_batch || ''}-${row.finish_time || ''}`
},
responseData (res) {
return res && res.data !== undefined ? res.data : res
},
buildParams () {
return {
...this.search,
page_no: this.pagination.current,
page_size: this.pagination.size
}
},
validateSearch () {
if (this.search.item_code && !this.search.item_batch) {
this.$message.error(this.$t(this.key('enter_item_batch')))
return false
}
return true
},
async fetchData () {
if (!this.validateSearch()) return
this.loading = true
try {
const res = await getForwardTraceabilityList(this.buildParams())
const data = this.responseData(res)
this.tableData = Array.isArray(data) ? data : []
this.pagination.total = this.tableData.length
} finally {
this.loading = false
}
},
onSearch () {
this.pagination.current = 1
this.fetchData()
},
onReset () {
this.search = {
item_code: '',
item_batch: '',
item_id: ''
}
this.pagination.current = 1
this.fetchData()
},
onPageChange (page) {
this.pagination = { ...this.pagination, ...page }
},
hasFilter () {
return Object.keys(this.search).some(key => this.search[key])
},
async exportTask () {
if (!this.hasFilter()) {
this.$message.error(this.$t(this.key('please_select_filter_condition')))
return
}
await this.$confirm(this.$t(this.key('export_confirm')), this.$t(this.key('prompt')), {
type: 'warning',
confirmButtonText: this.$t(this.key('confirm')),
cancelButtonText: this.$t(this.key('cancel'))
})
await createForwardTraceabilityExportTask({
...this.search,
total: this.pagination.total,
action: 'download'
})
this.$message.success(this.$t(this.key('create_download_task_success')))
this.$router.push({ name: 'task' })
},
goBackwardTraceability (row) {
this.$router.push({
name: 'data_middleground-basic_traceability-reverse_direction_traceability',
params: { battery_id: row.battery_id }
})
}
}
}
</script>
<style lang="scss" scoped>
.search-bar {
margin-bottom: -18px;
}
</style>

View 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>

View File

@@ -0,0 +1,257 @@
<template>
<d2-container>
<template #header>
<div class="search-bar">
<el-form :inline="true" size="mini">
<el-form-item :label="$t(key('keyword'))">
<el-input
v-model="search.keyword"
:placeholder="$t(key('enter_keyword'))"
clearable
style="width:200px"
@keyup.enter.native="onSearch"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="onSearch">{{ $t(key('search')) }}</el-button>
<el-button icon="el-icon-refresh" @click="onReset">{{ $t(key('reset')) }}</el-button>
</el-form-item>
</el-form>
</div>
</template>
<page-table
ref="pageTable"
:columns="columns"
:data="tableData"
:loading="loading"
:toolbar-buttons="toolbarButtons"
:row-buttons="rowButtons"
:pagination="pagination"
auto-height
@page-change="onPageChange"
@selection-change="onSelect"
/>
<page-dialog-form
ref="dialogForm"
:visible.sync="dialogVisible"
:title="dialogTitle"
width="40%"
:form-cols="formCols"
:form-data="formData"
:rules="rules"
label-width="130px"
:submitting="submitting"
:confirm-text="key('confirm')"
:cancel-text="key('cancel')"
@submit="onDialogSubmit"
@close="onDialogClose"
/>
</d2-container>
</template>
<script>
import { useTableColumns } from '@/composables/useTableColumns'
import { useTableButtons } from '@/composables/useTableButtons'
import { i18nMixin } from '@/composables/useI18n'
import { confirmMixin } from '@/composables/useConfirmHandle'
import PageTable from '@/components/page-table'
import PageDialogForm from '@/components/page-dialog-form'
import { getList, createItem, editItem, deleteItem, createExportTask } from '@/api/equipment-management/consumables-category'
export default {
name: 'equipment-management-consumables-category',
components: { PageTable, PageDialogForm },
mixins: [i18nMixin('page.equipment_management.consumables_management.consumables_category'), confirmMixin],
data () {
return {
loading: false,
submitting: false,
tableData: [],
selectedRows: [],
dialogVisible: false,
dialogTitle: '',
editId: '',
handleType: 'create',
search: {
keyword: ''
},
pagination: { current: 1, size: 10, total: 0 },
formData: {
device_consumables_category_code: '',
device_consumables_category_name: '',
remark: ''
},
rules: {
device_consumables_category_code: [{ required: true, message: this.key('enter_consumables_category_code'), trigger: 'blur' }],
device_consumables_category_name: [{ required: true, message: this.key('enter_consumables_category_name'), trigger: 'blur' }]
},
columns: [],
toolbarButtons: [],
rowButtons: [],
formCols: [
[
{
type: 'input',
prop: 'device_consumables_category_code',
label: this.key('consumables_category_code'),
placeholder: this.key('enter_consumables_category_code'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'device_consumables_category_name',
label: this.key('consumables_category_name'),
placeholder: this.key('enter_consumables_category_name'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'remark',
label: this.key('remark'),
placeholder: this.key('enter_remark'),
inputType: 'textarea',
autosize: { minRows: 2, maxRows: 6 },
clearable: true,
style: { width: '90%' }
}
]
]
}
},
created () {
this.columns = useTableColumns([
{ prop: 'device_consumables_category_code', label: this.key('consumables_category_code'), minWidth: 140 },
{ prop: 'device_consumables_category_name', label: this.key('consumables_category_name'), minWidth: 140 },
{ prop: 'remark', label: this.key('remark'), minWidth: 140 },
{ prop: 'create_time', label: this.key('create_time'), minWidth: 140 },
{ prop: '_actions', label: this.key('operation'), width: 170, fixed: 'right' }
])
const btns = useTableButtons({
toolbar: [
{ key: 'add', label: this.key('add'), icon: 'el-icon-plus', type: 'primary', auth: '/device_management/device_consumables/device_consumables_category/create', onClick: this.openAdd },
{ key: 'export', label: this.key('export'), icon: 'el-icon-download', auth: '/device_management/device_consumables/device_consumables_category/export', onClick: this.handleExport }
],
row: [
{ key: 'edit', label: this.key('edit'), icon: 'el-icon-edit', auth: '/device_management/device_consumables/device_consumables_category/edit', onClick: this.openEdit },
{ key: 'delete', label: this.key('delete'), icon: 'el-icon-delete', color: 'danger', auth: '/device_management/device_consumables/device_consumables_category/delete', onClick: this.handleDelete }
]
}, this.$permission)
this.toolbarButtons = btns.toolbarButtons
this.rowButtons = btns.rowButtons
this.fetchData()
},
methods: {
normalizeResponse (res) {
const data = res && res.data !== undefined ? res.data : res
if (Array.isArray(data)) return { list: data, total: data.length }
if (data && Array.isArray(data.data)) return { list: data.data, total: Number(data.count || data.total || data.data.length) }
if (data && data.data && Array.isArray(data.data.data)) return { list: data.data.data, total: Number(data.data.count || data.data.total || data.data.data.length) }
return { list: [], total: 0 }
},
async fetchData () {
this.loading = true
try {
const res = await getList({ ...this.search, page_no: this.pagination.current, page_size: this.pagination.size })
const data = this.normalizeResponse(res)
this.tableData = data.list
this.pagination.total = data.total
} finally {
this.loading = false
}
},
onSearch () {
this.pagination.current = 1
this.fetchData()
},
onReset () {
this.search = {
keyword: ''
}
this.pagination.current = 1
this.fetchData()
},
onPageChange (page) {
this.pagination.current = page.current
this.pagination.size = page.size
this.fetchData()
},
onSelect (rows) {
this.selectedRows = rows
},
resetForm () {
this.formData = {
device_consumables_category_code: '',
device_consumables_category_name: '',
remark: ''
}
this.editId = ''
},
openAdd () {
this.handleType = 'create'
this.dialogTitle = this.key('add_title')
this.$nextTick(() => {
this.$refs.dialogForm && this.$refs.dialogForm.reset()
this.resetForm()
this.dialogVisible = true
})
},
openEdit (row) {
this.handleType = 'edit'
this.dialogTitle = this.key('edit_title')
this.editId = row.id
this.formData = {
device_consumables_category_code: row.device_consumables_category_code || '',
device_consumables_category_name: row.device_consumables_category_name || '',
remark: row.remark || ''
}
this.dialogVisible = true
},
async onDialogSubmit () {
this.submitting = true
try {
if (this.handleType === 'create') await createItem(this.formData)
else await editItem({ ...this.formData, id: this.editId })
this.$message.success(this.$t(this.key('operation_success')))
this.dialogVisible = false
this.fetchData()
} finally {
this.submitting = false
}
},
onDialogClose () {
this.resetForm()
},
async handleDelete (row) {
const cancelled = await this.$confirmAction(
{ message: this.key('confirm_delete'), title: this.key('tip') },
() => deleteItem({ id: [row.id] })
)
if (cancelled) return
this.$message.success(this.$t(this.key('operation_success')))
this.pagination.current = Math.min(this.pagination.current, Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1)
this.fetchData()
},
async handleExport () {
const cancelled = await this.$confirmAction(
{ message: this.key('confirm_export'), title: this.key('tip') },
() => createExportTask({ ...this.search })
)
if (cancelled) return
this.$message.success(this.$t(this.key('operation_success')))
}
}
}
</script>
<style scoped>
.search-bar { padding: 10px 0; }
/deep/ .el-form-item--mini.el-form-item { margin-bottom: 4px; }
</style>

View File

@@ -0,0 +1,284 @@
<template>
<d2-container>
<template #header>
<div class="search-bar">
<el-form :inline="true" size="mini">
<el-form-item :label="$t(key('keyword'))">
<el-input
v-model="search.keyword"
:placeholder="$t(key('enter_keyword'))"
clearable
style="width:200px"
@keyup.enter.native="onSearch"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="onSearch">{{ $t(key('search')) }}</el-button>
<el-button icon="el-icon-refresh" @click="onReset">{{ $t(key('reset')) }}</el-button>
</el-form-item>
</el-form>
</div>
</template>
<page-table
ref="pageTable"
:columns="columns"
:data="tableData"
:loading="loading"
:toolbar-buttons="toolbarButtons"
:row-buttons="rowButtons"
:pagination="pagination"
auto-height
@page-change="onPageChange"
@selection-change="onSelect"
/>
<page-dialog-form
ref="dialogForm"
:visible.sync="dialogVisible"
:title="dialogTitle"
width="40%"
:form-cols="formCols"
:form-data="formData"
:rules="rules"
label-width="130px"
:submitting="submitting"
:confirm-text="key('confirm')"
:cancel-text="key('cancel')"
@submit="onDialogSubmit"
@close="onDialogClose"
/>
</d2-container>
</template>
<script>
import { useTableColumns } from '@/composables/useTableColumns'
import { useTableButtons } from '@/composables/useTableButtons'
import { i18nMixin } from '@/composables/useI18n'
import { confirmMixin } from '@/composables/useConfirmHandle'
import PageTable from '@/components/page-table'
import PageDialogForm from '@/components/page-dialog-form'
import { getList, createItem, editItem, deleteItem, createExportTask } from '@/api/equipment-management/consumables-items'
export default {
name: 'equipment-management-consumables-items',
components: { PageTable, PageDialogForm },
mixins: [i18nMixin('page.equipment_management.consumables_management.consumables_items'), confirmMixin],
data () {
return {
loading: false,
submitting: false,
tableData: [],
selectedRows: [],
dialogVisible: false,
dialogTitle: '',
editId: '',
handleType: 'create',
search: {
keyword: ''
},
pagination: { current: 1, size: 10, total: 0 },
formData: {
device_consumables_item_code: '',
device_consumables_item_name: '',
device_consumables_category_id: '',
specification: '',
remark: ''
},
rules: {
device_consumables_item_code: [{ required: true, message: this.key('enter_consumables_item_code'), trigger: 'blur' }],
device_consumables_item_name: [{ required: true, message: this.key('enter_consumables_item_name'), trigger: 'blur' }]
},
columns: [],
toolbarButtons: [],
rowButtons: [],
formCols: [
[
{
type: 'input',
prop: 'device_consumables_item_code',
label: this.key('consumables_item_code'),
placeholder: this.key('enter_consumables_item_code'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'device_consumables_item_name',
label: this.key('consumables_item_name'),
placeholder: this.key('enter_consumables_item_name'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'device_consumables_category_id',
label: this.key('consumables_category_id'),
placeholder: this.key('enter_consumables_category_id'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'specification',
label: this.key('specification'),
placeholder: this.key('enter_specification'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'remark',
label: this.key('remark'),
placeholder: this.key('enter_remark'),
inputType: 'textarea',
autosize: { minRows: 2, maxRows: 6 },
clearable: true,
style: { width: '90%' }
}
]
]
}
},
created () {
this.columns = useTableColumns([
{ prop: 'device_consumables_item_code', label: this.key('consumables_item_code'), minWidth: 140 },
{ prop: 'device_consumables_item_name', label: this.key('consumables_item_name'), minWidth: 140 },
{ prop: 'device_consumables_category_name', label: this.key('consumables_category'), minWidth: 140 },
{ prop: 'specification', label: this.key('specification'), minWidth: 140 },
{ prop: 'remark', label: this.key('remark'), minWidth: 140 },
{ prop: '_actions', label: this.key('operation'), width: 170, fixed: 'right' }
])
const btns = useTableButtons({
toolbar: [
{ key: 'add', label: this.key('add'), icon: 'el-icon-plus', type: 'primary', auth: '/device_management/device_consumables/device_consumables_items/create', onClick: this.openAdd },
{ key: 'export', label: this.key('export'), icon: 'el-icon-download', auth: '/device_management/device_consumables/device_consumables_items/export', onClick: this.handleExport }
],
row: [
{ key: 'edit', label: this.key('edit'), icon: 'el-icon-edit', auth: '/device_management/device_consumables/device_consumables_items/edit', onClick: this.openEdit },
{ key: 'delete', label: this.key('delete'), icon: 'el-icon-delete', color: 'danger', auth: '/device_management/device_consumables/device_consumables_items/delete', onClick: this.handleDelete }
]
}, this.$permission)
this.toolbarButtons = btns.toolbarButtons
this.rowButtons = btns.rowButtons
this.fetchData()
},
methods: {
normalizeResponse (res) {
const data = res && res.data !== undefined ? res.data : res
if (Array.isArray(data)) return { list: data, total: data.length }
if (data && Array.isArray(data.data)) return { list: data.data, total: Number(data.count || data.total || data.data.length) }
if (data && data.data && Array.isArray(data.data.data)) return { list: data.data.data, total: Number(data.data.count || data.data.total || data.data.data.length) }
return { list: [], total: 0 }
},
async fetchData () {
this.loading = true
try {
const res = await getList({ ...this.search, page_no: this.pagination.current, page_size: this.pagination.size })
const data = this.normalizeResponse(res)
this.tableData = data.list
this.pagination.total = data.total
} finally {
this.loading = false
}
},
onSearch () {
this.pagination.current = 1
this.fetchData()
},
onReset () {
this.search = {
keyword: ''
}
this.pagination.current = 1
this.fetchData()
},
onPageChange (page) {
this.pagination.current = page.current
this.pagination.size = page.size
this.fetchData()
},
onSelect (rows) {
this.selectedRows = rows
},
resetForm () {
this.formData = {
device_consumables_item_code: '',
device_consumables_item_name: '',
device_consumables_category_id: '',
specification: '',
remark: ''
}
this.editId = ''
},
openAdd () {
this.handleType = 'create'
this.dialogTitle = this.key('add_title')
this.$nextTick(() => {
this.$refs.dialogForm && this.$refs.dialogForm.reset()
this.resetForm()
this.dialogVisible = true
})
},
openEdit (row) {
this.handleType = 'edit'
this.dialogTitle = this.key('edit_title')
this.editId = row.id
this.formData = {
device_consumables_item_code: row.device_consumables_item_code || '',
device_consumables_item_name: row.device_consumables_item_name || '',
device_consumables_category_id: row.device_consumables_category_id || '',
specification: row.specification || '',
remark: row.remark || ''
}
this.dialogVisible = true
},
async onDialogSubmit () {
this.submitting = true
try {
if (this.handleType === 'create') await createItem(this.formData)
else await editItem({ ...this.formData, id: this.editId })
this.$message.success(this.$t(this.key('operation_success')))
this.dialogVisible = false
this.fetchData()
} finally {
this.submitting = false
}
},
onDialogClose () {
this.resetForm()
},
async handleDelete (row) {
const cancelled = await this.$confirmAction(
{ message: this.key('confirm_delete'), title: this.key('tip') },
() => deleteItem({ id: [row.id] })
)
if (cancelled) return
this.$message.success(this.$t(this.key('operation_success')))
this.pagination.current = Math.min(this.pagination.current, Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1)
this.fetchData()
},
async handleExport () {
const cancelled = await this.$confirmAction(
{ message: this.key('confirm_export'), title: this.key('tip') },
() => createExportTask({ ...this.search })
)
if (cancelled) return
this.$message.success(this.$t(this.key('operation_success')))
}
}
}
</script>
<style scoped>
.search-bar { padding: 10px 0; }
/deep/ .el-form-item--mini.el-form-item { margin-bottom: 4px; }
</style>

View File

@@ -0,0 +1,298 @@
<template>
<d2-container>
<template #header>
<div class="search-bar">
<el-form :inline="true" size="mini">
<el-form-item :label="$t(key('keyword'))">
<el-input
v-model="search.keyword"
:placeholder="$t(key('enter_keyword'))"
clearable
style="width:200px"
@keyup.enter.native="onSearch"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="onSearch">{{ $t(key('search')) }}</el-button>
<el-button icon="el-icon-refresh" @click="onReset">{{ $t(key('reset')) }}</el-button>
</el-form-item>
</el-form>
</div>
</template>
<page-table
ref="pageTable"
:columns="columns"
:data="tableData"
:loading="loading"
:toolbar-buttons="toolbarButtons"
:row-buttons="rowButtons"
:pagination="pagination"
auto-height
@page-change="onPageChange"
@selection-change="onSelect"
/>
<page-dialog-form
ref="dialogForm"
:visible.sync="dialogVisible"
:title="dialogTitle"
width="40%"
:form-cols="formCols"
:form-data="formData"
:rules="rules"
label-width="130px"
:submitting="submitting"
:confirm-text="key('confirm')"
:cancel-text="key('cancel')"
@submit="onDialogSubmit"
@close="onDialogClose"
/>
</d2-container>
</template>
<script>
import { useTableColumns } from '@/composables/useTableColumns'
import { useTableButtons } from '@/composables/useTableButtons'
import { i18nMixin } from '@/composables/useI18n'
import { confirmMixin } from '@/composables/useConfirmHandle'
import PageTable from '@/components/page-table'
import PageDialogForm from '@/components/page-dialog-form'
import { getList, createItem, editItem, deleteItem, createExportTask } from '@/api/equipment-management/consumables-lifecycle'
export default {
name: 'equipment-management-consumables-lifecycle',
components: { PageTable, PageDialogForm },
mixins: [i18nMixin('page.equipment_management.consumables_management.consumables_lifecycle'), confirmMixin],
data () {
return {
loading: false,
submitting: false,
tableData: [],
selectedRows: [],
dialogVisible: false,
dialogTitle: '',
editId: '',
handleType: 'create',
search: {
keyword: ''
},
pagination: { current: 1, size: 10, total: 0 },
formData: {
device_consumables_item_id: '',
device_id: '',
standard_life: '',
warning_life: '',
used_life: '',
remark: ''
},
rules: {
device_consumables_item_id: [{ required: true, message: this.key('enter_consumables_item_id'), trigger: 'blur' }],
device_id: [{ required: true, message: this.key('enter_device_id'), trigger: 'blur' }]
},
columns: [],
toolbarButtons: [],
rowButtons: [],
formCols: [
[
{
type: 'input',
prop: 'device_consumables_item_id',
label: this.key('consumables_item_id'),
placeholder: this.key('enter_consumables_item_id'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'device_id',
label: this.key('device_id'),
placeholder: this.key('enter_device_id'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'standard_life',
label: this.key('standard_life'),
placeholder: this.key('enter_standard_life'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'warning_life',
label: this.key('warning_life'),
placeholder: this.key('enter_warning_life'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'used_life',
label: this.key('used_life'),
placeholder: this.key('enter_used_life'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'remark',
label: this.key('remark'),
placeholder: this.key('enter_remark'),
inputType: 'textarea',
autosize: { minRows: 2, maxRows: 6 },
clearable: true,
style: { width: '90%' }
}
]
]
}
},
created () {
this.columns = useTableColumns([
{ prop: 'device_consumables_item_name', label: this.key('consumables_item'), minWidth: 140 },
{ prop: 'device_name', label: this.key('device_name'), minWidth: 140 },
{ prop: 'standard_life', label: this.key('standard_life'), minWidth: 140 },
{ prop: 'warning_life', label: this.key('warning_life'), minWidth: 140 },
{ prop: 'used_life', label: this.key('used_life'), minWidth: 140 },
{ prop: 'replace_time', label: this.key('replace_time'), minWidth: 140 },
{ prop: '_actions', label: this.key('operation'), width: 170, fixed: 'right' }
])
const btns = useTableButtons({
toolbar: [
{ key: 'add', label: this.key('add'), icon: 'el-icon-plus', type: 'primary', auth: '/device_management/device_consumables/device_consumables_lifetime_management/create', onClick: this.openAdd },
{ key: 'export', label: this.key('export'), icon: 'el-icon-download', auth: '/device_management/device_consumables/device_consumables_lifetime_management/export', onClick: this.handleExport }
],
row: [
{ key: 'edit', label: this.key('edit'), icon: 'el-icon-edit', auth: '/device_management/device_consumables/device_consumables_lifetime_management/edit', onClick: this.openEdit },
{ key: 'delete', label: this.key('delete'), icon: 'el-icon-delete', color: 'danger', auth: '/device_management/device_consumables/device_consumables_lifetime_management/delete', onClick: this.handleDelete }
]
}, this.$permission)
this.toolbarButtons = btns.toolbarButtons
this.rowButtons = btns.rowButtons
this.fetchData()
},
methods: {
normalizeResponse (res) {
const data = res && res.data !== undefined ? res.data : res
if (Array.isArray(data)) return { list: data, total: data.length }
if (data && Array.isArray(data.data)) return { list: data.data, total: Number(data.count || data.total || data.data.length) }
if (data && data.data && Array.isArray(data.data.data)) return { list: data.data.data, total: Number(data.data.count || data.data.total || data.data.data.length) }
return { list: [], total: 0 }
},
async fetchData () {
this.loading = true
try {
const res = await getList({ ...this.search, page_no: this.pagination.current, page_size: this.pagination.size })
const data = this.normalizeResponse(res)
this.tableData = data.list
this.pagination.total = data.total
} finally {
this.loading = false
}
},
onSearch () {
this.pagination.current = 1
this.fetchData()
},
onReset () {
this.search = {
keyword: ''
}
this.pagination.current = 1
this.fetchData()
},
onPageChange (page) {
this.pagination.current = page.current
this.pagination.size = page.size
this.fetchData()
},
onSelect (rows) {
this.selectedRows = rows
},
resetForm () {
this.formData = {
device_consumables_item_id: '',
device_id: '',
standard_life: '',
warning_life: '',
used_life: '',
remark: ''
}
this.editId = ''
},
openAdd () {
this.handleType = 'create'
this.dialogTitle = this.key('add_title')
this.$nextTick(() => {
this.$refs.dialogForm && this.$refs.dialogForm.reset()
this.resetForm()
this.dialogVisible = true
})
},
openEdit (row) {
this.handleType = 'edit'
this.dialogTitle = this.key('edit_title')
this.editId = row.id
this.formData = {
device_consumables_item_id: row.device_consumables_item_id || '',
device_id: row.device_id || '',
standard_life: row.standard_life || '',
warning_life: row.warning_life || '',
used_life: row.used_life || '',
remark: row.remark || ''
}
this.dialogVisible = true
},
async onDialogSubmit () {
this.submitting = true
try {
if (this.handleType === 'create') await createItem(this.formData)
else await editItem({ ...this.formData, id: this.editId })
this.$message.success(this.$t(this.key('operation_success')))
this.dialogVisible = false
this.fetchData()
} finally {
this.submitting = false
}
},
onDialogClose () {
this.resetForm()
},
async handleDelete (row) {
const cancelled = await this.$confirmAction(
{ message: this.key('confirm_delete'), title: this.key('tip') },
() => deleteItem({ id: [row.id] })
)
if (cancelled) return
this.$message.success(this.$t(this.key('operation_success')))
this.pagination.current = Math.min(this.pagination.current, Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1)
this.fetchData()
},
async handleExport () {
const cancelled = await this.$confirmAction(
{ message: this.key('confirm_export'), title: this.key('tip') },
() => createExportTask({ ...this.search })
)
if (cancelled) return
this.$message.success(this.$t(this.key('operation_success')))
}
}
}
</script>
<style scoped>
.search-bar { padding: 10px 0; }
/deep/ .el-form-item--mini.el-form-item { margin-bottom: 4px; }
</style>

View File

@@ -0,0 +1,311 @@
<template>
<d2-container>
<template #header>
<div class="search-bar">
<el-form :inline="true" size="mini">
<el-form-item :label="$t(key('keyword'))">
<el-input
v-model="search.keyword"
:placeholder="$t(key('enter_keyword'))"
clearable
style="width:200px"
@keyup.enter.native="onSearch"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="onSearch">{{ $t(key('search')) }}</el-button>
<el-button icon="el-icon-refresh" @click="onReset">{{ $t(key('reset')) }}</el-button>
</el-form-item>
</el-form>
</div>
</template>
<page-table
ref="pageTable"
:columns="columns"
:data="tableData"
:loading="loading"
:toolbar-buttons="toolbarButtons"
:row-buttons="rowButtons"
:pagination="pagination"
auto-height
@page-change="onPageChange"
@selection-change="onSelect"
/>
<page-dialog-form
ref="dialogForm"
:visible.sync="dialogVisible"
:title="dialogTitle"
width="40%"
:form-cols="formCols"
:form-data="formData"
:rules="rules"
label-width="130px"
:submitting="submitting"
:confirm-text="key('confirm')"
:cancel-text="key('cancel')"
@submit="onDialogSubmit"
@close="onDialogClose"
/>
</d2-container>
</template>
<script>
import { useTableColumns } from '@/composables/useTableColumns'
import { useTableButtons } from '@/composables/useTableButtons'
import { i18nMixin } from '@/composables/useI18n'
import { confirmMixin } from '@/composables/useConfirmHandle'
import PageTable from '@/components/page-table'
import PageDialogForm from '@/components/page-dialog-form'
import { getList, createItem, editItem, deleteItem, createExportTask } from '@/api/equipment-management/replacement-logs'
export default {
name: 'equipment-management-replacement-logs',
components: { PageTable, PageDialogForm },
mixins: [i18nMixin('page.equipment_management.consumables_management.replacement_logs'), confirmMixin],
data () {
return {
loading: false,
submitting: false,
tableData: [],
selectedRows: [],
dialogVisible: false,
dialogTitle: '',
editId: '',
handleType: 'create',
search: {
keyword: ''
},
pagination: { current: 1, size: 10, total: 0 },
formData: {
device_consumables_item_id: '',
device_id: '',
replace_user: '',
replace_time: '',
replace_reason: '',
replace_duration: '',
remark: ''
},
rules: {
device_consumables_item_id: [{ required: true, message: this.key('enter_consumables_item_id'), trigger: 'blur' }],
device_id: [{ required: true, message: this.key('enter_device_id'), trigger: 'blur' }]
},
columns: [],
toolbarButtons: [],
rowButtons: [],
formCols: [
[
{
type: 'input',
prop: 'device_consumables_item_id',
label: this.key('consumables_item_id'),
placeholder: this.key('enter_consumables_item_id'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'device_id',
label: this.key('device_id'),
placeholder: this.key('enter_device_id'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'replace_user',
label: this.key('replace_user'),
placeholder: this.key('enter_replace_user'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'replace_time',
label: this.key('replace_time'),
placeholder: this.key('enter_replace_time'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'replace_reason',
label: this.key('replace_reason'),
placeholder: this.key('enter_replace_reason'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'replace_duration',
label: this.key('replace_duration'),
placeholder: this.key('enter_replace_duration'),
clearable: true,
style: { width: '90%' }
}
],
[
{
type: 'input',
prop: 'remark',
label: this.key('remark'),
placeholder: this.key('enter_remark'),
inputType: 'textarea',
autosize: { minRows: 2, maxRows: 6 },
clearable: true,
style: { width: '90%' }
}
]
]
}
},
created () {
this.columns = useTableColumns([
{ prop: 'device_consumables_item_name', label: this.key('consumables_item'), minWidth: 140 },
{ prop: 'device_name', label: this.key('device_name'), minWidth: 140 },
{ prop: 'replace_user', label: this.key('replace_user'), minWidth: 140 },
{ prop: 'replace_time', label: this.key('replace_time'), minWidth: 140 },
{ prop: 'replace_reason', label: this.key('replace_reason'), minWidth: 140 },
{ prop: 'replace_duration', label: this.key('replace_duration'), minWidth: 140 },
{ prop: '_actions', label: this.key('operation'), width: 170, fixed: 'right' }
])
const btns = useTableButtons({
toolbar: [
{ key: 'add', label: this.key('add'), icon: 'el-icon-plus', type: 'primary', auth: '/device_management/device_consumables/device_consumables_replace_log/create', onClick: this.openAdd },
{ key: 'export', label: this.key('export'), icon: 'el-icon-download', auth: '/device_management/device_consumables/device_consumables_replace_log/export', onClick: this.handleExport }
],
row: [
{ key: 'edit', label: this.key('edit'), icon: 'el-icon-edit', auth: '/device_management/device_consumables/device_consumables_replace_log/edit', onClick: this.openEdit },
{ key: 'delete', label: this.key('delete'), icon: 'el-icon-delete', color: 'danger', auth: '/device_management/device_consumables/device_consumables_replace_log/delete', onClick: this.handleDelete }
]
}, this.$permission)
this.toolbarButtons = btns.toolbarButtons
this.rowButtons = btns.rowButtons
this.fetchData()
},
methods: {
normalizeResponse (res) {
const data = res && res.data !== undefined ? res.data : res
if (Array.isArray(data)) return { list: data, total: data.length }
if (data && Array.isArray(data.data)) return { list: data.data, total: Number(data.count || data.total || data.data.length) }
if (data && data.data && Array.isArray(data.data.data)) return { list: data.data.data, total: Number(data.data.count || data.data.total || data.data.data.length) }
return { list: [], total: 0 }
},
async fetchData () {
this.loading = true
try {
const res = await getList({ ...this.search, page_no: this.pagination.current, page_size: this.pagination.size })
const data = this.normalizeResponse(res)
this.tableData = data.list
this.pagination.total = data.total
} finally {
this.loading = false
}
},
onSearch () {
this.pagination.current = 1
this.fetchData()
},
onReset () {
this.search = {
keyword: ''
}
this.pagination.current = 1
this.fetchData()
},
onPageChange (page) {
this.pagination.current = page.current
this.pagination.size = page.size
this.fetchData()
},
onSelect (rows) {
this.selectedRows = rows
},
resetForm () {
this.formData = {
device_consumables_item_id: '',
device_id: '',
replace_user: '',
replace_time: '',
replace_reason: '',
replace_duration: '',
remark: ''
}
this.editId = ''
},
openAdd () {
this.handleType = 'create'
this.dialogTitle = this.key('add_title')
this.$nextTick(() => {
this.$refs.dialogForm && this.$refs.dialogForm.reset()
this.resetForm()
this.dialogVisible = true
})
},
openEdit (row) {
this.handleType = 'edit'
this.dialogTitle = this.key('edit_title')
this.editId = row.id
this.formData = {
device_consumables_item_id: row.device_consumables_item_id || '',
device_id: row.device_id || '',
replace_user: row.replace_user || '',
replace_time: row.replace_time || '',
replace_reason: row.replace_reason || '',
replace_duration: row.replace_duration || '',
remark: row.remark || ''
}
this.dialogVisible = true
},
async onDialogSubmit () {
this.submitting = true
try {
if (this.handleType === 'create') await createItem(this.formData)
else await editItem({ ...this.formData, id: this.editId })
this.$message.success(this.$t(this.key('operation_success')))
this.dialogVisible = false
this.fetchData()
} finally {
this.submitting = false
}
},
onDialogClose () {
this.resetForm()
},
async handleDelete (row) {
const cancelled = await this.$confirmAction(
{ message: this.key('confirm_delete'), title: this.key('tip') },
() => deleteItem({ id: [row.id] })
)
if (cancelled) return
this.$message.success(this.$t(this.key('operation_success')))
this.pagination.current = Math.min(this.pagination.current, Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1)
this.fetchData()
},
async handleExport () {
const cancelled = await this.$confirmAction(
{ message: this.key('confirm_export'), title: this.key('tip') },
() => createExportTask({ ...this.search })
)
if (cancelled) return
this.$message.success(this.$t(this.key('operation_success')))
}
}
}
</script>
<style scoped>
.search-bar { padding: 10px 0; }
/deep/ .el-form-item--mini.el-form-item { margin-bottom: 4px; }
</style>

Some files were not shown because too many files have changed in this diff Show More