feat(production-master-data): 新增生产主数据模块下物料与工序相关功能
1. 新增物料单位、物料类别、物料信息管理的API与页面 2. 新增工序单元管理的API、页面与弹窗组件 3. 新增可选参数管理组件与相关API 4. 补充对应国际化多语言配置 5. 新增生产主数据模块路由配置 6. 新增计量单位功能测试流程文档
This commit is contained in:
153
docs/功能测试流程-计量单位.md
Normal file
153
docs/功能测试流程-计量单位.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# 功能测试流程文档 ——【计量单位】
|
||||
|
||||
> 本文档为【计量单位】功能迁移后的独立测试文档。测试人员请按以下流程逐步执行,对通过的用例在"通过"列打勾(`[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 个物料已引用本表(用于验证删除联动提示) |
|
||||
| 浏览器工具 | 打开 DevTools(F12)→ 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 | | | | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 六、测试结论
|
||||
|
||||
| 项目 | 结论 |
|
||||
| --- | --- |
|
||||
| 功能完整性 | ☐ 满足 ☐ 部分缺失 ☐ 不满足 |
|
||||
| 性能表现 | ☐ 良好 ☐ 一般 ☐ 差 |
|
||||
| 权限控制 | ☐ 正确 ☐ 存在漏洞 |
|
||||
| 国际化 | ☐ 完整 ☐ 部分缺失 ☐ 缺失 |
|
||||
| 是否可发布 | ☐ 是 ☐ 否(请说明阻塞问题) |
|
||||
|
||||
测试人员签字:__________________ 日期:__________
|
||||
|
||||
---
|
||||
*本测试流程文档为【计量单位】功能迁移版本专用,请独立归档保存。*
|
||||
549
docs/功能测试流程文档.md
549
docs/功能测试流程文档.md
@@ -2847,7 +2847,552 @@
|
||||
| 产品列表 | 37 | | | |
|
||||
| 工艺流程类别 | 36 | | | |
|
||||
| 产线设置 | 30 | | | |
|
||||
| **合计** | **256** | | | |
|
||||
| 工序单元 | 36 | | | |
|
||||
| **合计** | **292** | | | |
|
||||
|
||||
---
|
||||
|
||||
# 十、工序单元功能测试
|
||||
|
||||
> **迁移日期**:2026-06-01
|
||||
> **对应页面**:`src/views/production-master-data/process-model/process-step/index.vue`
|
||||
> **API 文件**:`src/api/production-master-data/process-step.js`
|
||||
> **路由路径**:`/production_configuration/technology_model/technology_flow_workingsubclass`
|
||||
> **页面结构**:搜索区(工序单元编码 + 工序单元名称 + 查询/重置按钮)+ 数据表格 + 新增/编辑弹框(含设备类别下拉选择)
|
||||
|
||||
## 10.1 页面加载与数据展示
|
||||
|
||||
### TC-STEP-001:工序单元页面正常加载
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 登录系统 2. 点击菜单「生产配置 → 工艺模型 → 工序单元」 |
|
||||
| **预期结果** | 页面正常加载,显示工序单元列表表格,包含序号、工序单元编码、工序单元名称、设备类别、备注、操作列;底部分页组件显示总条数 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-002:表格列完整显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 观察表格列头 2. 横向滚动查看所有列 |
|
||||
| **预期结果** | 表格包含以下列:序号、工序单元编码、工序单元名称、设备类别、备注、操作(共 6 列),操作列固定在右侧 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-003:空数据状态展示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试前置条件** | 确保数据库中没有工序单元数据(或清空搜索条件后无匹配数据) |
|
||||
| **测试步骤** | 1. 在搜索框中输入一个不存在的工序单元编码 2. 点击「查询」 |
|
||||
| **预期结果** | 表格显示空状态提示(如"暂无数据"),分页显示总数为 0 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-004:表格序号正确递增
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 加载工序单元列表 2. 观察序号列 |
|
||||
| **预期结果** | 序号从 1 开始递增,翻页后序号接续正确 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
## 10.2 搜索与分页
|
||||
|
||||
### TC-STEP-005:按工序单元编码搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在工序单元编码输入框中输入已知存在的编码 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格仅显示编码匹配的记录 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-006:按工序单元名称搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在工序单元名称输入框中输入已知存在的名称 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格仅显示名称匹配的记录 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-007:组合搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 同时填写工序单元编码和名称搜索条件 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格显示同时满足编码和名称条件的记录 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-008:重置搜索条件
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 输入搜索条件并查询 2. 点击「重置」按钮 |
|
||||
| **预期结果** | 搜索条件清空,表格恢复显示全部数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-009:分页功能
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换每页显示条数 2. 点击下一页/上一页 3. 输入页码跳转 |
|
||||
| **预期结果** | 分页切换正常,表格数据按分页加载,总条数正确显示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
## 10.3 新增工序单元
|
||||
|
||||
### TC-STEP-010:打开新增弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 点击表格上方工具栏「新增」按钮 |
|
||||
| **预期结果** | 弹出新增工序单元弹框,标题显示"新增工序单元",表单字段为空:工序单元编码(输入框)、工序单元名称(输入框)、设备类别(下拉选择)、备注(多行文本域) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-011:新增工序单元成功
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 输入工序单元编码(如"PS-001")3. 输入工序单元名称(如"测试工序单元001")4. 选择设备类别 5. 输入备注(如"用于测试")6. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,提示"操作成功",表格刷新并显示新增的工序单元 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-012:新增工序单元—仅填写必填项
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 填写编码、名称,选择设备类别 3. 备注留空 4. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,提示"操作成功",备注字段在表格中显示为空 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-013:新增工序单元—设备类别下拉选择
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 点击设备类别下拉框 |
|
||||
| **预期结果** | 下拉列表显示所有设备类别选项,可搜索过滤 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-014:取消新增
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 填写部分内容 3. 点击「取消」按钮或弹框右上角 X |
|
||||
| **预期结果** | 弹框关闭,表格数据不变 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-015:连续新增
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 新增工序单元 A 成功后 2. 再次点击「新增」按钮 3. 新增工序单元 B |
|
||||
| **预期结果** | 弹框表单每次打开时均为空状态,上次填写的内容不残留;两次新增均成功 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
## 10.4 编辑工序单元
|
||||
|
||||
### TC-STEP-016:打开编辑弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 点击工序单元列表中任一工序单元行操作列的「编辑」按钮 |
|
||||
| **预期结果** | 弹出编辑工序单元弹框,标题显示"编辑工序单元",表单回填该工序单元的编码、名称、设备类别、备注 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-017:编辑工序单元—修改名称
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 修改工序单元名称(如改为"测试工序单元-已修改")3. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,提示"操作成功",表格中该行名称更新 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-018:编辑工序单元—修改设备类别
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 切换选择另一个设备类别 3. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,提示"操作成功",表格中该行设备类别列更新 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-019:编辑工序单元—工序单元编码不可修改
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 查看工序单元编码输入框 |
|
||||
| **预期结果** | 工序单元编码字段显示为禁用状态(灰色不可编辑) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-020:取消编辑
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 修改内容 3. 点击「取消」按钮 |
|
||||
| **预期结果** | 弹框关闭,原数据不变 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
## 10.5 删除工序单元
|
||||
|
||||
### TC-STEP-021:删除确认弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 点击工序单元列表中任一工序单元行操作列的「删除」按钮 |
|
||||
| **预期结果** | 弹出确认提示框,内容为"确定要执行该操作吗?",有「确定」和「取消」按钮 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-022:删除工序单元成功
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 点击工序单元行的「删除」按钮 2. 在确认框中点击「确定」 |
|
||||
| **预期结果** | 提示"操作成功",该工序单元从列表中消失,分页总数减 1 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-023:取消删除
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 点击「删除」按钮 2. 在确认框中点击「取消」 |
|
||||
| **预期结果** | 确认框关闭,工序单元数据保持不变 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-024:删除最后一页唯一数据后的分页处理
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试前置条件** | 存在数据,且最后一页只有一条记录 |
|
||||
| **测试步骤** | 1. 翻到最后一页 2. 删除该页唯一的工序单元 |
|
||||
| **预期结果** | 提示"操作成功",自动跳转到前一页,分页总数减 1 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
## 10.6 表单校验
|
||||
|
||||
### TC-STEP-025:新增表单校验—编码必填
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 不填写工序单元编码 3. 填写其他必填项 4. 点击「确定」 |
|
||||
| **预期结果** | 编码输入框下方显示红色校验提示,弹框不关闭 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-026:新增表单校验—名称必填
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 填写编码 3. 不填写工序单元名称 4. 选择设备类别 5. 点击「确定」 |
|
||||
| **预期结果** | 名称输入框下方显示红色校验提示,弹框不关闭 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-027:新增表单校验—设备类别必填
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 填写编码和名称 3. 不选择设备类别 4. 点击「确定」 |
|
||||
| **预期结果** | 设备类别下方显示红色校验提示"请选择设备类别",弹框不关闭 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-028:新增表单校验—编码长度上限
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 在编码输入框中输入超过 100 个字符 3. 点击「确定」 |
|
||||
| **预期结果** | 编码输入框下方显示红色校验提示"长度在 1 到 100 个字符" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-029:新增表单校验—中文字符正常输入
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 工序单元名称输入中文字符 3. 点击「确定」 |
|
||||
| **预期结果** | 表单正常提交,中文内容正确保存 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-030:编辑表单校验—清空必填项
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开已存在工序单元的编辑弹框 2. 清空工序单元名称 3. 点击「确定」 |
|
||||
| **预期结果** | 名称输入框下方显示红色校验提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
## 10.7 多语言切换
|
||||
|
||||
### TC-STEP-031:切换到英文—页面标签
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换到英文语言 2. 点击菜单进入工序单元页面 |
|
||||
| **预期结果** | 搜索区标签显示"Process Unit Code"、"Process Unit Name"、"Search"、"Reset";表格列头显示"No."、"Process Unit Code"、"Process Unit Name"、"Device Category"、"Remark"、"Actions";工具栏按钮显示"Add" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-032:切换到英文—新增弹框标签
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换到英文 2. 点击新增按钮 3. 查看弹框 |
|
||||
| **预期结果** | 弹框标题为"Add Process Unit";表单标签为"Process Unit Code"、"Process Unit Name"、"Device Category"、"Remark";底部按钮为"Confirm"、"Cancel" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-033:切换到英文—编辑弹框标签
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换到英文 2. 点击某行的编辑按钮 |
|
||||
| **预期结果** | 弹框标题为"Edit Process Unit",表单回填正确,所有标签为英文 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-034:切换到英文—提示信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换到英文 2. 执行新增/编辑/删除操作 |
|
||||
| **预期结果** | 操作成功提示为"Operation succeeded";删除确认框内容为"Proceed with this action?";校验提示为英文 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-035:切换到英文—设备类别下拉
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换到英文 2. 打开新增弹框 3. 点击设备类别下拉框 |
|
||||
| **预期结果** | 下拉 placeholder 为"Please select device category" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-036:英文下所有操作功能正常
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 在英文界面下执行:搜索、新增工序单元、编辑工序单元、删除工序单元、分页切换 |
|
||||
| **预期结果** | 所有功能正常运行,交互逻辑与中文界面一致 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-037:预设设定值按钮权限控制
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 登录系统 2. 进入工序单元页面 3. 查看某行数据的操作列 |
|
||||
| **预期结果** | 操作列显示:编辑(黄色)、预设设定值(橙色)、预设结果参数(绿色)、删除(红色)四个按钮 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-038:打开预设设定值弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 进入工序单元页面 2. 点击某行的"预设设定值"按钮 |
|
||||
| **预期结果** | 弹出"预设设定值"对话框,标题正确,显示配置区域占位符和提示信息 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-039:预设设定值弹框关闭
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开预设设定值弹框 2. 点击取消按钮 |
|
||||
| **预期结果** | 弹框关闭,无错误 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-040:预设设定值提交
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开预设设定值弹框 2. 点击确定按钮 |
|
||||
| **预期结果** | 显示"操作成功"提示,弹框关闭 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-041:预设结果参数按钮存在
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 进入工序单元页面,查看操作列按钮 |
|
||||
| **预期结果** | 显示"预设结果参数"按钮(绿色图标) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-042:打开预设结果参数弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 进入工序单元页面 2. 点击某行的"预设结果参数"按钮 |
|
||||
| **预期结果** | 弹出"预设结果参数"对话框,显示可选参数列表表格(名称、参数、类别、备注、是否唯一、是否上传) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-043:预设结果参数搜索功能
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开预设结果参数弹框 2. 输入名称和参数进行搜索 |
|
||||
| **预期结果** | 点击查询按钮后,表格数据根据搜索条件过滤 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-044:新增可选参数
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开预设结果参数弹框 2. 点击"新增一行"按钮 3. 填写参数信息 4. 点击确定 |
|
||||
| **预期结果** | 新增成功,列表刷新显示新数据,弹出成功提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-045:编辑可选参数
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开预设结果参数弹框 2. 点击某行的编辑图标 3. 修改信息 4. 点击确定 |
|
||||
| **预期结果** | 编辑成功,列表刷新显示更新后的数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-046:删除可选参数
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开预设结果参数弹框 2. 点击某行的删除图标 3. 确认删除 |
|
||||
| **预期结果** | 删除成功,列表刷新,该数据不再显示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-047:预设结果参数分页
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开预设结果参数弹框 2. 当数据较多时切换分页 |
|
||||
| **预期结果** | 分页切换正常,显示正确的页码和总条数 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-048:预设结果参数关闭
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开预设结果参数弹框 2. 点击关闭按钮 |
|
||||
| **预期结果** | 弹框正常关闭 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-049:预设结果参数导入功能入口
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开预设结果参数弹框 2. 点击"导入"按钮 |
|
||||
| **预期结果** | 显示"导入功能开发中"提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-STEP-050:预设结果参数英文界面
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换到英文 2. 打开预设结果参数弹框 |
|
||||
| **预期结果** | 弹框标题显示"Preset Result Parameters",表格列头显示英文(Name、Parameter、Category等) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
---
|
||||
|
||||
## 测试结果汇总
|
||||
|
||||
| 功能模块 | 测试用例数 | 通过 | 未通过 | 通过率 |
|
||||
|----------|-----------|------|--------|--------|
|
||||
| 角色管理 | 24 | | | |
|
||||
| 用户管理 | 33 | | | |
|
||||
| 菜单配置 | 37 | | | |
|
||||
| 接口日志 | 20 | | | |
|
||||
| 操作日志 | 18 | | | |
|
||||
| 问题帮助 | 21 | | | |
|
||||
| 产品列表 | 37 | | | |
|
||||
| 工艺流程类别 | 36 | | | |
|
||||
| 产线设置 | 30 | | | |
|
||||
| 工序单元 | 50 | | | |
|
||||
| **合计** | **306** | | | |
|
||||
|
||||
---
|
||||
|
||||
@@ -2861,3 +3406,5 @@
|
||||
> - v1.6 (2026-06-01):追加产品列表功能(37 条用例)
|
||||
> - v1.7 (2026-06-01):追加工艺流程类别功能(36 条用例)
|
||||
> - v1.8 (2026-06-01):追加产线设置功能(30 条用例)
|
||||
> - v1.9 (2026-06-01):追加工序单元功能(36 条用例)
|
||||
> - v1.10 (2026-06-01):追加工序单元预设设定值和预设结果参数功能(14 条用例)
|
||||
|
||||
359
docs/迁移功能测试流程.md
Normal file
359
docs/迁移功能测试流程.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# MES-UI 迁移功能测试流程文档(累积追加版)
|
||||
|
||||
> 本文档用于记录每次 MES-UI 迁移完成后的功能测试流程。
|
||||
> 采用**累积追加**模式:每次完成新功能迁移,将对应测试内容追加到本文件末尾,并在「目录」中补充章节索引。
|
||||
> 测试人员按章节顺序执行用例,每完成一项需在「[ ]」中改为「[x]」并填写实际结果;对未通过项在「问题记录」区域详细描述。
|
||||
>
|
||||
> **测试结果约定**:
|
||||
> - ✅ 通过(Pass)
|
||||
> - ❌ 失败(Fail)
|
||||
> - ⚠️ 阻塞(Block,依赖其他功能未通过)
|
||||
>
|
||||
> **填写示例**:
|
||||
> - `[x] 1.1 列表正常加载 ✅ 实际:10 条记录,分页正常`
|
||||
> - `[ ] 1.2 编码为空校验 ❌ 实际:未拦截空值,提示词缺失`
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [一、测试环境与通用前置条件](#一测试环境与通用前置条件)
|
||||
- [二、物料类别列表(Material Category)](#二物料类别列表material-category)
|
||||
- [三、物料信息管理(Material Master)](#三物料信息管理material-master)
|
||||
|
||||
---
|
||||
|
||||
## 一、测试环境与通用前置条件
|
||||
|
||||
> 所有功能测试前,请先确认以下环境和基础数据准备就绪。
|
||||
|
||||
### 1.1 测试环境
|
||||
|
||||
| 项目 | 要求 |
|
||||
|------|------|
|
||||
| 操作系统 | Windows 10 / 11 或 macOS |
|
||||
| 浏览器 | Chrome ≥ 100(推荐)、Edge ≥ 100 |
|
||||
| Node.js | ≥ 16.0(用于本地构建) |
|
||||
| MES-UI 仓库 | `d:\code\mes\mes-ui`,已安装依赖 `pnpm install` |
|
||||
| 后端服务 | Webman 后台已启动(地址、端口按部署环境),数据库表 `material_category` 已存在 |
|
||||
| 启动命令 | `pnpm dev` 或 `npm run serve`,默认 `http://localhost:8080` |
|
||||
| 登录账号 | 具备「生产配置」菜单权限的账号,建议使用 `admin` 角色 |
|
||||
| 网络 | 能正常访问后端 API |
|
||||
|
||||
### 1.2 通用前置条件
|
||||
|
||||
1. 已完成 `pnpm install`,依赖安装无错误。
|
||||
2. `pnpm dev` 启动成功,浏览器可访问 `http://localhost:8080`。
|
||||
3. 使用具有「生产配置 / 物料模型 / 物料类别列表」访问权限的账号登录。
|
||||
4. 后端数据库中 `material_category` 表至少有 1 条数据,用于列表展示和编辑。
|
||||
5. 中英文语言切换正常:在右上角下拉框切换「中文 / English」,所有标签翻译均能显示。
|
||||
6. 浏览器 DevTools 的 Network 面板可见请求 URL 类似 `production_configuration/matetial_model/matetial_category/list?...`(迁移后保持旧 URL)。
|
||||
|
||||
### 1.3 测试结果总览(按功能)
|
||||
|
||||
| 功能 | 章节 | 用例数 | 通过 | 失败 | 阻塞 | 测试人 / 日期 |
|
||||
|------|------|:----:|:----:|:----:|:----:|---------------|
|
||||
| 物料类别列表 | 二 | 22 | | | | |
|
||||
| 物料信息管理 | 三 | 30 | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 二、物料类别列表(Material Category)
|
||||
|
||||
### 2.1 功能概述
|
||||
|
||||
- **一级模块**:生产配置(Production Master Data)
|
||||
- **二级模块**:物料模型(Material Model)
|
||||
- **三级模块**:物料类别列表(Material Category)
|
||||
- **功能说明**:用于维护物料类别(区分原材料和半成品),支持编码、名称、备注、创建时间等字段的增删改查
|
||||
- **菜单路径**:生产配置 → 物料模型 → 物料类别列表
|
||||
- **路由地址**:`/production_configuration/matetial_model/matetial_category`(迁移阶段沿用旧 URL)
|
||||
- **涉及文件**:
|
||||
- 页面:[src/views/production-master-data/material-model/material-category/index.vue](file:///d:/code/mes/mes-ui/src/views/production-master-data/material-model/material-category/index.vue)
|
||||
- API:[src/api/production-master-data/material-category.js](file:///d:/code/mes/mes-ui/src/api/production-master-data/material-category.js)
|
||||
- 路由:[src/router/modules/production-master-data.js](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js)
|
||||
- i18n:`src/locales/zh-chs.json` & `en.json`(key 前缀 `page.production_master_data.material_model.material_category.*`)
|
||||
|
||||
### 2.2 字段说明
|
||||
|
||||
| 字段 | 类型 | 必填 | 长度限制 | 说明 |
|
||||
|------|------|:----:|---------|------|
|
||||
| 序号 | 数字 | — | — | 表格自增序号,由 `useTableColumns` 自动生成 |
|
||||
| 物料类别编码 | 字符串 | ✅ | 1~45 | 唯一标识 |
|
||||
| 物料类别名称 | 字符串 | ✅ | 1~45 | 类别中文名 |
|
||||
| 备注 | 字符串 | ❌ | — | 多行文本 |
|
||||
| 创建时间 | 日期 | — | — | 后端自动填充 |
|
||||
| 操作 | — | — | — | 行内「编辑」「删除」按钮 |
|
||||
|
||||
### 2.3 前置条件
|
||||
|
||||
1. 已登录具备「生产配置 / 物料模型 / 物料类别列表」权限的账号。
|
||||
2. 后端 `material_category` 表存在;若为空,可通过本测试用例的「新增」步骤补足。
|
||||
3. 浏览器地址栏输入 `/production_configuration/matetial_model/matetial_category` 可直接访问。
|
||||
4. 当前页面右上角语言切换为「中文」;切换为「English」后再次验证关键标签。
|
||||
|
||||
### 2.4 测试用例
|
||||
|
||||
#### 2.4.1 列表与搜索
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 1.1 | 打开「物料类别列表」页面 | 表格自动加载,列表正常显示;表头依次为:序号 / 物料类别编码 / 物料类别名称 / 备注 / 创建时间 / 操作 | | ☐ |
|
||||
| 1.2 | 在「物料类别编码」输入框输入已存在的关键字,点击「查询」 | 列表仅显示编码模糊匹配的数据,页码回到 1;URL 不暴露搜索值 | | ☐ |
|
||||
| 1.3 | 在「物料类别名称」输入框输入已存在的关键字,点击「查询」 | 列表仅显示名称模糊匹配的数据 | | ☐ |
|
||||
| 1.4 | 同时输入编码 + 名称后点击「查询」 | 列表为 AND 条件过滤结果 | | ☐ |
|
||||
| 1.5 | 在搜索框内按 `Enter` 键 | 触发查询,效果与点击「查询」一致 | | ☐ |
|
||||
| 1.6 | 点击「重置」按钮 | 搜索框清空,列表恢复初始数据,页码回到 1 | | ☐ |
|
||||
| 1.7 | 翻到第 2 页,点击「重置」 | 页码回到 1,列表刷新 | | ☐ |
|
||||
| 1.8 | 列表行数超过 1 页时,切换「每页显示条数」 | 列表按新分页重新加载,URL / 请求参数同步变化 | | ☐ |
|
||||
| 1.9 | 切换右上角语言为「English」 | 表头列名翻译正确:No. / Category Code / Category Name / Remark / Create Time / Actions | | ☐ |
|
||||
| 1.10 | 后端返回空数据 | 表格区域显示空数据占位,分页总数为 0 | | ☐ |
|
||||
|
||||
#### 2.4.2 新增物料类别
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 2.1 | 点击工具栏「新增」按钮 | 弹出「新增物料类别」对话框,含编码 / 名称 / 备注三个表单项,编码默认聚焦 | | ☐ |
|
||||
| 2.2 | 编码留空,名称输入「测试类别 A」,备注输入「原材料」,点击「确定」 | 弹框内编码字段红色错误提示「请输入物料类别编码」;不会提交成功 | | ☐ |
|
||||
| 2.3 | 编码输入「TEST_001」,名称留空,点击「确定」 | 名称字段红色错误提示「请输入物料类别名称」 | | ☐ |
|
||||
| 2.4 | 编码输入 46 个字符(如 46 个 `a`),名称输入「测试类别 A」,点击「确定」 | 编码字段提示「长度在 1 到 45 个字符」 | | ☐ |
|
||||
| 2.5 | 名称输入 46 个字符,编码输入「TEST_002」,点击「确定」 | 名称字段提示「长度在 1 到 45 个字符」 | | ☐ |
|
||||
| 2.6 | 编码输入已存在的编码(如数据库现存 `RAW_001`),名称输入「重复测试」,点击「确定」 | 提交失败,弹出后端返回的错误提示(如「编码已存在」);列表不刷新 | | ☐ |
|
||||
| 2.7 | 编码输入「TEST_001」,名称输入「测试类别 A」,备注输入「用于半成品分类」,点击「确定」 | 提交成功,弹框关闭,顶部提示「操作成功」;列表新增一行 | | ☐ |
|
||||
| 2.8 | 在弹框中点击「取消」 | 弹框关闭,不提交任何数据 | | ☐ |
|
||||
| 2.9 | 打开新增弹框后点击右上角 × 关闭 | 弹框关闭,再次打开时表单已重置 | | ☐ |
|
||||
| 2.10 | 重复 2.7 步骤的编码「TEST_001」再次提交 | 后端返回「编码已存在」或类似错误,不重复创建 | | ☐ |
|
||||
|
||||
#### 2.4.3 编辑物料类别
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 3.1 | 在列表中找到「测试类别 A」(2.7 创建),点击行内「编辑」 | 弹出「编辑物料类别」对话框,编码 / 名称 / 备注自动回填 | | ☐ |
|
||||
| 3.2 | 将名称修改为「测试类别 A1」,备注改为「原材料-已更新」,点击「确定」 | 提交成功,弹框关闭,提示「操作成功」;列表行名称 / 备注同步更新 | | ☐ |
|
||||
| 3.3 | 编辑时清空编码并保存 | 编码字段必填提示「请输入物料类别编码」 | | ☐ |
|
||||
| 3.4 | 编辑时清空名称并保存 | 名称字段必填提示「请输入物料类别名称」 | | ☐ |
|
||||
| 3.5 | 编辑时将编码修改为与另一行相同的值(如 `RAW_001`),点击「确定」 | 后端返回「编码已存在」错误 | | ☐ |
|
||||
| 3.6 | 编辑弹框打开后点击「取消」 | 数据不保存,列表无变化 | | ☐ |
|
||||
| 3.7 | 编辑后重新打开编辑弹框 | 表单仍展示上一次保存后的数据 | | ☐ |
|
||||
| 3.8 | 编辑成功后检查「创建时间」字段 | 创建时间未变化(仅首次新增时赋值) | | ☐ |
|
||||
|
||||
#### 2.4.4 删除物料类别
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 4.1 | 在列表中找到「测试类别 A1」,点击行内「删除」 | 弹出确认框「确定要执行该操作吗?」,标题为「提示」 | | ☐ |
|
||||
| 4.2 | 在确认框中点击「取消」 | 删除取消,列表无变化 | | ☐ |
|
||||
| 4.3 | 在确认框中点击「确定」 | 确认框关闭,顶部提示「操作成功」;该行从列表中消失 | | ☐ |
|
||||
| 4.4 | 在仅剩 1 条数据的列表上点击「删除」并确认 | 删除成功后,列表变为空,分页回到第 1 页 | | ☐ |
|
||||
| 4.5 | 尝试删除仍被「物料信息」引用的物料类别 | 后端返回「存在引用,无法删除」或类似错误 | | ☐ |
|
||||
| 4.6 | 删除操作期间切换「每页显示条数」 | 删除后页码自动修正,避免出现空页 | | ☐ |
|
||||
|
||||
#### 2.4.5 权限与异常场景
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 5.1 | 使用无「新增」权限的账号登录 | 工具栏「新增」按钮不显示或不可点击 | | ☐ |
|
||||
| 5.2 | 使用无「编辑」权限的账号登录 | 行内「编辑」按钮不显示 | | ☐ |
|
||||
| 5.3 | 使用无「删除」权限的账号登录 | 行内「删除」按钮不显示 | | ☐ |
|
||||
| 5.4 | 断网后刷新页面 | 表格显示加载失败或空态,DevTools Network 显示请求失败 | | ☐ |
|
||||
| 5.5 | 后端返回 500 错误(如临时停服) | 顶部红色错误提示(如「操作失败」),不卡死页面 | | ☐ |
|
||||
| 5.6 | 同时打开两个浏览器标签页,在 A 标签新增数据后,B 标签刷新列表 | B 标签列表应能看到 A 标签新增的数据 | | ☐ |
|
||||
|
||||
#### 2.4.6 国际化与界面
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 6.1 | 中文状态下打开新增弹框 | 弹框标题、字段标签、按钮文字均为中文 | | ☐ |
|
||||
| 6.2 | 切换为「English」后再次打开新增弹框 | 弹框标题「Add Material Category」,按钮「Confirm / Cancel」 | | ☐ |
|
||||
| 6.3 | 中文状态下点击删除,确认弹框标题为「提示」 | 确认框标题与按钮文字中文显示 | | ☐ |
|
||||
| 6.4 | 英文状态下点击删除,确认弹框标题为「Tip」 | 确认框英文显示 | | ☐ |
|
||||
| 6.5 | 列表右上角「帮助」按钮(? 图标) | 鼠标悬停或点击显示「物料类别用于区分原材料和半成品」 | | ☐ |
|
||||
| 6.6 | 备注字段为空时点击保存 | 提交成功,列表中备注列空白(无 `null` / `undefined` 文本) | | ☐ |
|
||||
|
||||
### 2.5 接口契约(用于前后端联调核对)
|
||||
|
||||
| 方法 | URL | 旧 method 名 | 入参关键 key | 备注 |
|
||||
|------|-----|-------------|-------------|------|
|
||||
| GET | `production_configuration/matetial_model/matetial_category/list` | `production_configuration_matetial_model_matetial_category_list` | `code / name / page_no / page_size` | 列表查询 |
|
||||
| GET | `…/all` | `…_all` | — | 全部类别(下拉框用,可选) |
|
||||
| POST | `…/create` | `…_create` | `code / name / remark` | 新增 |
|
||||
| PUT | `…/edit` | `…_edit` | `id / code / name / remark` | 编辑(`id` 为行主键) |
|
||||
| DELETE | `…/delete` | `…_delete` | `id: [rowId]` | 删除,参数名 `id` 数组(与旧项目保持一致) |
|
||||
|
||||
### 2.6 问题记录
|
||||
|
||||
> 测试人员发现问题时填写。每条问题一行,格式:`[编号] [用例编号] [现象] [复现步骤] [期望] [截图/日志] [负责人] [处理状态]`。
|
||||
|
||||
| 编号 | 用例 | 现象 | 复现步骤 | 期望 | 截图/日志 | 负责人 | 状态 |
|
||||
|:---:|:---:|------|---------|------|----------|:----:|:----:|
|
||||
| | | | | | | | |
|
||||
|
||||
### 2.7 备注
|
||||
|
||||
- 本页面沿用旧 URL 与后台 method 名,待后台数据库路由表统一更新后统一修改。
|
||||
- 涉及 `confirmMixin` / `useTableColumns` / `useTableButtons` / `i18nMixin` 等公共 Composable,如其他页面出现类似问题,先确认是否在迁移组件时有遗漏。
|
||||
- 若新增 / 编辑成功后接口返回结构变化(`res.data` 包裹层调整),需要同步更新 `fetchData` 的解析逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 三、物料信息管理(Material Master)
|
||||
|
||||
### 3.1 功能概述
|
||||
|
||||
- **一级模块**:生产配置(Production Master Data)
|
||||
- **二级模块**:物料模型(Material Model)
|
||||
- **三级模块**:物料信息管理(Material Master)
|
||||
- **功能说明**:用于维护物料主数据,包括物料编码、名称、所属物料类别、单位、备注等,支持新增、编辑、删除、批量删除、按编码/名称/类别检索。
|
||||
- **菜单路径**:生产配置 → 物料模型 → 物料信息管理
|
||||
- **路由地址**:`/production_configuration/matetial_model/matetial_management`(迁移阶段沿用旧 URL)
|
||||
- **涉及文件**:
|
||||
- 页面:[src/views/production-master-data/material-model/material-master/index.vue](file:///d:/code/mes/mes-ui/src/views/production-master-data/material-model/material-master/index.vue)
|
||||
- API:[src/api/production-master-data/material-master.js](file:///d:/code/mes/mes-ui/src/api/production-master-data/material-master.js)
|
||||
- 路由:[src/router/modules/production-master-data.js](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js)
|
||||
- i18n:`src/locales/zh-chs.json` & `en.json`(key 前缀 `page.production_master_data.material_model.material_master.*`)
|
||||
|
||||
### 3.2 字段说明
|
||||
|
||||
| 字段 | 类型 | 必填 | 长度限制 | 说明 |
|
||||
|------|------|:----:|---------|------|
|
||||
| 序号 | 数字 | — | — | 表格自增序号 |
|
||||
| 物料编码 | 字符串 | ✅ | 1~100 | 唯一标识 |
|
||||
| 物料名称 | 字符串 | ✅ | 1~100 | 物料中文名 |
|
||||
| 物料类别 | 下拉 | ✅ | — | 来源于物料类别下拉(依赖「物料类别列表」已迁移) |
|
||||
| 单位 | 输入/下拉 | ❌ | — | 当前版本以文本框占位;待「计量单位」模块迁移后接入下拉数据源 |
|
||||
| 备注 | 字符串 | ❌ | — | 多行文本,超过 20 字符鼠标悬停展示全量 |
|
||||
| 创建人 | 字符串 | — | — | 后端返回 |
|
||||
| 创建时间 | 日期 | — | — | 后端自动填充 |
|
||||
| 操作 | — | — | — | 行内「编辑」「删除」按钮 |
|
||||
|
||||
### 3.3 前置条件
|
||||
|
||||
1. 已登录具备「生产配置 / 物料模型 / 物料信息管理」权限的账号。
|
||||
2. 已完成「物料类别列表」迁移,「物料类别」下拉数据正常加载(用于新增/编辑时的类别选择)。
|
||||
3. 后端 `material` 表存在;若为空,可通过本测试用例的「新增」步骤补足。
|
||||
4. 浏览器地址栏输入 `/production_configuration/matetial_model/matetial_management` 可直接访问。
|
||||
5. 当前页面右上角语言切换为「中文」;切换为「English」后再次验证关键标签。
|
||||
|
||||
### 3.4 测试用例
|
||||
|
||||
#### 3.4.1 列表与搜索
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 1.1 | 打开「物料信息管理」页面 | 表格自动加载,列表正常显示;表头依次为:序号 / 物料编码 / 物料名称 / 物料类别 / 单位 / 备注 / 创建人 / 创建时间 / 操作 | | ☐ |
|
||||
| 1.2 | 物料类别下拉可正常展开,选项来源于物料类别数据 | 下拉选项与「物料类别列表」一致,支持搜索过滤 | | ☐ |
|
||||
| 1.3 | 在「物料编码」输入框输入已存在的关键字,点击「查询」 | 列表仅显示编码模糊匹配的数据,页码回到 1 | | ☐ |
|
||||
| 1.4 | 在「物料名称」输入框输入已存在的关键字,点击「查询」 | 列表仅显示名称模糊匹配的数据 | | ☐ |
|
||||
| 1.5 | 在「物料类别」下拉中选择某一项,点击「查询」 | 列表仅显示该类别下的物料 | | ☐ |
|
||||
| 1.6 | 同时输入编码 + 名称 + 类别后点击「查询」 | 列表为 AND 条件过滤结果 | | ☐ |
|
||||
| 1.7 | 在搜索框内按 `Enter` 键 | 触发查询,效果与点击「查询」一致 | | ☐ |
|
||||
| 1.8 | 点击「重置」按钮 | 搜索框清空,列表恢复初始数据,页码回到 1 | | ☐ |
|
||||
| 1.9 | 翻到第 2 页,点击「重置」 | 页码回到 1,列表刷新 | | ☐ |
|
||||
| 1.10 | 列表行数超过 1 页时,切换「每页显示条数」 | 列表按新分页重新加载,请求参数 `page_size` 同步变化 | | ☐ |
|
||||
| 1.11 | 切换右上角语言为「English」 | 表头列名翻译正确:No. / Material Code / Material Name / Category / Unit / Remark / Created By / Created At / Action | | ☐ |
|
||||
| 1.12 | 备注长度 > 20 字符时,鼠标悬停单元格 | 弹层显示完整备注内容 | | ☐ |
|
||||
| 1.13 | 备注长度 <= 20 字符时 | 单元格直接展示文本,不显示弹层 | | ☐ |
|
||||
| 1.14 | 后端返回空数据 | 表格区域显示空数据占位,分页总数为 0 | | ☐ |
|
||||
|
||||
#### 3.4.2 新增物料信息
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 2.1 | 点击工具栏「新增」按钮 | 弹出「新增物料信息」对话框,依次为:物料编码 / 物料名称 / 物料类别 / 单位 / 备注 | | ☐ |
|
||||
| 2.2 | 编码留空,名称输入「测试物料 A」,类别下拉选择「原材料」,点击「确定」 | 编码字段红色错误提示「请输入物料编码」 | | ☐ |
|
||||
| 2.3 | 编码输入「MAT_001」,名称留空,点击「确定」 | 名称字段红色错误提示「请输入物料名称」 | | ☐ |
|
||||
| 2.4 | 编码、名称都填写,类别留空,点击「确定」 | 类别字段红色错误提示「请选择物料类别」 | | ☐ |
|
||||
| 2.5 | 编码输入 101 个字符(如 101 个 `a`),名称输入「测试物料 A」,点击「确定」 | 编码字段提示「长度在 1 到 100 个字符」 | | ☐ |
|
||||
| 2.6 | 名称输入 101 个字符,编码输入「MAT_002」,点击「确定」 | 名称字段提示「长度在 1 到 100 个字符」 | | ☐ |
|
||||
| 2.7 | 编码输入已存在的编码,名称输入「重复测试」,点击「确定」 | 提交失败,弹出后端返回的错误提示(如「编码已存在」);列表不刷新 | | ☐ |
|
||||
| 2.8 | 编码输入「MAT_001」,名称输入「测试物料 A」,类别选择「原材料」,单位输入「个」,备注输入「用于产线领料」,点击「确定」 | 提交成功,弹框关闭,顶部提示「操作成功」;列表新增一行且所有字段正确展示 | | ☐ |
|
||||
| 2.9 | 在弹框中点击「取消」 | 弹框关闭,不提交任何数据 | | ☐ |
|
||||
| 2.10 | 打开新增弹框后点击右上角 × 关闭,再次点击「新增」 | 弹框再次打开时表单已重置 | | ☐ |
|
||||
| 2.11 | 重复 2.8 步骤的编码「MAT_001」再次提交 | 后端返回「编码已存在」或类似错误,不重复创建 | | ☐ |
|
||||
|
||||
#### 3.4.3 编辑物料信息
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 3.1 | 在列表中找到「MAT_001」(2.8 创建),点击行内「编辑」 | 弹出「编辑物料信息」对话框,编码 / 名称 / 类别 / 单位 / 备注自动回填 | | ☐ |
|
||||
| 3.2 | 将名称修改为「测试物料 A1」,类别改为「半成品」,单位改为「箱」,备注改为「用于产线补料」,点击「确定」 | 提交成功,弹框关闭,提示「操作成功」;列表行各字段同步更新 | | ☐ |
|
||||
| 3.3 | 编辑时清空编码并保存 | 编码字段必填提示「请输入物料编码」 | | ☐ |
|
||||
| 3.4 | 编辑时清空名称并保存 | 名称字段必填提示「请输入物料名称」 | | ☐ |
|
||||
| 3.5 | 编辑时清空类别并保存 | 类别字段必填提示「请选择物料类别」 | | ☐ |
|
||||
| 3.6 | 编辑时将编码修改为与另一行相同的值,点击「确定」 | 后端返回「编码已存在」错误 | | ☐ |
|
||||
| 3.7 | 编辑弹框打开后点击「取消」 | 数据不保存,列表无变化 | | ☐ |
|
||||
| 3.8 | 编辑后重新打开编辑弹框 | 表单仍展示上一次保存后的数据 | | ☐ |
|
||||
| 3.9 | 编辑成功后检查「创建时间」字段 | 创建时间未变化(仅首次新增时赋值) | | ☐ |
|
||||
| 3.10 | 编辑时清空备注并保存 | 提交成功,列表中备注列空白(无 `null` / `undefined` 文本) | | ☐ |
|
||||
|
||||
#### 3.4.4 删除与批量删除
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 4.1 | 在列表中找到「MAT_001」,点击行内「删除」 | 弹出确认框「确定要执行该操作吗?」,标题为「提示」 | | ☐ |
|
||||
| 4.2 | 在确认框中点击「取消」 | 删除取消,列表无变化 | | ☐ |
|
||||
| 4.3 | 在确认框中点击「确定」 | 确认框关闭,顶部提示「操作成功」;该行从列表中消失 | | ☐ |
|
||||
| 4.4 | 在仅剩 1 条数据的列表上点击「删除」并确认 | 删除成功后,列表变为空,分页回到第 1 页 | | ☐ |
|
||||
| 4.5 | 不勾选任何数据,直接点击工具栏「批量删除」 | 顶部红色错误提示「请先选择要删除的数据」 | | ☐ |
|
||||
| 4.6 | 勾选 3 条数据,点击「批量删除」,确认框标题与按钮中文显示 | 弹出确认框「确定要删除所选物料吗?」 | | ☐ |
|
||||
| 4.7 | 在 4.6 弹出的确认框中点击「确定」 | 3 条数据全部删除,顶部提示「操作成功」;列表刷新 | | ☐ |
|
||||
| 4.8 | 尝试删除被其他业务(如 BOM)引用的物料 | 后端返回「存在引用,无法删除」或类似错误 | | ☐ |
|
||||
| 4.9 | 删除操作期间切换「每页显示条数」 | 删除后页码自动修正,避免出现空页 | | ☐ |
|
||||
|
||||
#### 3.4.5 权限与异常场景
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 5.1 | 使用无「新增」权限的账号登录 | 工具栏「新增」按钮不显示或不可点击 | | ☐ |
|
||||
| 5.2 | 使用无「编辑」权限的账号登录 | 行内「编辑」按钮不显示 | | ☐ |
|
||||
| 5.3 | 使用无「删除」权限的账号登录 | 行内「删除」按钮不显示 | | ☐ |
|
||||
| 5.4 | 使用无「批量删除」权限的账号登录 | 工具栏「批量删除」按钮不显示或不可点击 | | ☐ |
|
||||
| 5.5 | 断网后刷新页面 | 表格显示加载失败或空态,DevTools Network 显示请求失败 | | ☐ |
|
||||
| 5.6 | 后端返回 500 错误(如临时停服) | 顶部红色错误提示(如「操作失败」),不卡死页面 | | ☐ |
|
||||
| 5.7 | 物料类别下拉数据加载失败时进入新增弹框 | 类别下拉为空,但仍可正常打开弹框 | | ☐ |
|
||||
| 5.8 | 同时打开两个浏览器标签页,在 A 标签新增数据后,B 标签刷新列表 | B 标签列表应能看到 A 标签新增的数据 | | ☐ |
|
||||
|
||||
#### 3.4.6 国际化与界面
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 6.1 | 中文状态下打开新增弹框 | 弹框标题、字段标签、按钮文字均为中文 | | ☐ |
|
||||
| 6.2 | 切换为「English」后再次打开新增弹框 | 弹框标题「Add Material」,按钮「Confirm / Cancel」 | | ☐ |
|
||||
| 6.3 | 中文状态下点击删除,确认弹框标题为「提示」 | 确认框标题与按钮文字中文显示 | | ☐ |
|
||||
| 6.4 | 英文状态下点击删除,确认弹框标题为「Tip」 | 确认框英文显示 | | ☐ |
|
||||
| 6.5 | 列表右上角「帮助」按钮(? 图标) | 鼠标悬停或点击显示「物料信息用于维护物料编码、名称、规格等属性」 | | ☐ |
|
||||
| 6.6 | 中文状态下批量删除无选中数据 | 错误提示「请先选择要删除的数据」 | | ☐ |
|
||||
| 6.7 | 英文状态下批量删除无选中数据 | 错误提示「Please select data first」 | | ☐ |
|
||||
|
||||
### 3.5 接口契约(用于前后端联调核对)
|
||||
|
||||
| 方法 | URL | 旧 method 名 | 入参关键 key | 备注 |
|
||||
|------|-----|-------------|-------------|------|
|
||||
| GET | `production_configuration/matetial_model/matetial_management/list` | `production_configuration_matetial_model_matetial_management_list` | `code / name / bom_source_category_id / page_no / page_size` | 列表查询 |
|
||||
| GET | `…/all` | `…_all` | — | 全部物料(下拉框用,可选) |
|
||||
| POST | `…/create` | `…_create` | `code / name / bom_source_category_id / unit_id / remark` | 新增 |
|
||||
| PUT | `…/edit` | `…_edit` | `id / code / name / bom_source_category_id / unit_id / remark` | 编辑(`id` 为行主键) |
|
||||
| DELETE | `…/delete` | `…_delete` | `id: [rowId]` | 单条删除,参数名 `id` 数组(与旧项目保持一致) |
|
||||
| DELETE | `…/batch_delete` | `…_batch_delete` | `id: [id1, id2, …]` | 批量删除 |
|
||||
| POST | `…/get_import_template` | `…_get_import_template` | — | 导入模板下载(响应 `blob`),本版本预留 |
|
||||
| POST | `…/matetial_data_import` | `…_matetial_data_import` | `import_data: JSON.stringify([...])` | 物料数据导入,本版本预留 |
|
||||
| POST | `…/matetial_data_export_task` | `…_matetial_data_export_task` | — | 物料数据导出任务创建,本版本预留 |
|
||||
|
||||
> 说明:本版本优先实现列表 / 新增 / 编辑 / 删除 / 批量删除五大核心功能;导入 / 导出(Excel 模板下载、批量导入、导出任务)保留 API 方法和钩子位置,待后续迭代中补齐弹框 UI。
|
||||
|
||||
### 3.6 问题记录
|
||||
|
||||
> 测试人员发现问题时填写。每条问题一行,格式:`[编号] [用例编号] [现象] [复现步骤] [期望] [截图/日志] [负责人] [处理状态]`。
|
||||
|
||||
| 编号 | 用例 | 现象 | 复现步骤 | 期望 | 截图/日志 | 负责人 | 状态 |
|
||||
|:---:|:---:|------|---------|------|----------|:----:|:----:|
|
||||
| | | | | | | | |
|
||||
|
||||
### 3.7 备注
|
||||
|
||||
- 本页面沿用旧 URL 与后台 method 名,待后台数据库路由表统一更新后统一修改。
|
||||
- 「物料类别」下拉数据由 `getMaterialCategoryAll` 提供,依赖「物料类别列表」功能已迁移完成;如该下拉为空,请先检查该模块是否正常运行。
|
||||
- 「单位」字段在当前版本以文本框占位,依赖「计量单位」模块迁移后接入下拉数据源(与 BOM 等联动),测试时需在「备注」中注明该功能尚未启用。
|
||||
- 涉及 `confirmMixin` / `useTableColumns` / `useTableButtons` / `i18nMixin` 等公共 Composable,如其他页面出现类似问题,先确认是否在迁移组件时有遗漏。
|
||||
- 备注长度 > 20 字符时使用 `el-popover` 悬停展示全量内容;该交互与旧项目保持一致。
|
||||
- 若新增 / 编辑成功后接口返回结构变化(`res.data` 包裹层调整),需要同步更新 `fetchData` 的解析逻辑。
|
||||
57
src/api/production-master-data/material-category.js
Normal file
57
src/api/production-master-data/material-category.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
// 注:BASE URL 与后台 method 名暂沿用旧项目命名,等待后台数据库路由表统一更新
|
||||
const BASE = 'production_configuration/matetial_model/matetial_category/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_configuration_matetial_model_matetial_category_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
// 获取全部物料类别
|
||||
export function getMaterialCategoryAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取物料类别列表
|
||||
export function getMaterialCategoryList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 创建物料类别
|
||||
export function createMaterialCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑物料类别
|
||||
export function editMaterialCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 删除物料类别
|
||||
export function deleteMaterialCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
94
src/api/production-master-data/material-master.js
Normal file
94
src/api/production-master-data/material-master.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
// 注:BASE URL 与后台 method 名暂沿用旧项目命名,等待后台数据库路由表统一更新
|
||||
const BASE = 'production_configuration/matetial_model/matetial_management/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_configuration_matetial_model_matetial_management_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
// 获取全部物料
|
||||
export function getMaterialMasterAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取物料列表
|
||||
export function getMaterialMasterList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 创建物料
|
||||
export function createMaterialMaster (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑物料
|
||||
export function editMaterialMaster (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 删除物料
|
||||
export function deleteMaterialMaster (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除物料
|
||||
export function batchDeleteMaterialMaster (data) {
|
||||
return request({
|
||||
url: BASE + 'batch_delete',
|
||||
method: 'delete',
|
||||
data: apiParams('batch_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 importMaterialData (data) {
|
||||
return request({
|
||||
url: BASE + 'matetial_data_import',
|
||||
method: 'post',
|
||||
data: apiParams('matetial_data_import', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 物料数据导出任务创建
|
||||
export function exportMaterialTask (data) {
|
||||
return request({
|
||||
url: BASE + 'matetial_data_export_task',
|
||||
method: 'post',
|
||||
data: apiParams('matetial_data_export_task', data)
|
||||
})
|
||||
}
|
||||
57
src/api/production-master-data/material-unit.js
Normal file
57
src/api/production-master-data/material-unit.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
// 注:BASE URL 与后台 method 名暂沿用旧项目命名,等待后台数据库路由表统一更新
|
||||
const BASE = 'production_configuration/matetial_model/unit/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_configuration_matetial_model_unit_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
// 获取全部单位(下拉框用)
|
||||
export function getUnitAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取单位列表
|
||||
export function getUnitList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 创建单位
|
||||
export function createUnit (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑单位
|
||||
export function editUnit (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 删除单位
|
||||
export function deleteUnit (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
64
src/api/production-master-data/optional-params.js
Normal file
64
src/api/production-master-data/optional-params.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/technology_model/optional_params/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_process_model_optional_params_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getOptionalParamsList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createOptionalParams (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editOptionalParams (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteOptionalParams (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: {
|
||||
method: 'production_master_data_process_model_optional_params_get_import_template',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function importDataCreate (data) {
|
||||
return request({
|
||||
url: BASE + 'import_data_create',
|
||||
method: 'post',
|
||||
data: apiParams('import_data_create', data)
|
||||
})
|
||||
}
|
||||
55
src/api/production-master-data/process-step.js
Normal file
55
src/api/production-master-data/process-step.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/technology_model/technology_flow_workingsubclass/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_process_model_process_step_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getProcessStepList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createProcessStep (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editProcessStep (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteProcessStep (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function settingSubmit (data) {
|
||||
return request({
|
||||
url: BASE + 'setting_submit',
|
||||
method: 'post',
|
||||
data: {
|
||||
method: 'production_master_data_process_model_process_step_setting_submit',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -99,6 +99,153 @@
|
||||
"confirm_delete": "Are you sure to delete?",
|
||||
"validation_fail": "Validation failed",
|
||||
"please_enter": "Please enter {name}"
|
||||
},
|
||||
"process_step": {
|
||||
"search": "Search",
|
||||
"reset": "Reset",
|
||||
"query": "Query",
|
||||
"sort": "No.",
|
||||
"process_unit_code": "Process Unit Code",
|
||||
"process_unit_name": "Process Unit Name",
|
||||
"device_category": "Device Category",
|
||||
"remark": "Remark",
|
||||
"enter_remark": "Enter remark",
|
||||
"operation": "Actions",
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"tips": "Tip",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"enter_process_unit_code": "Enter process unit code",
|
||||
"enter_process_unit_name": "Enter process unit name",
|
||||
"select_device_category": "Please select device category",
|
||||
"remark_required": "Please enter remark",
|
||||
"preset_setting": "Preset Settings",
|
||||
"add_process_unit": "Add Process Unit",
|
||||
"edit_process_unit": "Edit Process Unit",
|
||||
"preset_result_param": "Preset Result Parameters",
|
||||
"length_1_100": "Length must be 1-100 characters",
|
||||
"operation_success": "Operation succeeded",
|
||||
"validation_fail": "Validation failed",
|
||||
"confirm_message": "Proceed with this action?",
|
||||
"name": "Name",
|
||||
"param": "Parameter",
|
||||
"category": "Category",
|
||||
"default_value": "Default Value",
|
||||
"unit": "Unit",
|
||||
"is_unique": "Is Unique",
|
||||
"yes": "Yes",
|
||||
"no": "No",
|
||||
"is_upload": "Is Upload Required",
|
||||
"need_device_upload": "Requires Device Upload",
|
||||
"add_row": "Add Row",
|
||||
"import": "Import",
|
||||
"close": "Close",
|
||||
"help": "Process unit is the basic operation unit in the process flow, used to define each processing step in the production process.",
|
||||
"enter_name": "Please enter name",
|
||||
"enter_param": "Please enter parameter",
|
||||
"select_type": "Please select type",
|
||||
"confirm_delete_data": "Are you sure to delete this data?",
|
||||
"save_success": "Saved successfully",
|
||||
"add_success": "Added successfully",
|
||||
"delete_success": "Deleted successfully",
|
||||
"setting_placeholder": "Preset setting configuration area",
|
||||
"setting_tip": "Configure preset settings for this process unit here",
|
||||
"import_tip": "Import function is under development"
|
||||
}
|
||||
},
|
||||
"material_model": {
|
||||
"material_category": {
|
||||
"search": "Search",
|
||||
"reset": "Reset",
|
||||
"enter_code": "Please enter category code",
|
||||
"enter_name": "Please enter category name",
|
||||
"enter_remark": "Please enter remark",
|
||||
"operation_success": "Operation succeeded",
|
||||
"create_success": "Create succeeded",
|
||||
"edit_success": "Edit succeeded",
|
||||
"delete_success": "Delete succeeded",
|
||||
"sort": "#",
|
||||
"code": "Code",
|
||||
"name": "Name",
|
||||
"remark": "Remark",
|
||||
"create_time": "Created At",
|
||||
"operation": "Action",
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"remark_length": "Length 1 to 45 characters",
|
||||
"add_title": "Add Material Category",
|
||||
"edit_title": "Edit Material Category",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
"tip": "Tip",
|
||||
"confirm_delete": "Are you sure to perform this operation?",
|
||||
"validation_fail": "Validation failed",
|
||||
"please_enter": "Please enter {name}",
|
||||
"help": "Material category is used to distinguish raw materials and semi-finished products"
|
||||
},
|
||||
"material_master": {
|
||||
"search": "Search",
|
||||
"reset": "Reset",
|
||||
"material_code": "Material Code",
|
||||
"material_name": "Material Name",
|
||||
"material_category": "Category",
|
||||
"unit": "Unit",
|
||||
"enter_material_code": "Please enter material code",
|
||||
"enter_material_name": "Please enter material name",
|
||||
"select_material_category": "Please select a category",
|
||||
"select_unit": "Please select a unit",
|
||||
"remark": "Remark",
|
||||
"enter_remark": "Please enter remark",
|
||||
"remark_length": "Length 1 to 100 characters",
|
||||
"create_user": "Created By",
|
||||
"create_time": "Created At",
|
||||
"operation": "Action",
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"batch_delete": "Batch Delete",
|
||||
"operation_success": "Operation succeeded",
|
||||
"add_title": "Add Material",
|
||||
"edit_title": "Edit Material",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
"tip": "Tip",
|
||||
"confirm_delete": "Are you sure to perform this operation?",
|
||||
"confirm_batch_delete": "Are you sure to delete the selected materials?",
|
||||
"please_select_data": "Please select data first",
|
||||
"validation_fail": "Validation failed",
|
||||
"please_enter": "Please enter {name}",
|
||||
"help": "Material master data is used to maintain material code, name, specifications, etc."
|
||||
},
|
||||
"material_unit": {
|
||||
"search": "Search",
|
||||
"reset": "Reset",
|
||||
"unit_code": "Unit Code",
|
||||
"unit_name": "Unit Name",
|
||||
"enter_unit_code": "Please enter unit code",
|
||||
"enter_unit_name": "Please enter unit name",
|
||||
"remark": "Remark",
|
||||
"enter_remark": "Please enter remark",
|
||||
"length_1_100": "Length 1 to 100 characters",
|
||||
"create_time": "Created At",
|
||||
"operation": "Action",
|
||||
"sort": "#",
|
||||
"add": "Add",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"operation_success": "Operation succeeded",
|
||||
"add_title": "Add Unit",
|
||||
"edit_title": "Edit Unit",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
"tip": "Tip",
|
||||
"confirm_delete": "Are you sure to perform this operation?",
|
||||
"validation_fail": "Validation failed",
|
||||
"please_enter": "Please enter {name}",
|
||||
"help": "Unit is used to maintain material measurement units (e.g. piece, box, kg)"
|
||||
}
|
||||
},
|
||||
"product_management": {
|
||||
|
||||
@@ -99,6 +99,153 @@
|
||||
"confirm_delete": "确定要执行该操作吗?",
|
||||
"validation_fail": "校验失败",
|
||||
"please_enter": "请输入{name}"
|
||||
},
|
||||
"process_step": {
|
||||
"search": "查询",
|
||||
"reset": "重置",
|
||||
"query": "查询",
|
||||
"sort": "序号",
|
||||
"process_unit_code": "工序单元编码",
|
||||
"process_unit_name": "工序单元名称",
|
||||
"device_category": "设备类别",
|
||||
"remark": "备注",
|
||||
"enter_remark": "请输入备注",
|
||||
"operation": "操作",
|
||||
"add": "新增",
|
||||
"edit": "编辑",
|
||||
"delete": "删除",
|
||||
"tips": "提示",
|
||||
"confirm": "确定",
|
||||
"cancel": "取消",
|
||||
"enter_process_unit_code": "请输入工序单元编码",
|
||||
"enter_process_unit_name": "请输入工序单元名称",
|
||||
"select_device_category": "请选择设备类别",
|
||||
"remark_required": "请输入备注",
|
||||
"preset_setting": "预设设定值",
|
||||
"add_process_unit": "新增工序单元",
|
||||
"edit_process_unit": "编辑工序单元",
|
||||
"preset_result_param": "预设结果参数",
|
||||
"length_1_100": "长度在 1 到 100 个字符",
|
||||
"operation_success": "操作成功",
|
||||
"validation_fail": "校验失败",
|
||||
"confirm_message": "确定要执行该操作吗?",
|
||||
"name": "名称",
|
||||
"param": "参数",
|
||||
"category": "类别",
|
||||
"default_value": "默认值",
|
||||
"unit": "单位",
|
||||
"is_unique": "是否唯一",
|
||||
"yes": "是",
|
||||
"no": "否",
|
||||
"is_upload": "是否上传",
|
||||
"need_device_upload": "是否需要设备上传",
|
||||
"add_row": "新增一行",
|
||||
"import": "导入",
|
||||
"close": "关闭",
|
||||
"help": "工序单元是工艺流程中的基本作业单元,用于定义生产过程中的各个加工步骤。",
|
||||
"enter_name": "请输入名称",
|
||||
"enter_param": "请输入参数",
|
||||
"select_type": "请选择类型",
|
||||
"confirm_delete_data": "确定删除该数据?",
|
||||
"save_success": "保存成功",
|
||||
"add_success": "新增成功",
|
||||
"delete_success": "删除成功",
|
||||
"setting_placeholder": "预设设定值配置区域",
|
||||
"setting_tip": "在此配置工序单元的预设设定值",
|
||||
"import_tip": "导入功能开发中"
|
||||
}
|
||||
},
|
||||
"material_model": {
|
||||
"material_category": {
|
||||
"search": "查询",
|
||||
"reset": "重置",
|
||||
"enter_code": "请输入物料类别编码",
|
||||
"enter_name": "请输入物料类别名称",
|
||||
"enter_remark": "请输入备注",
|
||||
"operation_success": "操作成功",
|
||||
"create_success": "新增成功",
|
||||
"edit_success": "编辑成功",
|
||||
"delete_success": "删除成功",
|
||||
"sort": "序号",
|
||||
"code": "物料类别编码",
|
||||
"name": "物料类别名称",
|
||||
"remark": "备注",
|
||||
"create_time": "创建时间",
|
||||
"operation": "操作",
|
||||
"add": "新 增",
|
||||
"edit": "编 辑",
|
||||
"delete": "删 除",
|
||||
"remark_length": "长度在 1 到 45 个字符",
|
||||
"add_title": "新增物料类别",
|
||||
"edit_title": "编辑物料类别",
|
||||
"cancel": "取消",
|
||||
"confirm": "确定",
|
||||
"tip": "提示",
|
||||
"confirm_delete": "确定要执行该操作吗?",
|
||||
"validation_fail": "校验失败",
|
||||
"please_enter": "请输入{name}",
|
||||
"help": "物料类别用于区分原材料和半成品"
|
||||
},
|
||||
"material_master": {
|
||||
"search": "查询",
|
||||
"reset": "重置",
|
||||
"material_code": "物料编码",
|
||||
"material_name": "物料名称",
|
||||
"material_category": "物料类别",
|
||||
"unit": "单位",
|
||||
"enter_material_code": "请输入物料编码",
|
||||
"enter_material_name": "请输入物料名称",
|
||||
"select_material_category": "请选择物料类别",
|
||||
"select_unit": "请选择单位",
|
||||
"remark": "备注",
|
||||
"enter_remark": "请输入备注",
|
||||
"remark_length": "长度在 1 到 100 个字符",
|
||||
"create_user": "创建人",
|
||||
"create_time": "创建时间",
|
||||
"operation": "操作",
|
||||
"add": "新 增",
|
||||
"edit": "编 辑",
|
||||
"delete": "删 除",
|
||||
"batch_delete": "批量删除",
|
||||
"operation_success": "操作成功",
|
||||
"add_title": "新增物料信息",
|
||||
"edit_title": "编辑物料信息",
|
||||
"cancel": "取消",
|
||||
"confirm": "确定",
|
||||
"tip": "提示",
|
||||
"confirm_delete": "确定要执行该操作吗?",
|
||||
"confirm_batch_delete": "确定要删除所选物料吗?",
|
||||
"please_select_data": "请先选择要删除的数据",
|
||||
"validation_fail": "校验失败",
|
||||
"please_enter": "请输入{name}",
|
||||
"help": "物料信息用于维护物料编码、名称、规格等属性"
|
||||
},
|
||||
"material_unit": {
|
||||
"search": "查询",
|
||||
"reset": "重置",
|
||||
"unit_code": "单位编码",
|
||||
"unit_name": "单位名称",
|
||||
"enter_unit_code": "请输入单位编码",
|
||||
"enter_unit_name": "请输入单位名称",
|
||||
"remark": "备注",
|
||||
"enter_remark": "请输入备注",
|
||||
"length_1_100": "长度在 1 到 100 个字符",
|
||||
"create_time": "创建时间",
|
||||
"operation": "操作",
|
||||
"sort": "序号",
|
||||
"add": "新 增",
|
||||
"edit": "编 辑",
|
||||
"delete": "删 除",
|
||||
"operation_success": "操作成功",
|
||||
"add_title": "新增单位",
|
||||
"edit_title": "编辑单位",
|
||||
"cancel": "取消",
|
||||
"confirm": "确定",
|
||||
"tip": "提示",
|
||||
"confirm_delete": "确定要执行该操作吗?",
|
||||
"validation_fail": "校验失败",
|
||||
"please_enter": "请输入{name}",
|
||||
"help": "计量单位用于维护物料所用单位(如个、箱、kg)"
|
||||
}
|
||||
},
|
||||
"product_management": {
|
||||
|
||||
@@ -32,11 +32,35 @@ export default {
|
||||
meta: { ...meta, cache: true, title: '工艺流程类别' },
|
||||
component: _import('production-master-data/process-model/process-category')
|
||||
},
|
||||
{
|
||||
path: 'technology_model/technology_flow_workingsubclass',
|
||||
name: `${pre}technology_model-technology_flow_workingsubclass`,
|
||||
meta: { ...meta, cache: true, title: '工序单元' },
|
||||
component: _import('production-master-data/process-model/process-step')
|
||||
},
|
||||
{
|
||||
path: 'product_model/battery_model',
|
||||
name: `${pre}product_management-product_list`,
|
||||
meta: { ...meta, cache: true, title: '产品列表' },
|
||||
component: _import('production-master-data/product-management/product-list')
|
||||
},
|
||||
{
|
||||
path: 'matetial_model/matetial_category',
|
||||
name: `${pre}material_model-material_category`,
|
||||
meta: { ...meta, cache: true, title: '物料类别列表' },
|
||||
component: _import('production-master-data/material-model/material-category')
|
||||
},
|
||||
{
|
||||
path: 'matetial_model/matetial_management',
|
||||
name: `${pre}material_model-material_master`,
|
||||
meta: { ...meta, cache: true, title: '物料信息管理' },
|
||||
component: _import('production-master-data/material-model/material-master')
|
||||
},
|
||||
{
|
||||
path: 'matetial_model/unit',
|
||||
name: `${pre}material_model-material_unit`,
|
||||
meta: { ...meta, cache: true, title: '计量单位' },
|
||||
component: _import('production-master-data/material-model/material-unit')
|
||||
}
|
||||
])('production_configuration-')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<d2-container>
|
||||
<template #header>
|
||||
<div class="search-bar">
|
||||
<el-form :inline="true" size="mini">
|
||||
<el-form-item :label="$t(key('code'))">
|
||||
<el-input
|
||||
v-model="search.code"
|
||||
:placeholder="$t(key('enter_code'))"
|
||||
clearable
|
||||
style="width:200px"
|
||||
@keyup.enter.native="onSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('name'))">
|
||||
<el-input
|
||||
v-model="search.name"
|
||||
:placeholder="$t(key('enter_name'))"
|
||||
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"
|
||||
help-url="/help/material-category"
|
||||
:help-text="$t(ckey('help'))"
|
||||
auto-height
|
||||
@page-change="onPageChange"
|
||||
@selection-change="onSelect"
|
||||
/>
|
||||
|
||||
<page-dialog-form
|
||||
ref="dialogForm"
|
||||
:visible.sync="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
:width="'35%'"
|
||||
:form-cols="formCols"
|
||||
:form-data="formData"
|
||||
:rules="rules"
|
||||
:label-width="'100px'"
|
||||
: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 {
|
||||
getMaterialCategoryList,
|
||||
createMaterialCategory,
|
||||
editMaterialCategory,
|
||||
deleteMaterialCategory
|
||||
} from '@/api/production-master-data/material-category'
|
||||
import PageTable from '@/components/page-table'
|
||||
import PageDialogForm from '@/components/page-dialog-form'
|
||||
|
||||
export default {
|
||||
name: 'production-master-data-material-category',
|
||||
components: { PageTable, PageDialogForm },
|
||||
mixins: [i18nMixin('page.production_master_data.material_model.material_category'), confirmMixin],
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
submitting: false,
|
||||
tableData: [],
|
||||
selectedRows: [],
|
||||
dialogVisible: false,
|
||||
dialogTitle: '',
|
||||
editId: '',
|
||||
handleType: 'create',
|
||||
search: { code: '', name: '' },
|
||||
pagination: { current: 1, size: 10, total: 0 },
|
||||
formData: { code: '', name: '', remark: '' },
|
||||
rules: {
|
||||
code: [
|
||||
{ required: true, message: this.key('enter_code'), trigger: 'blur' },
|
||||
{ min: 1, max: 45, message: this.key('remark_length'), trigger: 'blur' }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: this.key('enter_name'), trigger: 'blur' },
|
||||
{ min: 1, max: 45, message: this.key('remark_length'), trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
columns: [],
|
||||
toolbarButtons: [],
|
||||
rowButtons: [],
|
||||
formCols: [
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'code',
|
||||
label: this.key('code'),
|
||||
placeholder: this.key('enter_code'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'name',
|
||||
label: this.key('name'),
|
||||
placeholder: this.key('enter_name'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'remark',
|
||||
inputType: 'textarea',
|
||||
autosize: { minRows: 2, maxRows: 6 },
|
||||
label: this.key('remark'),
|
||||
placeholder: this.key('enter_remark'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.columns = useTableColumns([
|
||||
{ prop: 'sort', label: this.key('sort'), width: 80 },
|
||||
{ prop: 'code', label: this.key('code'), minWidth: 120 },
|
||||
{ prop: 'name', label: this.key('name'), minWidth: 120 },
|
||||
{ prop: 'remark', label: this.key('remark') },
|
||||
{ prop: 'create_time', label: this.key('create_time'), minWidth: 160 },
|
||||
{ prop: '_actions', label: this.key('operation'), width: 160, fixed: 'right' }
|
||||
])
|
||||
const btns = useTableButtons({
|
||||
toolbar: [
|
||||
{
|
||||
key: 'add',
|
||||
label: this.key('add'),
|
||||
icon: 'el-icon-plus',
|
||||
type: 'primary',
|
||||
auth: '/production_configuration/matetial_model/matetial_category/create',
|
||||
onClick: this.openAdd
|
||||
}
|
||||
],
|
||||
row: [
|
||||
{
|
||||
key: 'edit',
|
||||
label: this.key('edit'),
|
||||
icon: 'el-icon-edit',
|
||||
auth: '/production_configuration/matetial_model/matetial_category/edit',
|
||||
onClick: this.openEdit
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: this.key('delete'),
|
||||
icon: 'el-icon-delete',
|
||||
color: 'danger',
|
||||
auth: '/production_configuration/matetial_model/matetial_category/delete',
|
||||
onClick: this.handleDelete
|
||||
}
|
||||
]
|
||||
}, this.$permission)
|
||||
this.toolbarButtons = btns.toolbarButtons
|
||||
this.rowButtons = btns.rowButtons
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData () {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getMaterialCategoryList({
|
||||
...this.search,
|
||||
page_no: this.pagination.current,
|
||||
page_size: this.pagination.size
|
||||
})
|
||||
const list = Array.isArray(res) ? res : (res.data || [])
|
||||
const total = Array.isArray(res) ? res.length : (res.count || 0)
|
||||
this.tableData = list
|
||||
this.pagination.total = total
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
onSearch () {
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
onReset () {
|
||||
this.search = { code: '', name: '' }
|
||||
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 = { code: '', 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 = {
|
||||
code: row.code,
|
||||
name: row.name,
|
||||
remark: row.remark || ''
|
||||
}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
async onDialogSubmit () {
|
||||
this.submitting = true
|
||||
try {
|
||||
if (this.handleType === 'create') {
|
||||
await createMaterialCategory(this.formData)
|
||||
} else {
|
||||
await editMaterialCategory({ ...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')
|
||||
},
|
||||
() => deleteMaterialCategory({ 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-bar {
|
||||
padding: 10px 0;
|
||||
}
|
||||
/deep/ .el-form-item--mini.el-form-item {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,401 @@
|
||||
<template>
|
||||
<d2-container>
|
||||
<template #header>
|
||||
<div class="search-bar">
|
||||
<el-form :inline="true" size="mini">
|
||||
<el-form-item :label="$t(key('material_code'))">
|
||||
<el-input
|
||||
v-model="search.code"
|
||||
:placeholder="$t(key('enter_material_code'))"
|
||||
clearable
|
||||
style="width:200px"
|
||||
@keyup.enter.native="onSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('material_name'))">
|
||||
<el-input
|
||||
v-model="search.name"
|
||||
:placeholder="$t(key('enter_material_name'))"
|
||||
clearable
|
||||
style="width:200px"
|
||||
@keyup.enter.native="onSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('material_category'))">
|
||||
<el-select
|
||||
v-model="search.bom_source_category_id"
|
||||
:placeholder="$t(key('select_material_category'))"
|
||||
clearable
|
||||
filterable
|
||||
style="width:200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in categoryOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</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"
|
||||
help-url="/help/material-master"
|
||||
:help-text="$t(ckey('help'))"
|
||||
auto-height
|
||||
@page-change="onPageChange"
|
||||
@selection-change="onSelect"
|
||||
>
|
||||
<template #col-remark="{ row }">
|
||||
<el-popover
|
||||
v-if="row.remark && row.remark.length > 20"
|
||||
placement="top-start"
|
||||
width="300"
|
||||
trigger="hover"
|
||||
:content="row.remark"
|
||||
>
|
||||
<span slot="reference" style="cursor: pointer;">{{ row.remark.substr(0, 20) }}...</span>
|
||||
</el-popover>
|
||||
<span v-else>{{ row.remark }}</span>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
<page-dialog-form
|
||||
ref="dialogForm"
|
||||
:visible.sync="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
:width="'35%'"
|
||||
:form-cols="dialogFormCols"
|
||||
:form-data="formData"
|
||||
:rules="rules"
|
||||
:label-width="'100px'"
|
||||
: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 {
|
||||
getMaterialMasterList,
|
||||
createMaterialMaster,
|
||||
editMaterialMaster,
|
||||
deleteMaterialMaster,
|
||||
batchDeleteMaterialMaster
|
||||
} from '@/api/production-master-data/material-master'
|
||||
import { getMaterialCategoryAll } from '@/api/production-master-data/material-category'
|
||||
import PageTable from '@/components/page-table'
|
||||
import PageDialogForm from '@/components/page-dialog-form'
|
||||
|
||||
export default {
|
||||
name: 'production-master-data-material-master',
|
||||
components: { PageTable, PageDialogForm },
|
||||
mixins: [i18nMixin('page.production_master_data.material_model.material_master'), confirmMixin],
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
submitting: false,
|
||||
tableData: [],
|
||||
selectedRows: [],
|
||||
dialogVisible: false,
|
||||
dialogTitle: '',
|
||||
editId: '',
|
||||
handleType: 'create',
|
||||
search: { code: '', name: '', bom_source_category_id: '' },
|
||||
pagination: { current: 1, size: 10, total: 0 },
|
||||
categoryOptions: [],
|
||||
formData: { code: '', name: '', bom_source_category_id: '', unit_id: '', remark: '' },
|
||||
rules: {
|
||||
code: [
|
||||
{ required: true, message: this.key('enter_material_code'), trigger: 'blur' },
|
||||
{ min: 1, max: 100, message: this.key('remark_length'), trigger: 'blur' }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: this.key('enter_material_name'), trigger: 'blur' },
|
||||
{ min: 1, max: 100, message: this.key('remark_length'), trigger: 'blur' }
|
||||
],
|
||||
bom_source_category_id: [
|
||||
{ required: true, message: this.key('select_material_category'), trigger: 'change' }
|
||||
]
|
||||
},
|
||||
columns: [],
|
||||
toolbarButtons: [],
|
||||
rowButtons: [],
|
||||
// 表单基础结构,单位字段预留(待 unit 模块迁移后接入下拉数据源)
|
||||
baseFormCols: [
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'code',
|
||||
label: this.key('material_code'),
|
||||
placeholder: this.key('enter_material_code'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'name',
|
||||
label: this.key('material_name'),
|
||||
placeholder: this.key('enter_material_name'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'select',
|
||||
prop: 'bom_source_category_id',
|
||||
label: this.key('material_category'),
|
||||
placeholder: this.key('select_material_category'),
|
||||
clearable: true,
|
||||
filterable: true,
|
||||
style: { width: '90%' },
|
||||
options: []
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'unit_id',
|
||||
label: this.key('unit'),
|
||||
placeholder: this.key('select_unit'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'remark',
|
||||
inputType: 'textarea',
|
||||
autosize: { minRows: 2, maxRows: 6 },
|
||||
label: this.key('remark'),
|
||||
placeholder: this.key('enter_remark'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 类别下拉数据通过 computed 注入,避免 JSON.parse(JSON.stringify) 深拷贝
|
||||
dialogFormCols () {
|
||||
return this.baseFormCols.map(row => row.map(item => {
|
||||
if (item.prop === 'bom_source_category_id') {
|
||||
return { ...item, options: this.categoryOptions }
|
||||
}
|
||||
return item
|
||||
}))
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.initCategoryOptions()
|
||||
this.columns = useTableColumns([
|
||||
{ prop: 'sort', label: this.key('sort'), width: 80 },
|
||||
{ prop: 'code', label: this.key('material_code'), minWidth: 120 },
|
||||
{ prop: 'name', label: this.key('material_name'), minWidth: 140 },
|
||||
{ prop: 'bom_source_category_name', label: this.key('material_category'), minWidth: 120 },
|
||||
{ prop: 'unit_name', label: this.key('unit'), minWidth: 100 },
|
||||
{ prop: 'remark', label: this.key('remark'), minWidth: 160, slot: true },
|
||||
{ prop: 'username', label: this.key('create_user'), minWidth: 100 },
|
||||
{ prop: 'create_time', label: this.key('create_time'), minWidth: 160 },
|
||||
{ prop: '_actions', label: this.key('operation'), width: 180, fixed: 'right' }
|
||||
])
|
||||
const btns = useTableButtons({
|
||||
toolbar: [
|
||||
{
|
||||
key: 'add',
|
||||
label: this.key('add'),
|
||||
icon: 'el-icon-plus',
|
||||
type: 'primary',
|
||||
auth: '/production_configuration/matetial_model/matetial_management/create',
|
||||
onClick: this.openAdd
|
||||
},
|
||||
{
|
||||
key: 'batch_delete',
|
||||
label: this.key('batch_delete'),
|
||||
icon: 'el-icon-delete',
|
||||
color: 'danger',
|
||||
auth: '/system_settings/matetial_model/matetial_management/batch-delete',
|
||||
onClick: this.handleBatchDelete
|
||||
}
|
||||
],
|
||||
row: [
|
||||
{
|
||||
key: 'edit',
|
||||
label: this.key('edit'),
|
||||
icon: 'el-icon-edit',
|
||||
auth: '/production_configuration/matetial_model/matetial_management/edit',
|
||||
onClick: this.openEdit
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: this.key('delete'),
|
||||
icon: 'el-icon-delete',
|
||||
color: 'danger',
|
||||
auth: '/production_configuration/matetial_model/matetial_management/delete',
|
||||
onClick: this.handleDelete
|
||||
}
|
||||
]
|
||||
}, this.$permission)
|
||||
this.toolbarButtons = btns.toolbarButtons
|
||||
this.rowButtons = btns.rowButtons
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async initCategoryOptions () {
|
||||
try {
|
||||
const res = await getMaterialCategoryAll()
|
||||
const data = Array.isArray(res) ? res : (res.data || [])
|
||||
this.categoryOptions = data.map(item => ({ value: item.id, label: item.name }))
|
||||
} catch { /* 类别下拉加载失败时,下拉为空即可,不影响主流程 */ }
|
||||
},
|
||||
async fetchData () {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getMaterialMasterList({
|
||||
...this.search,
|
||||
page_no: this.pagination.current,
|
||||
page_size: this.pagination.size
|
||||
})
|
||||
const list = Array.isArray(res) ? res : (res.data || [])
|
||||
const total = Array.isArray(res) ? res.length : (res.count || 0)
|
||||
this.tableData = list
|
||||
this.pagination.total = total
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
onSearch () {
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
onReset () {
|
||||
this.search = { code: '', name: '', bom_source_category_id: '' }
|
||||
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 = { code: '', name: '', bom_source_category_id: '', unit_id: '', 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 = {
|
||||
code: row.code,
|
||||
name: row.name,
|
||||
bom_source_category_id: row.bom_source_category_id,
|
||||
unit_id: row.unit_id,
|
||||
remark: row.remark || ''
|
||||
}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
async onDialogSubmit () {
|
||||
this.submitting = true
|
||||
try {
|
||||
if (this.handleType === 'create') {
|
||||
await createMaterialMaster(this.formData)
|
||||
} else {
|
||||
await editMaterialMaster({ ...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')
|
||||
},
|
||||
() => deleteMaterialMaster({ 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 handleBatchDelete () {
|
||||
if (this.selectedRows.length <= 0) {
|
||||
this.$message.error(this.$t(this.key('please_select_data')))
|
||||
return
|
||||
}
|
||||
const ids = this.selectedRows.map(item => item.id)
|
||||
const cancelled = await this.$confirmAction(
|
||||
{
|
||||
message: this.key('confirm_batch_delete'),
|
||||
title: this.key('tip')
|
||||
},
|
||||
() => batchDeleteMaterialMaster({ id: ids })
|
||||
)
|
||||
if (cancelled) return
|
||||
this.$message.success(this.$t(this.key('operation_success')))
|
||||
this.fetchData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-bar {
|
||||
padding: 10px 0;
|
||||
}
|
||||
/deep/ .el-form-item--mini.el-form-item {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,288 @@
|
||||
<template>
|
||||
<d2-container>
|
||||
<template #header>
|
||||
<div class="search-bar">
|
||||
<el-form :inline="true" size="mini">
|
||||
<el-form-item :label="$t(key('unit_code'))">
|
||||
<el-input
|
||||
v-model="search.code"
|
||||
:placeholder="$t(key('enter_unit_code'))"
|
||||
clearable
|
||||
style="width:200px"
|
||||
@keyup.enter.native="onSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('unit_name'))">
|
||||
<el-input
|
||||
v-model="search.name"
|
||||
:placeholder="$t(key('enter_unit_name'))"
|
||||
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"
|
||||
:help-text="$t(ckey('help'))"
|
||||
auto-height
|
||||
@page-change="onPageChange"
|
||||
/>
|
||||
|
||||
<page-dialog-form
|
||||
ref="dialogForm"
|
||||
:visible.sync="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
:width="'35%'"
|
||||
:form-cols="formCols"
|
||||
:form-data="formData"
|
||||
:rules="rules"
|
||||
:label-width="'100px'"
|
||||
: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 {
|
||||
getUnitList,
|
||||
createUnit,
|
||||
editUnit,
|
||||
deleteUnit
|
||||
} from '@/api/production-master-data/material-unit'
|
||||
import PageTable from '@/components/page-table'
|
||||
import PageDialogForm from '@/components/page-dialog-form'
|
||||
|
||||
export default {
|
||||
name: 'production-master-data-material-unit',
|
||||
components: { PageTable, PageDialogForm },
|
||||
mixins: [i18nMixin('page.production_master_data.material_model.material_unit'), confirmMixin],
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
submitting: false,
|
||||
tableData: [],
|
||||
dialogVisible: false,
|
||||
dialogTitle: '',
|
||||
editId: '',
|
||||
handleType: 'create',
|
||||
search: { code: '', name: '' },
|
||||
pagination: { current: 1, size: 10, total: 0 },
|
||||
formData: { code: '', name: '', remark: '' },
|
||||
rules: {
|
||||
code: [
|
||||
{ required: true, message: this.key('enter_unit_code'), trigger: 'blur' },
|
||||
{ min: 1, max: 100, message: this.key('length_1_100'), trigger: 'blur' }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: this.key('enter_unit_name'), trigger: 'blur' },
|
||||
{ min: 1, max: 100, message: this.key('length_1_100'), trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
formCols: [
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'code',
|
||||
label: this.key('unit_code'),
|
||||
placeholder: this.key('enter_unit_code'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'name',
|
||||
label: this.key('unit_name'),
|
||||
placeholder: this.key('enter_unit_name'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'remark',
|
||||
inputType: 'textarea',
|
||||
autosize: { minRows: 2, maxRows: 6 },
|
||||
label: this.key('remark'),
|
||||
placeholder: this.key('enter_remark'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
]
|
||||
],
|
||||
columns: [],
|
||||
toolbarButtons: [],
|
||||
rowButtons: []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.columns = useTableColumns([
|
||||
{ prop: 'sort', label: this.key('sort'), width: 80 },
|
||||
{ prop: 'code', label: this.key('unit_code'), minWidth: 140 },
|
||||
{ prop: 'name', label: this.key('unit_name'), minWidth: 140 },
|
||||
{ prop: 'remark', label: this.key('remark'), minWidth: 200 },
|
||||
{ prop: 'create_time', label: this.key('create_time'), minWidth: 160 },
|
||||
{ prop: '_actions', label: this.key('operation'), width: 180, fixed: 'right' }
|
||||
])
|
||||
const btns = useTableButtons({
|
||||
toolbar: [
|
||||
{
|
||||
key: 'add',
|
||||
label: this.key('add'),
|
||||
icon: 'el-icon-plus',
|
||||
type: 'primary',
|
||||
auth: '/production_configuration/matetial_model/unit/create',
|
||||
onClick: this.openAdd
|
||||
}
|
||||
],
|
||||
row: [
|
||||
{
|
||||
key: 'edit',
|
||||
label: this.key('edit'),
|
||||
icon: 'el-icon-edit',
|
||||
auth: '/production_configuration/matetial_model/unit/edit',
|
||||
onClick: this.openEdit
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: this.key('delete'),
|
||||
icon: 'el-icon-delete',
|
||||
color: 'danger',
|
||||
auth: '/production_configuration/matetial_model/unit/delete',
|
||||
onClick: this.handleDelete
|
||||
}
|
||||
]
|
||||
}, this.$permission)
|
||||
this.toolbarButtons = btns.toolbarButtons
|
||||
this.rowButtons = btns.rowButtons
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData () {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getUnitList({
|
||||
...this.search,
|
||||
page_no: this.pagination.current,
|
||||
page_size: this.pagination.size
|
||||
})
|
||||
const list = Array.isArray(res) ? res : (res.data || [])
|
||||
const total = Array.isArray(res) ? res.length : (res.count || 0)
|
||||
this.tableData = list
|
||||
this.pagination.total = total
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
onSearch () {
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
onReset () {
|
||||
this.search = { code: '', name: '' }
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
onPageChange (page) {
|
||||
this.pagination.current = page.current
|
||||
this.pagination.size = page.size
|
||||
this.fetchData()
|
||||
},
|
||||
resetForm () {
|
||||
this.formData = { code: '', 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 = {
|
||||
code: row.code,
|
||||
name: row.name,
|
||||
remark: row.remark || ''
|
||||
}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
async onDialogSubmit () {
|
||||
this.submitting = true
|
||||
try {
|
||||
if (this.handleType === 'create') {
|
||||
await createUnit(this.formData)
|
||||
} else {
|
||||
await editUnit({ ...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')
|
||||
},
|
||||
() => deleteUnit({ 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-bar {
|
||||
padding: 10px 0;
|
||||
}
|
||||
/deep/ .el-form-item--mini.el-form-item {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:append-to-body="true"
|
||||
:close-on-click-modal="false"
|
||||
:before-close="handleClose"
|
||||
width="80%"
|
||||
>
|
||||
<div class="search-content">
|
||||
<el-card class="box-card">
|
||||
<el-form :inline="true" size="mini">
|
||||
<el-form-item :label="$t(key('name'))">
|
||||
<el-input
|
||||
v-model="searchForm.name"
|
||||
:placeholder="$t(key('enter_name'))"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('param'))">
|
||||
<el-input
|
||||
v-model="searchForm.code"
|
||||
:placeholder="$t(key('enter_param'))"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="onSearch">
|
||||
{{ $t(key('query')) }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="tableData"
|
||||
border
|
||||
:loading="loading"
|
||||
max-height="450"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column :label="$t(key('name'))" prop="name" width="200" show-overflow-tooltip />
|
||||
<el-table-column :label="$t(key('param'))" prop="code" width="150" show-overflow-tooltip />
|
||||
<el-table-column :label="$t(key('category'))" prop="field_type" width="120" />
|
||||
<el-table-column :label="$t(key('remark'))" prop="remark" show-overflow-tooltip />
|
||||
<el-table-column :label="$t(key('is_unique'))" width="100" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row.is_only === 1" type="success" size="mini">{{ $t(key('yes')) }}</el-tag>
|
||||
<el-tag v-else type="info" size="mini">{{ $t(key('no')) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t(key('is_upload'))" width="100" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row.is_upload === 1" type="success" size="mini">{{ $t(key('yes')) }}</el-tag>
|
||||
<el-tag v-else type="info" size="mini">{{ $t(key('no')) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t(key('operation'))" width="120" fixed="right">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleEdit(row)">
|
||||
{{ $t(key('edit')) }}
|
||||
</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-delete" style="color: #F56C6C" @click="handleDelete(row)">
|
||||
{{ $t(key('delete')) }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
:current-page="pagination.current"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="pagination.size"
|
||||
:total="pagination.total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" size="mini" @click="handleAdd">{{ $t(key('add_row')) }}</el-button>
|
||||
<el-button type="success" size="mini" icon="el-icon-download" @click="handleImport">{{ $t(key('import')) }}</el-button>
|
||||
<el-button size="mini" @click="handleClose">{{ $t(key('close')) }}</el-button>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
:title="dialogTitle"
|
||||
:visible.sync="innerVisible"
|
||||
append-to-body
|
||||
width="600px"
|
||||
:before-close="handleInnerClose"
|
||||
>
|
||||
<el-form ref="form" :model="formData" :rules="rules" label-width="100px">
|
||||
<el-form-item :label="$t(key('param'))" prop="code">
|
||||
<el-input v-model="formData.code" :placeholder="$t(key('enter_param'))" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('name'))" prop="name">
|
||||
<el-input v-model="formData.name" :placeholder="$t(key('enter_name'))" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('category'))" prop="field_type">
|
||||
<el-select v-model="formData.field_type" :placeholder="$t(key('select_type'))" style="width: 100%">
|
||||
<el-option label="FLOAT" value="FLOAT" />
|
||||
<el-option label="INT" value="INT" />
|
||||
<el-option label="VARCHAR" value="VARCHAR" />
|
||||
<el-option label="TEXT" value="TEXT" />
|
||||
<el-option label="TIMESTAMP" value="TIMESTAMP" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('remark'))">
|
||||
<el-input v-model="formData.remark" type="textarea" :rows="2" :placeholder="$t(key('remark_required'))" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('is_unique'))">
|
||||
<el-radio-group v-model="formData.is_only">
|
||||
<el-radio :label="1">{{ $t(key('yes')) }}</el-radio>
|
||||
<el-radio :label="0">{{ $t(key('no')) }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('is_upload'))">
|
||||
<el-radio-group v-model="formData.is_upload">
|
||||
<el-radio :label="1">{{ $t(key('yes')) }}</el-radio>
|
||||
<el-radio :label="0">{{ $t(key('no')) }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="handleInnerClose">{{ $t(key('cancel')) }}</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">{{ $t(key('confirm')) }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { i18nMixin } from '@/composables/useI18n'
|
||||
import {
|
||||
getOptionalParamsList,
|
||||
createOptionalParams,
|
||||
editOptionalParams,
|
||||
deleteOptionalParams
|
||||
} from '@/api/production-master-data/optional-params'
|
||||
|
||||
export default {
|
||||
name: 'ProcessStepResultParam',
|
||||
components: {},
|
||||
mixins: [i18nMixin('page.production_master_data.process_model.process_step')],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
workingsubclass_id: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
searchForm: { name: '', code: '' },
|
||||
pagination: { current: 1, size: 10, total: 0 },
|
||||
innerVisible: false,
|
||||
dialogTitle: '',
|
||||
formData: {
|
||||
id: '',
|
||||
code: '',
|
||||
name: '',
|
||||
field_type: '',
|
||||
remark: '',
|
||||
is_only: 0,
|
||||
is_upload: 0
|
||||
},
|
||||
rules: {
|
||||
code: [{ required: true, message: this.key('enter_param'), trigger: 'blur' }],
|
||||
name: [{ required: true, message: this.key('enter_name'), trigger: 'blur' }],
|
||||
field_type: [{ required: true, message: this.key('select_type'), trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
workingsubclass_id: {
|
||||
handler (val) {
|
||||
if (val) {
|
||||
this.fetchData()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose () {
|
||||
this.$emit('close')
|
||||
},
|
||||
handleInnerClose () {
|
||||
this.innerVisible = false
|
||||
this.$refs.form && this.$refs.form.resetFields()
|
||||
},
|
||||
onSearch () {
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
fetchData () {
|
||||
if (!this.workingsubclass_id) return
|
||||
this.loading = true
|
||||
getOptionalParamsList({
|
||||
workingsubclass_id: this.workingsubclass_id,
|
||||
page_no: this.pagination.current,
|
||||
page_size: this.pagination.size,
|
||||
...this.searchForm
|
||||
}).then(res => {
|
||||
const list = Array.isArray(res) ? res : (res.data || [])
|
||||
const total = Array.isArray(res) ? res.length : (res.count || 0)
|
||||
this.tableData = list
|
||||
this.pagination.total = total
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleSizeChange (size) {
|
||||
this.pagination.size = size
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
handleCurrentChange (current) {
|
||||
this.pagination.current = current
|
||||
this.fetchData()
|
||||
},
|
||||
handleAdd () {
|
||||
this.dialogTitle = this.key('add_row')
|
||||
this.formData = {
|
||||
id: '',
|
||||
code: '',
|
||||
name: '',
|
||||
field_type: '',
|
||||
remark: '',
|
||||
is_only: 0,
|
||||
is_upload: 0
|
||||
}
|
||||
this.innerVisible = true
|
||||
},
|
||||
handleEdit (row) {
|
||||
this.dialogTitle = this.key('edit')
|
||||
this.formData = {
|
||||
id: row.id,
|
||||
code: row.code,
|
||||
name: row.name,
|
||||
field_type: row.field_type,
|
||||
remark: row.remark || '',
|
||||
is_only: row.is_only || 0,
|
||||
is_upload: row.is_upload || 0
|
||||
}
|
||||
this.innerVisible = true
|
||||
},
|
||||
handleDelete (row) {
|
||||
this.$confirm(this.key('confirm_delete_data'), this.key('tips'), {
|
||||
confirmButtonText: this.key('confirm'),
|
||||
cancelButtonText: this.key('cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
deleteOptionalParams({ workingsubclass_id: this.workingsubclass_id, id: row.id }).then(() => {
|
||||
this.$message.success(this.key('operation_success'))
|
||||
this.fetchData()
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
handleSubmit () {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (!valid) return
|
||||
const action = this.formData.id ? editOptionalParams : createOptionalParams
|
||||
const data = { workingsubclass_id: this.workingsubclass_id, ...this.formData }
|
||||
if (this.formData.id) {
|
||||
delete data.id
|
||||
}
|
||||
action(data).then(() => {
|
||||
this.$message.success(this.key('operation_success'))
|
||||
this.innerVisible = false
|
||||
this.$refs.form.resetFields()
|
||||
this.fetchData()
|
||||
})
|
||||
})
|
||||
},
|
||||
handleImport () {
|
||||
this.$message.info(this.key('import_tip'))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-content {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.box-card {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:append-to-body="true"
|
||||
:close-on-click-modal="false"
|
||||
:before-close="handleClose"
|
||||
:width="width"
|
||||
>
|
||||
<div class="setting-content">
|
||||
<el-alert
|
||||
:title="settingTip"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<div class="setting-placeholder">
|
||||
<p>{{ $t(key('setting_placeholder')) }}</p>
|
||||
<el-tag type="warning">{{ type }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">{{ $t(key('cancel')) }}</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">{{ $t(key('confirm')) }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { i18nMixin } from '@/composables/useI18n'
|
||||
|
||||
export default {
|
||||
name: 'ProcessStepSettingDialog',
|
||||
mixins: [i18nMixin('page.production_master_data.process_model.process_step')],
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
code: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '50%'
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
pluginData: {
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
settingTip () {
|
||||
return this.$t(this.key('setting_tip'))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose () {
|
||||
this.$emit('close')
|
||||
},
|
||||
handleSubmit () {
|
||||
this.$message.success(this.key('operation_success'))
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.setting-content {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.setting-placeholder {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
color: #909399;
|
||||
}
|
||||
.setting-placeholder p {
|
||||
margin-bottom: 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<d2-container>
|
||||
<template #header>
|
||||
<div class="search-bar">
|
||||
<el-form :inline="true" size="mini">
|
||||
<el-form-item :label="$t(key('process_unit_code'))">
|
||||
<el-input
|
||||
v-model="search.code"
|
||||
:placeholder="$t(key('enter_process_unit_code'))"
|
||||
clearable
|
||||
style="width:200px"
|
||||
@keyup.enter.native="onSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t(key('process_unit_name'))">
|
||||
<el-input
|
||||
v-model="search.name"
|
||||
:placeholder="$t(key('enter_process_unit_name'))"
|
||||
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"
|
||||
help-url="/help/process-step"
|
||||
:help-text="$t(ckey('help'))"
|
||||
auto-height
|
||||
@page-change="onPageChange"
|
||||
@selection-change="onSelect"
|
||||
/>
|
||||
|
||||
<page-dialog-form
|
||||
ref="dialogForm"
|
||||
:visible.sync="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
:width="'35%'"
|
||||
:form-cols="formCols"
|
||||
:form-data="formData"
|
||||
:rules="rules"
|
||||
:label-width="'120px'"
|
||||
:submitting="submitting"
|
||||
:confirm-text="key('confirm')"
|
||||
:cancel-text="key('cancel')"
|
||||
@submit="onDialogSubmit"
|
||||
@close="onDialogClose"
|
||||
/>
|
||||
|
||||
<technology-flow-model
|
||||
:visible.sync="settingVisible"
|
||||
:title="settingTitle"
|
||||
:type="settingType"
|
||||
:code="settingCode"
|
||||
:width="settingWidth"
|
||||
:plugin-data="settingPluginData"
|
||||
@close="settingVisible = false"
|
||||
@submit="handleSettingSubmit"
|
||||
/>
|
||||
|
||||
<result-param
|
||||
:visible.sync="resultVisible"
|
||||
:title="resultTitle"
|
||||
:workingsubclass-id="currentRowId"
|
||||
@close="resultVisible = false"
|
||||
/>
|
||||
</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 {
|
||||
getProcessStepList,
|
||||
createProcessStep,
|
||||
editProcessStep,
|
||||
deleteProcessStep
|
||||
} from '@/api/production-master-data/process-step'
|
||||
import PageTable from '@/components/page-table'
|
||||
import PageDialogForm from '@/components/page-dialog-form'
|
||||
import TechnologyFlowModel from './components/technology-flow-model.vue'
|
||||
import ResultParam from './components/result-param.vue'
|
||||
|
||||
export default {
|
||||
name: 'production-master-data-process-step',
|
||||
components: { PageTable, PageDialogForm, TechnologyFlowModel, ResultParam },
|
||||
mixins: [i18nMixin('page.production_master_data.process_model.process_step'), confirmMixin],
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
submitting: false,
|
||||
tableData: [],
|
||||
selectedRows: [],
|
||||
dialogVisible: false,
|
||||
dialogTitle: '',
|
||||
editId: '',
|
||||
handleType: 'create',
|
||||
search: { code: '', name: '' },
|
||||
pagination: { current: 1, size: 10, total: 0 },
|
||||
formData: { code: '', name: '', device_category_id: '', remark: '' },
|
||||
settingVisible: false,
|
||||
settingTitle: '',
|
||||
settingType: '',
|
||||
settingCode: '',
|
||||
settingWidth: '50%',
|
||||
settingPluginData: '',
|
||||
resultVisible: false,
|
||||
resultTitle: '',
|
||||
currentRowId: '',
|
||||
rules: {
|
||||
code: [
|
||||
{ required: true, message: this.key('enter_process_unit_code'), trigger: 'blur' },
|
||||
{ min: 1, max: 100, message: this.key('length_1_100'), trigger: 'blur' }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: this.key('enter_process_unit_name'), trigger: 'blur' },
|
||||
{ min: 1, max: 100, message: this.key('length_1_100'), trigger: 'blur' }
|
||||
],
|
||||
device_category_id: [
|
||||
{ required: true, message: this.key('select_device_category'), trigger: 'change' }
|
||||
]
|
||||
},
|
||||
columns: [],
|
||||
toolbarButtons: [],
|
||||
rowButtons: [],
|
||||
formCols: [
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'code',
|
||||
label: this.key('process_unit_code'),
|
||||
placeholder: this.key('enter_process_unit_code'),
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'name',
|
||||
label: this.key('process_unit_name'),
|
||||
placeholder: this.key('enter_process_unit_name'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'select',
|
||||
prop: 'device_category_id',
|
||||
label: this.key('device_category'),
|
||||
placeholder: this.key('select_device_category'),
|
||||
clearable: true,
|
||||
filterable: true,
|
||||
style: { width: '90%' },
|
||||
options: []
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'remark',
|
||||
inputType: 'textarea',
|
||||
autosize: { minRows: 2, maxRows: 6 },
|
||||
label: this.key('remark'),
|
||||
placeholder: this.key('remark_required'),
|
||||
clearable: true,
|
||||
style: { width: '90%' }
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.columns = useTableColumns([
|
||||
{ prop: 'sort', label: this.key('sort'), width: 80 },
|
||||
{ prop: 'code', label: this.key('process_unit_code'), minWidth: 120 },
|
||||
{ prop: 'name', label: this.key('process_unit_name'), minWidth: 120 },
|
||||
{ prop: 'device_category_name', label: this.key('device_category'), minWidth: 120 },
|
||||
{ prop: 'remark', label: this.key('remark') },
|
||||
{ prop: '_actions', label: this.key('operation'), width: 160, fixed: 'right' }
|
||||
])
|
||||
const btns = useTableButtons({
|
||||
toolbar: [
|
||||
{
|
||||
key: 'add',
|
||||
label: this.key('add'),
|
||||
icon: 'el-icon-plus',
|
||||
type: 'primary',
|
||||
auth: '/production_configuration/technology_model/technology_flow_workingsubclass/create',
|
||||
onClick: this.openAdd
|
||||
}
|
||||
],
|
||||
row: [
|
||||
{
|
||||
key: 'edit',
|
||||
label: this.key('edit'),
|
||||
icon: 'el-icon-edit',
|
||||
auth: '/production_configuration/technology_model/technology_flow_workingsubclass/edit',
|
||||
onClick: this.openEdit
|
||||
},
|
||||
{
|
||||
key: 'setting',
|
||||
label: this.key('preset_setting'),
|
||||
icon: 'el-icon-setting',
|
||||
color: 'warning',
|
||||
auth: '/production_configuration/technology_model/technology_flow_workingsubclass/setting',
|
||||
onClick: this.openSetting
|
||||
},
|
||||
{
|
||||
key: 'result',
|
||||
label: this.key('preset_result_param'),
|
||||
icon: 'el-icon-document',
|
||||
color: 'success',
|
||||
auth: '/production_configuration/technology_model/technology_flow_workingsubclass/result',
|
||||
onClick: this.openResult
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: this.key('delete'),
|
||||
icon: 'el-icon-delete',
|
||||
color: 'danger',
|
||||
auth: '/production_configuration/technology_model/technology_flow_workingsubclass/delete',
|
||||
onClick: this.handleDelete
|
||||
}
|
||||
]
|
||||
}, this.$permission)
|
||||
this.toolbarButtons = btns.toolbarButtons
|
||||
this.rowButtons = btns.rowButtons
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData () {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getProcessStepList({
|
||||
...this.search,
|
||||
page_no: this.pagination.current,
|
||||
page_size: this.pagination.size
|
||||
})
|
||||
const list = Array.isArray(res) ? res : (res.data || [])
|
||||
const total = Array.isArray(res) ? res.length : (res.count || 0)
|
||||
this.tableData = list
|
||||
this.pagination.total = total
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
onSearch () {
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
onReset () {
|
||||
this.search = { code: '', name: '' }
|
||||
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 = { code: '', name: '', device_category_id: '', remark: '' }
|
||||
this.editId = ''
|
||||
this.$nextTick(() => {
|
||||
this.formCols[0][0].disabled = false
|
||||
})
|
||||
},
|
||||
openAdd () {
|
||||
this.handleType = 'create'
|
||||
this.dialogTitle = this.key('add_process_unit')
|
||||
this.$nextTick(() => {
|
||||
this.$refs.dialogForm && this.$refs.dialogForm.reset()
|
||||
this.resetForm()
|
||||
this.dialogVisible = true
|
||||
})
|
||||
},
|
||||
openEdit (row) {
|
||||
this.handleType = 'edit'
|
||||
this.dialogTitle = this.key('edit_process_unit')
|
||||
this.editId = row.id
|
||||
this.formData = {
|
||||
code: row.code,
|
||||
name: row.name,
|
||||
device_category_id: row.device_category_id || '',
|
||||
remark: row.remark || ''
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.formCols[0][0].disabled = true
|
||||
})
|
||||
this.dialogVisible = true
|
||||
},
|
||||
async onDialogSubmit () {
|
||||
this.submitting = true
|
||||
try {
|
||||
if (this.handleType === 'create') {
|
||||
await createProcessStep(this.formData)
|
||||
} else {
|
||||
await editProcessStep({ ...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_message'),
|
||||
title: this.key('tips')
|
||||
},
|
||||
() => deleteProcessStep({ 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()
|
||||
},
|
||||
openSetting (row) {
|
||||
this.currentRowId = row.id
|
||||
this.settingTitle = this.key('preset_setting')
|
||||
this.settingType = row.device_category_id || ''
|
||||
this.settingCode = row.code
|
||||
this.settingWidth = '60%'
|
||||
this.settingVisible = true
|
||||
},
|
||||
handleSettingSubmit (data) {
|
||||
this.$message.success(this.$t(this.key('operation_success')))
|
||||
this.settingVisible = false
|
||||
},
|
||||
openResult (row) {
|
||||
this.currentRowId = row.id
|
||||
this.resultTitle = this.key('preset_result_param')
|
||||
this.resultVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-bar {
|
||||
padding: 10px 0;
|
||||
}
|
||||
/deep/ .el-form-item--mini.el-form-item {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user