diff --git a/docs/功能测试流程-异常不良管理.md b/docs/功能测试流程-异常不良管理.md new file mode 100644 index 00000000..6c63d6e0 --- /dev/null +++ b/docs/功能测试流程-异常不良管理.md @@ -0,0 +1,210 @@ +# 功能测试流程文档 ——【异常不良管理】 + +> 本文档为【异常不良管理】功能迁移后的独立测试文档。测试人员请按以下流程逐步执行,对通过的用例在"通过"列打勾(`[x]`),未通过的用例在"问题记录"列描述具体现象(包括错误截图编号、操作步骤、实际/预期差异等)。 + +| 项目名称 | MES-UI(生产主数据 → 产品模型 → 异常不良管理) | +| --- | --- | +| 文档版本 | v1.0 | +| 适用版本 | mes-ui 本次迁移版本 | +| 编写日期 | 2026-06-02 | +| 测试入口 | 菜单:生产主数据 → 产品模型 → 异常不良管理 | +| 关联文件 | [主页面](file:///d:/code/mes/mes-ui/src/views/production-master-data/product-model/product-ng-info/index.vue) · [导入组件](file:///d:/code/mes/mes-ui/src/views/production-master-data/product-model/product-ng-info/components/ImportDialog/index.vue) · [API](file:///d:/code/mes/mes-ui/src/api/production-master-data/product-ng-info.js) · [路由](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js) · [文件工具](file:///d:/code/mes/mes-ui/src/utils/file.js) | + +--- + +## 一、测试环境配置要求 + +| 项 | 要求 | +| --- | --- | +| 后台环境 | Webman + 已部署 `production_configuration/product_model/product_ng_info/` 路由接口;数据库中已创建 `product_ng_info` 与 `device_category` 数据表 | +| 前端环境 | `npm run serve` / `pnpm run serve` 启动 mes-ui 工程;浏览器推荐 Chrome 110+ | +| 登录账号 | 拥有「生产主数据 / 异常不良管理」菜单访问权限,且分配以下权限点:
· `…/product_ng_info/create`(新增 / 导入)
· `…/product_ng_info/edit`(编辑)
· `…/product_ng_info/delete`(删除 / 批量删除)
· `…/product_ng_info/export`(导出) | +| 网络要求 | 前后端网络互通;浏览器可访问 `VUE_APP_API` 环境变量对应域名 | +| 数据准备 | · 至少 3 条设备类别(device_category)数据
· 至少 5 条异常不良类别记录
· 准备一份合法格式的 Excel 导入文件(含 3 行导入数据)和一份缺少列的非法文件 | +| 浏览器工具 | 打开 DevTools(F12)→ Network 与 Console,便于抓取接口与异常 | +| 第三方库 | `xlsx` 已安装(Excel 读写支持),确认 `node_modules/xlsx/` 目录存在 | + +--- + +## 二、测试前置条件 + +1. 已成功登录系统,登录账号具备上述所有权限点。 +2. 侧边栏显示 **生产主数据 → 产品模型 → 异常不良管理** 菜单可点击进入。 +3. 浏览器语言切换为「简体中文」,界面文字全部为中文,无 `key is not defined` 或英文缺失。 +4. 列表默认展示至少 5 条记录(通过 `product_ng_info/list` 接口返回)。 +5. 浏览器缩放比例 100%;分辨率建议 1440×900 及以上。 +6. DevTools 关闭缓存(Network → Disable cache),确保加载最新前端资源。 + +--- + +## 三、测试用例 + +> **字段说明**: +> - 设备类别 `device_category_id`:必选,从设备类别 API 动态加载 +> - 类别 `type`:必选,下拉值「异常(ERR)/不良(NG)」 +> - 异常不良编码 `number`:必填,长度 1~100 +> - 异常不良名称 `explain`:必填,长度 1~100 +> - 备注 `note`:选填,多行文本 +> - 列表展示字段:复选框 / 设备类别 / 异常不良类别 / 异常不良编码 / 异常不良名称 / 备注 / 操作 + +### 3.1 列表展示与加载 + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.1.1 | 进入「异常不良管理」菜单 | 列表正常加载,无 JS 报错;表头依次为:复选框、设备类别、异常不良类别、异常不良编码、异常不良名称、备注、操作 | | [ ] | | +| 3.1.2 | 观察列表加载过程 | 列表加载期间显示 loading 遮罩动画,加载完成后消失 | | [ ] | | +| 3.1.3 | 列表为空时(搜索条件无匹配结果) | 表格区域显示空状态占位(如"暂无数据"),无 JavaScript 报错 | | [ ] | | +| 3.1.4 | 分页组件:跳转到第 2 页 | 请求参数 `page_no=2` 正确,数据刷新 | | [ ] | | +| 3.1.5 | 修改每页条数(如从 10 改为 20) | `page_size=20`,列表重载 | | [ ] | | +| 3.1.6 | 列表行内操作列固定在右侧,横向滚动查看 | 操作列不随滚动条消失,固定可见 | | [ ] | | + +### 3.2 搜索功能 + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.2.1 | 点击「设备类别」下拉框 → 从列表中选择一项 → 点击「查询」 | 列表仅展示所选设备类别的记录 | | [ ] | | +| 3.2.2 | 在「查询类型」下拉框中选择「异常」→ 点击「查询」 | 列表仅展示 `type=ERR` 的记录 | | [ ] | | +| 3.2.3 | 在「查询类型」下拉框中选择「不良」→ 点击「查询」 | 列表仅展示 `type=NG` 的记录 | | [ ] | | +| 3.2.4 | 在「异常不良编码」输入框输入关键字 → 点击「查询」 | 列表仅展示编码包含该关键字的记录 | | [ ] | | +| 3.2.5 | 在「异常不良名称」输入框输入关键字 → 点击「查询」 | 列表仅展示名称包含该关键字的记录 | | [ ] | | +| 3.2.6 | 设备类别 + 类型 + 编码同时填写 → 点击「查询」 | 列表为三条件 AND 过滤结果 | | [ ] | | +| 3.2.7 | 输入查询条件后点击「重置」 | 所有输入框、下拉框恢复初始状态,列表恢复为全量数据 | | [ ] | | +| 3.2.8 | 在搜索输入框中按回车键 | 等同于点击「查询」,触发列表刷新 | | [ ] | | +| 3.2.9 | 设备类别下拉框首次展开时(即 focus 事件) | 自动调用 `/device_category/all` 加载选项;未展开时不再重复请求 | | [ ] | | + +### 3.3 新增 + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.3.1 | 点击工具栏「新增」按钮 | 弹出标题为「新增异常不良类别」的对话框,表单为空 | | [ ] | | +| 3.3.2 | 新增弹框打开时,设备类别下拉框自动加载可选项 | 设备类别下拉列表有数据,与搜索时的下拉来源一致 | | [ ] | | +| 3.3.3 | 不填任何字段,点击「确定」 | 表单校验:设备类别、类别、编码、名称四个字段下方均红字提示必填 | | [ ] | | +| 3.3.4 | 仅选择设备类别与类别,不填编码 → 点击「确定」 | 仅编码字段提示必填;名称字段提示必填 | | [ ] | | +| 3.3.5 | 编码输入 101 字符 → 点击「确定」 | 编码字段下方提示「长度在1到100个字符」 | | [ ] | | +| 3.3.6 | 名称输入 101 字符 → 点击「确定」 | 名称字段下方提示「长度在1到100个字符」 | | [ ] | | +| 3.3.7 | 正常填写:设备类别=任意、类别=异常、编码=ERR-01、名称=设备宕机、备注=生产设备意外停机 → 点击「确定」 | 弹出"操作成功"提示,对话框自动关闭,列表自动刷新并出现该条记录 | | [ ] | | +| 3.3.8 | 新增弹框点击「取消」或右上角 × | 对话框关闭,列表未新增任何记录 | | [ ] | | +| 3.3.9 | 使用无 `product_ng_info/create` 权限的账号访问 | 工具栏不显示「新增」按钮 | | [ ] | | +| 3.3.10 | 后端返回「编码已存在」业务错误 | 页面友好提示错误原因,弹框不关闭(可修正后重试) | | [ ] | | + +### 3.4 编辑 + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.4.1 | 列表行点击「编辑」 | 弹出标题为「编辑异常不良类别」的对话框,表单字段回显该行当前数据 | | [ ] | | +| 3.4.2 | 清空编码后「确定」 | 提示编码必填 | | [ ] | | +| 3.4.3 | 修改名称为新值,备注保留为空 → 「确定」 | 操作成功,列表对应行名称更新,备注为空 | | [ ] | | +| 3.4.4 | 修改编码(与已有数据重复)→ 「确定」 | 后端返回重复提示,页面友好显示不崩溃 | | [ ] | | +| 3.4.5 | 编辑对话中点击「取消」 | 对话框关闭,列表数据未变化 | | [ ] | | +| 3.4.6 | 无 `product_ng_info/edit` 权限的账号 | 行内不显示「编辑」按钮 | | [ ] | | +| 3.4.7 | 两个标签页同时打开同一条编辑 → A 保存后 B 再次保存 | 后端返回最新数据的验证结果,不产生脏写 | | [ ] | | + +### 3.5 删除(行内删除) + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.5.1 | 列表行点击「删除」 | 弹出确认框,提示「确定要执行该操作吗?」 | | [ ] | | +| 3.5.2 | 确认框点击「取消」 | 关闭确认框,数据未删除 | | [ ] | | +| 3.5.3 | 确认框点击「确定」 | 操作成功,列表自动移除该记录 | | [ ] | | +| 3.5.4 | 删除最后一页唯一一条数据 | 列表自动回退到上一页(或空状态),不出现空白页 | | [ ] | | +| 3.5.5 | 删除已被其他业务引用的记录 | 后端返回业务级错误提示,数据保留,页面不崩溃 | | [ ] | | +| 3.5.6 | 无 `product_ng_info/delete` 权限的账号 | 行内不显示「删除」按钮 | | [ ] | | +| 3.5.7 | 网络断开时点击「删除」 | 提示网络异常,数据未被误删 | | [ ] | | + +### 3.6 批量删除 + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.6.1 | 不勾选任何复选框,点击「批量删除」 | 提示"请先选择数据" | | [ ] | | +| 3.6.2 | 勾选 2~3 行记录 → 点击「批量删除」 | 弹出确认框「确定要删除所选异常不良类别吗?」 | | [ ] | | +| 3.6.3 | 确认框点击「确定」 | 选中的记录全部从列表消失,操作成功提示 | | [ ] | | +| 3.6.4 | 确认框点击「取消」 | 数据保留,勾选状态保留 | | [ ] | | +| 3.6.5 | 选中的行中包含已被引用的数据 | 后端返回针对性的业务错误提示,未删除任何数据 | | [ ] | | +| 3.6.6 | 勾选全选(表头复选框)→ 批量删除 | 所有可见页数据被选中并删除 | | [ ] | | + +### 3.7 Excel 导入 + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.7.1 | 点击工具栏「导入」按钮 | 弹出标题为「导入异常不良数据」的对话框,上方显示黄色警告提示 | | [ ] | | +| 3.7.2 | 点击「下载模板」按钮 | 浏览器下载 Excel 文件(文件名为「异常不良数据导入模版.xlsx」),内容包含表头 | | [ ] | | +| 3.7.3 | 点击「选择文件」→ 选择非 Excel 文件(如 .txt) | 提示"上传文件格式错误" | | [ ] | | +| 3.7.4 | 点击「选择文件」→ 选择列缺失的 Excel(缺少"异常不良编码"列) | 提示"文件列缺失: 异常不良编码" | | [ ] | | +| 3.7.5 | 选择合法 Excel 文件(含 3 行数据) | 预览表格展示 3 行数据,列与 Excel 内容一致 | | [ ] | | +| 3.7.6 | 选择合法 Excel 后,再次选择另一个文件 | 上一个文件被替换,预览表格刷新为新文件数据 | | [ ] | | +| 3.7.7 | 预览数据无误后点击「确定」 | 提示"操作成功",弹框关闭,列表刷新,新数据出现在列表中 | | [ ] | | +| 3.7.8 | 未选择文件(预览表格为空)点击「确定」 | 提示"请先导入数据" | | [ ] | | +| 3.7.9 | 导入过程中点击「取消」或关闭弹框 | 操作中断,已读取的预览数据被清空 | | [ ] | | +| 3.7.10 | 无 `product_ng_info/create` 权限的账号 | 工具栏不显示「导入」按钮 | | [ ] | | +| 3.7.11 | 导入的 Excel 中设备类别名称在后端无法匹配 | 后端返回相应错误提示,列表不新增 | | [ ] | | + +### 3.8 Excel 导出 + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.8.1 | 点击工具栏「导出」按钮 | 弹出确认框「确定要导出当前查询结果吗?」 | | [ ] | | +| 3.8.2 | 确认框点击「取消」 | 提示"操作已取消",未触发任何请求 | | [ ] | | +| 3.8.3 | 确认框点击「确定」 | 提示"创建导出任务成功",自动跳转至任务管理页面 | | [ ] | | +| 3.8.4 | 在查询条件中筛选后再点击「导出」 | 后端收到的请求包含当前搜索条件 `device_category_id` / `type` / `number` / `explain` | | [ ] | | +| 3.8.5 | 无 `product_ng_info/export` 权限的账号 | 工具栏不显示「导出」按钮 | | [ ] | | + +### 3.9 权限与国际化 + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.9.1 | 切换为仅有「查询」权限的账号 | 仅能查看列表与查询,无新增/编辑/删除/导入/导出按钮 | | [ ] | | +| 3.9.2 | 切换为中文环境 → 英文环境 → 刷新页面 | 表头、按钮、弹框标题、验证提示语全部为英文 | | [ ] | | +| 3.9.3 | 中文下打开新增弹框 → 切换至英文 | 弹框标题、表单 label 立即切换为英文(无需关闭弹框) | | [ ] | | +| 3.9.4 | 英文环境下切换回中文 | 所有文案恢复中文 | | [ ] | | +| 3.9.5 | 英文下执行新增/编辑/导入功能,成功/失败提示 | 提示语为英文,含义正确 | | [ ] | | + +### 3.10 异常与边界 + +| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 | +| --- | --- | --- | --- | --- | --- | +| 3.10.1 | 后端 `/list` 接口返回 500 错误 | 列表显示空状态或错误提示用 Message 弹窗展示,页面不白屏 | | [ ] | | +| 3.10.2 | 后端返回字段缺失(如某行缺少 `device_category`) | 缺失字段列显示为空,不抛 JS 异常 | | [ ] | | +| 3.10.3 | 备注字段输入 5000 字符并保存 | 提交成功,列表备注列可滚动展示(建议使用省略号截断) | | [ ] | | +| 3.10.4 | 新增/编辑弹框打开后按 ESC 键 | 弹框关闭,表单状态被重置 | | [ ] | | +| 3.10.5 | 列表横向宽度超过浏览器视口 | 操作列固定在右侧,表格支持横向滚动 | | [ ] | | +| 3.10.6 | DevTools Console 全程监控 | 无 `Vue warn`、无未捕获 Promise 异常、无 i18n key 缺失警告 | | [ ] | | +| 3.10.7 | 快速连续点击「新增」→ 「确定」 | 提交按钮 loading 状态阻止重复提交 | | [ ] | | +| 3.10.8 | 设备类别 API 请求失败时展开搜索下拉 | 下拉框为空,页面不崩溃;重试正常 | | [ ] | | + +--- + +## 四、测试结果汇总 + +| 用例总数 | 通过 | 失败 | 阻塞 | 通过率 | +| --- | --- | --- | --- | --- | +| | | | | | + +--- + +## 五、问题记录区 + +| 编号 | 用例编号 | 复现步骤 | 实际结果 | 严重程度 | 处理人 | 状态 | 备注 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 1 | | | | | | | | +| 2 | | | | | | | | +| 3 | | | | | | | | +| 4 | | | | | | | | +| 5 | | | | | | | | + +--- + +## 六、测试结论 + +| 项目 | 结论 | +| --- | --- | +| 功能完整性 | ☐ 满足 ☐ 部分缺失 ☐ 不满足 | +| 性能表现 | ☐ 良好 ☐ 一般 ☐ 差 | +| 权限控制 | ☐ 正确 ☐ 存在漏洞 | +| 国际化 | ☐ 完整 ☐ 部分缺失 ☐ 缺失 | +| 导入导出 | ☐ 正常 ☐ 部分异常 ☐ 不可用 | +| 是否可发布 | ☐ 是 ☐ 否(请说明阻塞问题) | + +测试人员签字:__________________ 日期:__________ + +--- +*本测试流程文档为【异常不良管理】功能迁移版本专用,请独立归档保存。* diff --git a/package.json b/package.json index 1c4c9076..837d14dc 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,8 @@ "vue-router": "^3.6.2", "vue-splitpane": "^1.0.6", "vue-ueditor-wrap": "^2.5.6", - "vuex": "^3.6.2" + "vuex": "^3.6.2", + "xlsx": "^0.18.5" }, "devDependencies": { "@d2-projects/vue-filename-injector": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ce1ab66..4b7d8b29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,6 +116,9 @@ importers: vuex: specifier: ^3.6.2 version: 3.6.2(vue@2.7.16) + xlsx: + specifier: ^0.18.5 + version: 0.18.5 devDependencies: '@d2-projects/vue-filename-injector': specifier: ^1.1.1 @@ -2220,6 +2223,10 @@ packages: engines: {node: '>=0.8'} hasBin: true + codepage@1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + collection-visit@1.0.0: resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} engines: {node: '>=0.10.0'} @@ -6228,6 +6235,10 @@ packages: engines: {node: '>=0.8'} hasBin: true + ssf@0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + sshpk@1.18.0: resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} engines: {node: '>=0.10.0'} @@ -7046,10 +7057,18 @@ packages: engines: {node: '>= 8'} hasBin: true + wmf@1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + word@0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + worker-farm@1.7.0: resolution: {integrity: sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==} @@ -7118,6 +7137,11 @@ packages: engines: {node: '>=0.8'} hasBin: true + xlsx@0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + xml-name-validator@3.0.0: resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} @@ -8180,9 +8204,7 @@ snapshots: source-map: 0.6.1 string-length: 2.0.0 transitivePeerDependencies: - - bufferutil - supports-color - - utf-8-validate '@jest/source-map@24.9.0': dependencies: @@ -10016,6 +10038,8 @@ snapshots: commander: 2.14.1 exit-on-epipe: 1.0.1 + codepage@1.15.0: {} + collection-visit@1.0.0: dependencies: map-visit: 1.0.0 @@ -12437,9 +12461,7 @@ snapshots: pretty-format: 24.9.0 throat: 4.1.0 transitivePeerDependencies: - - bufferutil - supports-color - - utf-8-validate jest-leak-detector@24.9.0: dependencies: @@ -14683,6 +14705,10 @@ snapshots: dependencies: frac: 1.1.2 + ssf@0.11.2: + dependencies: + frac: 1.1.2 + sshpk@1.18.0: dependencies: asn1: 0.2.6 @@ -15789,8 +15815,12 @@ snapshots: dependencies: isexe: 2.0.0 + wmf@1.0.2: {} + word-wrap@1.2.5: {} + word@0.3.0: {} + worker-farm@1.7.0: dependencies: errno: 0.1.8 @@ -15851,6 +15881,16 @@ snapshots: exit-on-epipe: 1.0.1 ssf: 0.10.3 + xlsx@0.18.5: + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + xml-name-validator@3.0.0: {} xmlchars@2.2.0: {} diff --git a/src/api/production-master-data/device-category.js b/src/api/production-master-data/device-category.js new file mode 100644 index 00000000..03f63a9b --- /dev/null +++ b/src/api/production-master-data/device-category.js @@ -0,0 +1,19 @@ +import { request } from '@/api/_service' + +const BASE = 'production_configuration/device_model/device_category/' + +function apiParams (method, data = {}) { + return { + method: `production_configuration_device_model_device_category_${method}`, + platform: 'background', + ...data + } +} + +export function getDeviceCategoryAll (data) { + return request({ + url: BASE + 'all', + method: 'get', + params: apiParams('all', data) + }) +} diff --git a/src/api/production-master-data/product-ng-info.js b/src/api/production-master-data/product-ng-info.js new file mode 100644 index 00000000..581495f3 --- /dev/null +++ b/src/api/production-master-data/product-ng-info.js @@ -0,0 +1,76 @@ +import { request } from '@/api/_service' + +const BASE = 'production_configuration/product_model/product_ng_info/' + +function apiParams (method, data = {}) { + return { + method: `production_configuration_product_model_product_ng_info_${method}`, + platform: 'background', + ...data + } +} + +export function getProductNgInfoAll (data) { + return request({ + url: BASE + 'all', + method: 'get', + params: { ...data } + }) +} + +export function getProductNgInfoList (data) { + return request({ + url: BASE + 'list', + method: 'get', + params: { ...data } + }) +} + +export function createProductNgInfo (data) { + return request({ + url: BASE + 'create', + method: 'post', + data: apiParams('create', data) + }) +} + +export function editProductNgInfo (data) { + return request({ + url: BASE + 'edit', + method: 'put', + data: apiParams('edit', data) + }) +} + +export function deleteProductNgInfo (data) { + return request({ + url: BASE + 'delete', + method: 'delete', + data: apiParams('delete', data) + }) +} + +export function getImportTemplate (data) { + return request({ + url: BASE + 'get_import_template', + method: 'post', + responseType: 'blob', + data: apiParams('get_import_template', data) + }) +} + +export function productNgInfoImport (data) { + return request({ + url: BASE + 'import', + method: 'post', + data: apiParams('import', data) + }) +} + +export function productNgInfoExportTask (data) { + return request({ + url: BASE + 'product_ng_info_export_task', + method: 'post', + data: apiParams('product_ng_info_export_task', data) + }) +} diff --git a/src/locales/en.json b/src/locales/en.json index 965d8ff4..2ecf8e6b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -248,6 +248,60 @@ "help": "Unit is used to maintain material measurement units (e.g. piece, box, kg)" } }, + "product_model": { + "product_ng_info": { + "search": "Search", + "reset": "Reset", + "device_category": "Device Category", + "select_device_category": "Please select device category", + "query_type": "Query Type", + "select_query_type": "Please select query type", + "type_error": "Error", + "type_ng": "NG", + "ng_code": "Error/NG Code", + "enter_ng_code": "Please enter error/NG code", + "ng_name": "Error/NG Name", + "enter_ng_name": "Please enter error/NG name", + "type": "Category", + "select_type": "Please select category", + "exception_ng_category": "Error/NG Category", + "remark": "Remark", + "enter_remark": "Please enter remark", + "remark_length": "Length 1 to 100 characters", + "operation": "Action", + "add": "Add", + "edit": "Edit", + "delete": "Delete", + "batch_delete": "Batch Delete", + "import": "Import", + "export": "Export", + "operation_success": "Operation succeeded", + "add_exception_ng_category": "Add Error/NG Category", + "edit_exception_ng_category": "Edit Error/NG Category", + "cancel": "Cancel", + "confirm": "Confirm", + "prompt": "Prompt", + "confirm_delete": "Are you sure to perform this operation?", + "confirm_batch_delete": "Are you sure to delete the selected categories?", + "please_select_data": "Please select data first", + "validation_fail": "Validation failed", + "export_confirm_tip": "Are you sure to export the current query results?", + "operation_cancelled": "Operation cancelled", + "create_download_task_success": "Export task created successfully", + "import_exception_ng_data": "Import Error/NG Data", + "import_file_format_tip": "Please import file in template format", + "import_table": "Import Table", + "select_file": "Select File", + "download_template": "Download Template", + "preview": "Preview", + "import_template_name": "Error/NG data import template", + "file_not_exist": "File does not exist", + "upload_format_error": "Upload file format error", + "file_column_missing": "File column missing: {title}", + "please_import_data": "Please import data first", + "help": "Error/NG management is used to maintain equipment error types and product NG types" + } + }, "product_management": { "product_list": { "search": "Search", diff --git a/src/locales/zh-chs.json b/src/locales/zh-chs.json index 948fcfe3..fa2a29af 100644 --- a/src/locales/zh-chs.json +++ b/src/locales/zh-chs.json @@ -248,6 +248,60 @@ "help": "计量单位用于维护物料所用单位(如个、箱、kg)" } }, + "product_model": { + "product_ng_info": { + "search": "查询", + "reset": "重置", + "device_category": "设备类别", + "select_device_category": "请选择设备类别", + "query_type": "查询类型", + "select_query_type": "请选择查询类型", + "type_error": "异常", + "type_ng": "不良", + "ng_code": "异常不良编码", + "enter_ng_code": "请输入异常不良编码", + "ng_name": "异常不良名称", + "enter_ng_name": "请输入异常不良名称", + "type": "类别", + "select_type": "请选择类别", + "exception_ng_category": "异常不良类别", + "remark": "备注", + "enter_remark": "请输入备注", + "remark_length": "长度在1到100个字符", + "operation": "操作", + "add": "新 增", + "edit": "编 辑", + "delete": "删 除", + "batch_delete": "批量删除", + "import": "导 入", + "export": "导 出", + "operation_success": "操作成功", + "add_exception_ng_category": "新增异常不良类别", + "edit_exception_ng_category": "编辑异常不良类别", + "cancel": "取消", + "confirm": "确定", + "prompt": "提示", + "confirm_delete": "确定要执行该操作吗?", + "confirm_batch_delete": "确定要删除所选异常不良类别吗?", + "please_select_data": "请先选择数据", + "validation_fail": "校验失败", + "export_confirm_tip": "确定要导出当前查询结果吗?", + "operation_cancelled": "操作已取消", + "create_download_task_success": "创建导出任务成功", + "import_exception_ng_data": "导入异常不良数据", + "import_file_format_tip": "请按模板格式导入文件", + "import_table": "导入列表", + "select_file": "选择文件", + "download_template": "下载模板", + "preview": "预览", + "import_template_name": "异常不良数据导入模版", + "file_not_exist": "文件不存在", + "upload_format_error": "上传文件格式错误", + "file_column_missing": "文件列缺失: {title}", + "please_import_data": "请先导入数据", + "help": "异常不良管理用于维护设备的异常种类和产品的不良种类信息" + } + }, "product_management": { "product_list": { "search": "查询", diff --git a/src/router/modules/production-master-data.js b/src/router/modules/production-master-data.js index d89636d8..d5a3bd87 100644 --- a/src/router/modules/production-master-data.js +++ b/src/router/modules/production-master-data.js @@ -61,6 +61,12 @@ export default { name: `${pre}material_model-material_unit`, meta: { ...meta, cache: true, title: '计量单位' }, component: _import('production-master-data/material-model/material-unit') + }, + { + path: 'product_model/product_ng_info', + name: `${pre}product_model-product_ng_info`, + meta: { ...meta, cache: true, title: '异常不良管理' }, + component: _import('production-master-data/product-model/product-ng-info') } ])('production_configuration-') } diff --git a/src/utils/file.js b/src/utils/file.js new file mode 100644 index 00000000..493e2680 --- /dev/null +++ b/src/utils/file.js @@ -0,0 +1,41 @@ +import * as XLSX from 'xlsx' + +export function downloadRename (blob, fileType, filename) { + const typesMap = { + xlsx: 'application/vnd.ms-excel', + xls: 'application/vnd.ms-excel', + pdf: 'application/pdf', + doc: 'application/msword', + docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + csv: 'text/csv' + } + const type = typesMap[fileType] || '' + const newBlob = new Blob([blob], { type }) + const href = URL.createObjectURL(newBlob) + const a = document.createElement('a') + a.href = href + a.download = filename + '.' + fileType + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(href) +} + +export function readExcel (file) { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.readAsBinaryString(file) + reader.onload = ev => { + try { + const data = ev.target.result + const workbook = XLSX.read(data, { type: 'binary' }) + const firstSheet = workbook.Sheets[workbook.SheetNames[0]] + const list = XLSX.utils.sheet_to_json(firstSheet) + resolve(list) + } catch (e) { + reject(e) + } + } + reader.onerror = () => reject(new Error('文件读取失败')) + }) +} diff --git a/src/views/production-master-data/product-model/product-ng-info/components/ImportDialog/index.vue b/src/views/production-master-data/product-model/product-ng-info/components/ImportDialog/index.vue new file mode 100644 index 00000000..8c0c1b22 --- /dev/null +++ b/src/views/production-master-data/product-model/product-ng-info/components/ImportDialog/index.vue @@ -0,0 +1,175 @@ + + + diff --git a/src/views/production-master-data/product-model/product-ng-info/index.vue b/src/views/production-master-data/product-model/product-ng-info/index.vue new file mode 100644 index 00000000..05887f05 --- /dev/null +++ b/src/views/production-master-data/product-model/product-ng-info/index.vue @@ -0,0 +1,459 @@ + + + + +