新增工艺流程参数与补偿配置
Some checks failed
Release pipeline / Always run job (push) Has been cancelled
Release pipeline / publish (push) Has been cancelled

This commit is contained in:
sheng
2026-06-22 22:44:45 +08:00
parent c1e8626289
commit 7cf7caf31f
12 changed files with 1236 additions and 26 deletions

View File

@@ -0,0 +1,140 @@
# 功能测试流程文档 ——【工艺流程】
> 本文档为【工艺流程】功能补齐后的独立测试文档。测试人员请按以下流程逐步执行,对通过的用例在“通过”列打勾(`[x]`),未通过的用例在“问题记录”列描述具体现象。
| 项目名称 | MES-UI生产主数据 → 工艺模型 → 工艺流程) |
| --- | --- |
| 文档版本 | v1.0 |
| 适用版本 | mes-ui 本次迁移版本 |
| 编写日期 | 2026-06-22 |
| 测试入口 | 菜单:生产主数据 → 工艺模型 → 工艺流程 |
| 关联文件 | `src/views/production-master-data/process-model/process-routing/index.vue``src/views/production-master-data/process-model/process-routing-card/index.vue``src/api/production-master-data/process-routing-card.js``src/api/production-master-data/calculation-script.js` |
---
## 一、测试环境配置要求
| 项 | 要求 |
| --- | --- |
| 后台环境 | Webman 已部署 `production_configuration/technology_model/technology_flow``technology_flow_process``calculation_script` 相关接口 |
| 前端环境 | 使用 Node 18.16.0,执行 `pnpm run serve` 启动 V2 项目 |
| 登录账号 | 拥有工艺流程、流程卡、设定值、结果参数、温度补偿、计算脚本相关权限点 |
| 数据准备 | 至少 1 个流程类别、1 个产品型号、多个工序单元;至少 1 个未绑定批次的工艺流程 |
| 浏览器工具 | 打开 DevTools → Network 与 Console便于核对接口参数和异常 |
---
## 二、测试前置条件
1. 已成功登录系统,侧边栏可进入 **生产主数据 → 工艺模型 → 工艺流程**
2. 工艺流程列表能正常加载,且存在可进入“设置工序”的流程。
3. 测试账号拥有以下权限点:新增、编辑、删除、复制、设置工序、排序、设定值、结果参数、温度补偿、计算脚本、查看日志。
4. 浏览器语言分别切换为简体中文 / English 时页面无 i18n key 缺失。
---
## 三、测试用例
### 3.1 工艺流程主列表
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.1.1 | 进入“工艺流程”菜单 | 列表加载成功,展示编码、名称、流程类别、产品、备注、创建人、创建时间、操作 | | [ ] | |
| 3.1.2 | 按编码、名称、流程类别、产品分别查询 | 请求参数正确,列表按条件刷新 | | [ ] | |
| 3.1.3 | 点击重置 | 查询条件清空,列表恢复第一页 | | [ ] | |
| 3.1.4 | 新增工艺流程并保存 | 调用 `technology_flow/create`,保存成功后列表刷新 | | [ ] | |
| 3.1.5 | 编辑工艺流程 | 表单回显正确,编码禁用,保存后列表更新 | | [ ] | |
| 3.1.6 | 复制工艺流程 | 弹出确认框;确认后调用 `technology_flow/copy` 并刷新列表 | | [ ] | |
| 3.1.7 | 删除未绑定批次流程 | 弹出确认框;确认后调用删除接口并刷新 | | [ ] | |
| 3.1.8 | 删除已绑定批次流程 | 页面提示已绑定批次不可删除,不发删除请求 | | [ ] | |
### 3.2 流程卡工序维护
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.2.1 | 点击列表行“设置工序” | 进入流程卡页面,展示工艺信息和工序明细 | | [ ] | |
| 3.2.2 | 点击“新增”选择工序单元 | 自动生成工序编码/名称,并按工序单元编码自动设置通道检测 | | [ ] | |
| 3.2.3 | 新增工序保存 | 调用 `technology_flow_process/create`,参数包含 `flow_id``workingsubclass_id``code``name``pin_check` | | [ ] | |
| 3.2.4 | 编辑工序 | 工序单元、编码、名称禁用,通道检测可回显;保存调用 `edit` | | [ ] | |
| 3.2.5 | 删除工序 | 调用 `technology_flow_process/delete`,参数包含 `id: []``flow_id` | | [ ] | |
| 3.2.6 | 上移/下移工序 | 调用 `move_up` / `move_down`,排序刷新正确 | | [ ] | |
| 3.2.7 | 已绑定批次流程 | 新增、删除、排序被限制或提示不可操作 | | [ ] | |
### 3.3 设定值
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.3.1 | 点击有 `setting_plugin` 的工序“设定值” | 弹出设定值窗口,显示插件类型、工序编码和现有 JSON | | [ ] | |
| 3.3.2 | 输入合法 JSON 并保存 | 调用 `technology_flow_process/set_setting``setting` 为 JSON 字符串 | | [ ] | |
| 3.3.3 | 输入非法 JSON | 页面提示 JSON 格式不正确,不提交接口 | | [ ] | |
### 3.4 结果参数
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.4.1 | 点击任一工序“结果参数” | 弹窗按当前 `flow_process_id` 调用 `get_optional_params_details` | | [ ] | |
| 3.4.2 | 按名称/参数查询 | 请求包含 `process_id``name``code`,列表刷新 | | [ ] | |
| 3.4.3 | 点击“添加结果参数” | 调用 `get_all_workingsubclass_params`,左侧显示全部参数,右侧显示已选参数 | | [ ] | |
| 3.4.4 | 勾选参数并同步到右侧后提交 | 调用 `add_optional_params``optional_params` 为 JSON 字符串,列表刷新 | | [ ] | |
| 3.4.5 | 未变更直接提交 | 页面提示请选择结果参数,不提交保存 | | [ ] | |
### 3.5 温度补偿
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.5.1 | 对化成/分容类工序点击“温度补偿” | 打开抽屉,调用 `get_step``get_temperature_list` | | [ ] | |
| 3.5.2 | 选择开始/结束工步 | 开始大于结束时出现提示 | | [ ] | |
| 3.5.3 | 新增温度补偿行并保存 | 调用 `create_temperature`,包含 `process_id`、工步范围、`temp_data` | | [ ] | |
| 3.5.4 | 输入非数字温度或补偿值 | 页面提示必须为数字,不提交 | | [ ] | |
| 3.5.5 | 输入重复温度 | 页面提示重复温度,不提交 | | [ ] | |
| 3.5.6 | 下载模板 | 调用 `get_temperature_template`,浏览器下载 xlsx 文件 | | [ ] | |
| 3.5.7 | 导入含“温度 / 温度补偿值”列的 Excel | 数据追加到表格并按温度升序排列 | | [ ] | |
### 3.6 计算脚本
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.6.1 | 点击工序“计算脚本” | 打开弹窗并调用 `calculation_script/all` | | [ ] | |
| 3.6.2 | 新增计算脚本 | 必填编码、名称、接口、接口位置、脚本内容;保存调用 `create` | | [ ] | |
| 3.6.3 | 编辑计算脚本 | 表单和脚本内容回显,保存调用 `edit` | | [ ] | |
| 3.6.4 | 删除计算脚本 | 弹出确认框,确认后调用 `delete` 并刷新列表 | | [ ] | |
| 3.6.5 | 脚本内容为空时保存 | 页面提示请输入脚本内容,不提交 | | [ ] | |
### 3.7 日志、权限与异常
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
| --- | --- | --- | --- | --- | --- |
| 3.7.1 | 点击“查看日志” | 打开日志抽屉并展示 `technology_flow_operate_log` | | [ ] | |
| 3.7.2 | 使用只读账号访问 | 新增/编辑/删除/排序/插件按钮按权限隐藏 | | [ ] | |
| 3.7.3 | 切换英文 | 主列表、流程卡、结果参数、温度补偿、计算脚本文案显示英文 | | [ ] | |
| 3.7.4 | 后端返回 500 或字段缺失 | 页面不白屏Console 无未捕获异常 | | [ ] | |
---
## 四、测试结果汇总
| 用例总数 | 通过 | 失败 | 阻塞 | 通过率 |
| --- | --- | --- | --- | --- |
| | | | | |
---
## 五、问题记录区
| 编号 | 用例编号 | 复现步骤 | 实际结果 | 严重程度 | 处理人 | 状态 | 备注 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | | | | | | | |
| 2 | | | | | | | | |
---
## 六、测试结论
| 项目 | 结论 |
| --- | --- |
| 功能完整性 | ☐ 满足 ☐ 部分缺失 ☐ 不满足 |
| 权限控制 | ☐ 正确 ☐ 存在漏洞 |
| 国际化 | ☐ 完整 ☐ 部分缺失 ☐ 缺失 |
| 是否可发布 | ☐ 是 ☐ 否(请说明阻塞问题) |
测试人员签字__________________ 日期__________

View File

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

View File

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

25
docs/运行提示词.md Normal file
View File

@@ -0,0 +1,25 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD> Ǩ<><C7A8><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE>.md <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>˵<EFBFBD><CBB5>.md <20><> <20><>̨Webman<61><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD>ձ<EFBFBD>.md <20>е<EFBFBD><D0B5><EFBFBD>Ϣ<EFBFBD><CFA2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD>С<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ<EFBFBD><CFA2><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>Ǩ<EFBFBD>ƹ<EFBFBD><C6B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ǩ<EFBFBD>ƺ<EFBFBD><C6BA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>ϸ<EFBFBD>Ĺ<EFBFBD><C4B9>ܲ<EFBFBD><DCB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ա<EFBFBD>IJ<EFBFBD><C4B2><EFBFBD>ָ<EFBFBD>ϡ<EFBFBD><CFA1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ա<EFBFBD><EFBFBD><E8B0B4><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD>в<EFBFBD><D0B2>ԣ<EFBFBD><D4A3>Բ<EFBFBD><D4B2><EFBFBD>ͨ<EFBFBD><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD>й<EFBFBD>ѡ<EFBFBD><D1A1><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD>δͨ<CEB4><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF>¼<EFBFBD><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD>ҪҪ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
1. <20>˲<EFBFBD><CBB2><EFBFBD><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><C4B5>ķ<EFBFBD>ʽ<EFBFBD><CABD>ÿ<EFBFBD><C3BF><EFBFBD><EFBFBD><EFBFBD>ɹ<EFBFBD><C9B9><EFBFBD>Ǩ<EFBFBD>ƺ󣬽<C6BA><F3A3ACBD>¹<EFBFBD><C2B9>ܵIJ<DCB5><C4B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݷŵ<DDB7>ÿ<EFBFBD><C3BF><EFBFBD><EFBFBD><EFBFBD>ܵ<EFBFBD><DCB5><EFBFBD><EFBFBD>ĵ<EFBFBD><C4B5>
2. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݱ<EFBFBD><DDB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸҪ<CFB8>أ<EFBFBD>
- <20><><EFBFBD>Ի<EFBFBD><D4BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA>
- <20><><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
- <20><>ϸ<EFBFBD>IJ<EFBFBD><C4B2>Բ<EFBFBD><D4B2>裨ÿ<E8A3A8><C3BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȷ˵<C8B7><CBB5><EFBFBD><EFBFBD>
- Ԥ<>ڽ<EFBFBD><DABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E5A1A2><EFBFBD><EFBFBD>֤<EFBFBD><D6A4>
- ʵ<>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><C2BC><EFBFBD><EFBFBD>
- <20><><EFBFBD><EFBFBD>ͨ<EFBFBD><CDA8><><CAA7>״̬<D7B4><CCAC><EFBFBD><EFBFBD>
- <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><C2BC><EFBFBD><EFBFBD>
3. ȷ<><C8B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD≯<EFBFBD><CCB8><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܵ<EFBFBD><DCB5><EFBFBD><EFBFBD>к<EFBFBD><D0BA>IJ<EFBFBD><C4B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD><E0BCAD>ɾ<EFBFBD><C9BE><EFBFBD><EFBFBD>Ȩ<EFBFBD>޷<EFBFBD><DEB7><EFBFBD><EFBFBD><EFBFBD>
4. <20>ĵ<EFBFBD><C4B5><EFBFBD>ʽӦ<CABD><D3A6><EFBFBD><EFBFBD><EFBFBD>׶<EFBFBD><D7B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڲ<EFBFBD><DAB2><EFBFBD><EFBFBD><EFBFBD>Ա<EFBFBD><D4B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD>в<EFBFBD><D0B2><EFBFBD>¼<EFBFBD><C2BC><EFBFBD><EFBFBD>

View File

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

View File

@@ -65,3 +65,68 @@ export function moveDown (data) {
data: apiParams('move_down', data)
})
}
export function getOptionalParamsDetails (data) {
return request({
url: BASE + 'get_optional_params_details',
method: 'get',
params: apiParams('get_optional_params_details', data)
})
}
export function getOptionalAllParamsDetails (data) {
return request({
url: BASE + 'get_optional_all_params_details',
method: 'get',
params: apiParams('get_optional_all_params_details', data)
})
}
export function getStep (data) {
return request({
url: BASE + 'get_step',
method: 'get',
params: apiParams('get_step', data)
})
}
export function getTemperatureList (data) {
return request({
url: BASE + 'get_temperature_list',
method: 'get',
params: apiParams('get_temperature_list', data)
})
}
export function createTemperature (data) {
return request({
url: BASE + 'create_temperature',
method: 'post',
data: apiParams('create_temperature', data)
})
}
export function getTemperatureTemplate (data) {
return request({
url: BASE + 'get_temperature_template',
method: 'post',
responseType: 'blob',
data: apiParams('get_temperature_template', data)
})
}
export function getAllWorkingsubclassParams (data) {
return request({
url: BASE + 'get_all_workingsubclass_params',
method: 'get',
params: apiParams('get_all_workingsubclass_params', data)
})
}
export function addOptionalParams (data) {
return request({
url: BASE + 'add_optional_params',
method: 'post',
data: apiParams('add_optional_params', data)
})
}

View File

@@ -225,7 +225,69 @@
"length_1_100": "Length must be 1-100 characters",
"batch_bound_no_move": "Batch bound, cannot move",
"select_category": "Please select a category",
"log_operation_step": "Operation step: "
"log_operation_step": "Operation step: ",
"search": "Search",
"query": "Query",
"close": "Close",
"submit": "Submit",
"add": "Add",
"remove": "Remove",
"save": "Save",
"edit": "Edit",
"name": "Name",
"param": "Parameter",
"remark": "Remark",
"enter_name": "Please enter name",
"enter_param": "Please enter parameter",
"enter_code": "Please enter code",
"enter_remark": "Please enter remark",
"add_result_param": "Add Result Params",
"result_param_bind_tip": "Select result parameters to bind to the current process step.",
"all_result_params": "All Result Params",
"selected_result_params": "Selected Result Params",
"please_select_param": "Please select result parameters",
"temperature_step_range": "Select Temperature Compensation Step Range",
"start_step": "Start Step",
"end_step": "End Step",
"select_start_step": "Please select start step",
"select_end_step": "Please select end step",
"temperature_data": "Temperature Compensation Data",
"import_data": "Import Data",
"temperature_value": "Temperature",
"compensation_value": "Compensation Value",
"import_temperature_data": "Import Temperature Data",
"drag_file_here": "Drag file here, or ",
"click_to_upload": "click to upload",
"download_template": "Download Template",
"start_step_greater_than_end": "Start step cannot be greater than end step",
"select_at_least_one": "Please select at least one row",
"select_start_step_required": "Please select start step",
"select_end_step_required": "Please select end step",
"temperature_required": "Temperature is required",
"compensation_required": "Compensation value is required",
"temperature_must_be_numeric": "Temperature must be numeric",
"compensation_must_be_numeric": "Compensation value must be numeric",
"duplicate_temperature_exists": "Duplicate temperature exists",
"temperature_template_name": "Temperature Compensation Import Template",
"invalid_upload_format": "Please upload xls or xlsx file",
"temperature_import_columns_required": "File must contain Temperature and Compensation columns",
"add_calculation_script": "Add Calculation Script",
"edit_calculation_script": "Edit Calculation Script",
"script_code": "Script Code",
"script_name": "Script Name",
"interface_name": "Interface Name",
"interface_position": "Interface Position",
"start_position": "Start Position",
"end_position": "End Position",
"status": "Status",
"enabled": "Enabled",
"disabled": "Disabled",
"select_interface": "Please select interface",
"select_interface_position": "Please select interface position",
"script_content": "Script Content",
"script_content_required": "Please enter script content",
"length_1_45": "Length must be 1-45 characters",
"confirm_delete_script": "Are you sure to delete this calculation script?"
}
}
},

View File

@@ -191,7 +191,7 @@
"card": {
"flow_info": "工艺信息",
"flow_name": "流程名称",
"category": "流程类别",
"category": "类别",
"product": "产品型号",
"batch_bound": "绑定批次",
"yes": "是",
@@ -225,7 +225,69 @@
"length_1_100": "长度在 1 到 100 个字符",
"batch_bound_no_move": "已绑定批次,不可移动",
"select_category": "请选择流程类别",
"log_operation_step": "操作工序:"
"log_operation_step": "操作工序:",
"search": "搜索",
"query": "查询",
"close": "关闭",
"submit": "提交",
"add": "新增",
"remove": "移除",
"save": "保存",
"edit": "编辑",
"name": "名称",
"param": "参数",
"remark": "备注",
"enter_name": "请输入名称",
"enter_param": "请输入参数",
"enter_code": "请输入编码",
"enter_remark": "请输入备注",
"add_result_param": "添加结果参数",
"result_param_bind_tip": "请选择需要绑定到当前流程工序的结果参数。",
"all_result_params": "全部结果参数",
"selected_result_params": "已选结果参数",
"please_select_param": "请选择结果参数",
"temperature_step_range": "选择温度补偿工步范围",
"start_step": "开始工步",
"end_step": "结束工步",
"select_start_step": "请选择开始工步",
"select_end_step": "请选择结束工步",
"temperature_data": "录入温度补偿数据",
"import_data": "导入数据",
"temperature_value": "温度",
"compensation_value": "温度补偿值",
"import_temperature_data": "导入温度补偿数据",
"drag_file_here": "将文件拖到此处,或",
"click_to_upload": "点击上传",
"download_template": "下载模板",
"start_step_greater_than_end": "开始工步不能大于结束工步",
"select_at_least_one": "请至少选择一条数据",
"select_start_step_required": "请选择开始工步",
"select_end_step_required": "请选择结束工步",
"temperature_required": "温度不能为空",
"compensation_required": "温度补偿值不能为空",
"temperature_must_be_numeric": "温度必须为数字",
"compensation_must_be_numeric": "温度补偿值必须为数字",
"duplicate_temperature_exists": "存在重复温度值,请检查",
"temperature_template_name": "温度补偿导入模板",
"invalid_upload_format": "请上传 xls 或 xlsx 文件",
"temperature_import_columns_required": "文件必须包含【温度】和【温度补偿值】两列",
"add_calculation_script": "新增计算脚本",
"edit_calculation_script": "编辑计算脚本",
"script_code": "脚本编码",
"script_name": "脚本名称",
"interface_name": "接口名称",
"interface_position": "接口位置",
"start_position": "开始位置",
"end_position": "结束位置",
"status": "状态",
"enabled": "启用",
"disabled": "禁用",
"select_interface": "请选择接口",
"select_interface_position": "请选择接口位置",
"script_content": "脚本内容",
"script_content_required": "请输入脚本内容",
"length_1_45": "长度在 1 到 45 个字符",
"confirm_delete_script": "确定要删除该计算脚本吗?"
}
}
},

View File

@@ -0,0 +1,235 @@
<template>
<el-dialog :title="$t(title)" :visible.sync="visible" append-to-body :close-on-click-modal="false" width="70%" :before-close="handleClose">
<div class="toolbar">
<el-button type="primary" size="mini" icon="el-icon-plus" @click="openAdd">
{{ $t(key('add_calculation_script')) }}
</el-button>
</div>
<el-table :data="tableData" border height="480" v-loading="loading">
<el-table-column prop="id" label="ID" width="70" />
<el-table-column prop="code" :label="$t(key('script_code'))" min-width="130" show-overflow-tooltip />
<el-table-column prop="name" :label="$t(key('script_name'))" min-width="150" show-overflow-tooltip />
<el-table-column prop="interface_code" :label="$t(key('interface_name'))" min-width="220" show-overflow-tooltip />
<el-table-column :label="$t(key('interface_position'))" width="130">
<template slot-scope="{ row }">
{{ row.interface_position === 'begin' ? $t(key('start_position')) : $t(key('end_position')) }}
</template>
</el-table-column>
<el-table-column :label="$t(key('status'))" width="110">
<template slot-scope="{ row }">
<span v-if="Number(row.status) === 1" class="status-on"><i class="el-icon-circle-check" /> {{ $t(key('enabled')) }}</span>
<span v-else class="status-off"><i class="el-icon-circle-close" /> {{ $t(key('disabled')) }}</span>
</template>
</el-table-column>
<el-table-column prop="remark" :label="$t(key('remark'))" show-overflow-tooltip />
<el-table-column :label="$t(key('operation'))" width="150" fixed="right">
<template slot-scope="{ row }">
<el-button type="text" size="mini" icon="el-icon-edit" @click="openEdit(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-dialog :title="$t(dialogTitle)" :visible.sync="formVisible" append-to-body width="900px" :close-on-click-modal="false">
<el-row :gutter="20">
<el-col :span="10">
<el-form ref="form" :model="formData" :rules="rules" label-width="120px">
<el-form-item :label="$t(key('script_code'))" prop="code">
<el-input v-model="formData.code" :placeholder="$t(key('enter_code'))" clearable />
</el-form-item>
<el-form-item :label="$t(key('script_name'))" prop="name">
<el-input v-model="formData.name" :placeholder="$t(key('enter_name'))" clearable />
</el-form-item>
<el-form-item :label="$t(key('interface_name'))" prop="interface_code">
<el-select v-model="formData.interface_code" :placeholder="$t(key('select_interface'))" clearable filterable style="width:100%">
<el-option v-for="item in interfaceOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t(key('interface_position'))" prop="interface_position">
<el-select v-model="formData.interface_position" :placeholder="$t(key('select_interface_position'))" clearable style="width:100%">
<el-option :label="$t(key('start_position'))" value="begin" />
<el-option :label="$t(key('end_position'))" value="end" />
</el-select>
</el-form-item>
<el-form-item :label="$t(key('status'))" prop="status">
<el-radio-group v-model="formData.status">
<el-radio label="1">{{ $t(key('enabled')) }}</el-radio>
<el-radio label="0">{{ $t(key('disabled')) }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t(key('remark'))">
<el-input v-model="formData.remark" type="textarea" :rows="4" :placeholder="$t(key('enter_remark'))" />
</el-form-item>
</el-form>
</el-col>
<el-col :span="14">
<div class="script-editor-title">{{ $t(key('script_content')) }}</div>
<el-input v-model="scriptContent" type="textarea" :rows="20" class="script-editor" :placeholder="$t(key('script_content_required'))" />
</el-col>
</el-row>
<span slot="footer">
<el-button @click="formVisible = false">{{ $t(key('cancel')) }}</el-button>
<el-button type="primary" :loading="submitting" @click="submitForm">{{ $t(key('confirm')) }}</el-button>
</span>
</el-dialog>
</el-dialog>
</template>
<script>
import { i18nMixin } from '@/composables/useI18n'
import {
getCalculationScriptAll,
createCalculationScript,
editCalculationScript,
deleteCalculationScript
} from '@/api/production-master-data/calculation-script'
export default {
name: 'ProcessRoutingCardCalculationScript',
mixins: [i18nMixin('page.production_master_data.process_model.process_routing.card')],
props: {
title: { type: String, default: '' },
visible: { type: Boolean, default: false },
flowProcessId: { type: [String, Number], default: '' }
},
data () {
return {
loading: false,
submitting: false,
tableData: [],
formVisible: false,
dialogTitle: '',
handleType: 'create',
editId: '',
scriptContent: '',
formData: { code: '', name: '', interface_code: '', interface_position: '', status: '1', remark: '' },
interfaceOptions: [
{ label: 'set_battery_process_result成品段下料接口', value: 'set_battery_process_result' },
{ label: 'set_material_input制片段上料接口', value: 'set_material_input' },
{ label: 'get_battery_process_route电芯路由接口', value: 'get_battery_process_route' },
{ label: 'set_wip_output_by_item_id裸电芯下料接口', value: 'set_wip_output_by_item_id' },
{ label: 'set_tray_process_result托盘下料接口', value: 'set_tray_process_result' }
],
rules: {
code: [
{ required: true, message: this.key('enter_code'), trigger: 'blur' },
{ min: 1, max: 45, message: this.key('length_1_45'), trigger: 'blur' }
],
name: [
{ required: true, message: this.key('enter_name'), trigger: 'blur' },
{ min: 1, max: 45, message: this.key('length_1_45'), trigger: 'blur' }
],
interface_code: [{ required: true, message: this.key('select_interface'), trigger: 'change' }],
interface_position: [{ required: true, message: this.key('select_interface_position'), trigger: 'change' }]
}
}
},
watch: {
visible (val) {
if (val) this.fetchData()
},
flowProcessId (val) {
if (val && this.visible) this.fetchData()
}
},
methods: {
handleClose () {
this.$emit('close')
},
async fetchData () {
if (!this.flowProcessId) return
this.loading = true
try {
const res = await getCalculationScriptAll({ process_id: this.flowProcessId })
this.tableData = (res && res.data) || res || []
} finally {
this.loading = false
}
},
resetForm () {
this.formData = { code: '', name: '', interface_code: '', interface_position: '', status: '1', remark: '' }
this.scriptContent = ''
this.editId = ''
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
},
openAdd () {
this.handleType = 'create'
this.dialogTitle = this.key('add_calculation_script')
this.resetForm()
this.formVisible = true
},
openEdit (row) {
this.handleType = 'edit'
this.dialogTitle = this.key('edit_calculation_script')
this.editId = row.id
this.formData = {
code: row.code || '',
name: row.name || '',
interface_code: row.interface_code || '',
interface_position: row.interface_position || '',
status: row.status !== undefined ? String(row.status) : '1',
remark: row.remark || ''
}
this.scriptContent = row.calculation_script_content || ''
this.formVisible = true
},
submitForm () {
this.$refs.form.validate(async valid => {
if (!valid) return
if (!this.scriptContent) {
this.$message.error(this.$t(this.key('script_content_required')))
return
}
this.submitting = true
try {
const payload = {
...this.formData,
process_id: this.flowProcessId,
calculation_script_content: this.scriptContent
}
if (this.handleType === 'create') {
await createCalculationScript(payload)
} else {
await editCalculationScript({ ...payload, id: this.editId })
}
this.$message.success(this.$t(this.key('operation_success')))
this.formVisible = false
this.fetchData()
} finally {
this.submitting = false
}
})
},
async handleDelete (row) {
await this.$confirm(this.$t(this.key('confirm_delete_script')), this.$t(this.key('tip')), {
confirmButtonText: this.$t(this.key('confirm')),
cancelButtonText: this.$t(this.key('cancel')),
type: 'warning',
closeOnClickModal: false
})
await deleteCalculationScript({ id: row.id })
this.$message.success(this.$t(this.key('operation_success')))
this.fetchData()
}
}
}
</script>
<style scoped>
.toolbar {
margin-bottom: 10px;
}
.status-on {
color: #67C23A;
}
.status-off {
color: #F56C6C;
}
.script-editor-title {
margin-bottom: 8px;
color: #606266;
}
.script-editor /deep/ textarea {
font-family: Menlo, Consolas, monospace;
}
</style>

View File

@@ -0,0 +1,253 @@
<template>
<el-dialog
:title="$t(title)"
:visible.sync="visible"
append-to-body
:close-on-click-modal="false"
width="80%"
:before-close="handleClose"
>
<div class="result-param-layout">
<el-card class="filter-panel" shadow="never">
<div slot="header">
<span>{{ $t(key('search')) }}</span>
<el-button type="text" style="float:right;padding:3px 0" @click="onSearch">
{{ $t(key('query')) }}
</el-button>
</div>
<el-form label-position="top" :model="searchForm" size="mini">
<el-form-item :label="$t(key('name'))">
<el-input v-model.trim="searchForm.name" :placeholder="$t(key('enter_name'))" clearable />
</el-form-item>
<el-form-item :label="$t(key('param'))">
<el-input v-model.trim="searchForm.code" :placeholder="$t(key('enter_param'))" clearable />
</el-form-item>
</el-form>
</el-card>
<div class="table-panel">
<div class="table-actions">
<el-button type="primary" size="mini" icon="el-icon-plus" @click="openBindDialog">
{{ $t(key('add_result_param')) }}
</el-button>
</div>
<el-table :data="tableData" v-loading="loading" border height="480">
<el-table-column prop="name" :label="$t(key('name'))" width="220" show-overflow-tooltip />
<el-table-column prop="code" :label="$t(key('param'))" width="180" show-overflow-tooltip />
<el-table-column prop="field_type" :label="$t(key('category'))" width="140" />
<el-table-column prop="remark" :label="$t(key('remark'))" show-overflow-tooltip />
</el-table>
</div>
</div>
<el-pagination
style="margin-top:12px;text-align:right"
:current-page="pagination.current"
:page-size="pagination.size"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="onSizeChange"
@current-change="onCurrentChange"
/>
<div slot="footer">
<el-button @click="handleClose">{{ $t(key('close')) }}</el-button>
</div>
<el-dialog
:title="$t(key('add_result_param'))"
:visible.sync="bindVisible"
append-to-body
width="720px"
:close-on-click-modal="false"
>
<el-alert :title="$t(key('result_param_bind_tip'))" type="warning" :closable="false" show-icon />
<div class="bind-layout">
<el-card class="bind-card" shadow="never">
<div slot="header">
<span>{{ $t(key('all_result_params')) }}</span>
<el-button type="text" style="float:right;padding:3px 0">{{ checkedCount }} / {{ leftParams.length }}</el-button>
</div>
<el-tree
ref="leftTree"
:data="leftParams"
show-checkbox
node-key="code"
accordion
:check-strictly="true"
:default-checked-keys="defaultCheckedKeys"
:props="treeProps"
@check="onTreeCheck"
/>
</el-card>
<div class="bind-arrow">
<el-button type="primary" icon="el-icon-arrow-right" circle @click="syncRightParams" />
</div>
<el-card class="bind-card" shadow="never">
<div slot="header">
<span>{{ $t(key('selected_result_params')) }}</span>
<el-button type="text" style="float:right;padding:3px 0">{{ rightParams.length }}</el-button>
</div>
<el-tree :data="rightParams" node-key="code" :props="treeProps" />
</el-card>
</div>
<span slot="footer">
<el-button size="mini" @click="bindVisible = false">{{ $t(key('cancel')) }}</el-button>
<el-button size="mini" type="primary" @click="submitBindParams">{{ $t(key('submit')) }}</el-button>
</span>
</el-dialog>
</el-dialog>
</template>
<script>
import { i18nMixin } from '@/composables/useI18n'
import {
getOptionalParamsDetails,
getAllWorkingsubclassParams,
addOptionalParams
} from '@/api/production-master-data/process-routing-card'
function readListData (res) {
const data = res && res.data ? res.data : res
if (!data) return { list: [], total: 0 }
if (Array.isArray(data)) return { list: data, total: data.length }
const list = data.data || data.list || []
return { list: Array.isArray(list) ? list : [], total: Number(data.count || data.total || list.length || 0) }
}
export default {
name: 'ProcessRoutingCardResultParam',
mixins: [i18nMixin('page.production_master_data.process_model.process_routing.card')],
props: {
title: { type: String, default: '' },
visible: { type: Boolean, default: false },
workingsubclassId: { type: [String, Number], default: '' },
flowProcessId: { type: [String, Number], default: '' }
},
data () {
return {
loading: false,
tableData: [],
searchForm: { name: '', code: '' },
pagination: { current: 1, size: 10, total: 0 },
bindVisible: false,
leftParams: [],
rightParams: [],
defaultCheckedKeys: [],
selectedCodes: [],
changed: false,
treeProps: { children: 'children', label: 'label' }
}
},
computed: {
checkedCount () {
return this.selectedCodes.length
}
},
watch: {
visible (val) {
if (val) this.fetchData(true)
},
flowProcessId (val) {
if (val && this.visible) this.fetchData(true)
}
},
methods: {
handleClose () {
this.pagination.current = 1
this.pagination.size = 10
this.$emit('close')
},
onSearch () {
this.fetchData(true)
},
onSizeChange (size) {
this.pagination.size = size
this.fetchData(true)
},
onCurrentChange (current) {
this.pagination.current = current
this.fetchData(false)
},
async fetchData (resetPage = false) {
if (!this.flowProcessId) return
if (resetPage) this.pagination.current = 1
this.loading = true
try {
const res = await getOptionalParamsDetails({
page_no: this.pagination.current,
page_size: this.pagination.size,
process_id: this.flowProcessId,
...this.searchForm
})
const { list, total } = readListData(res)
this.tableData = list
this.pagination.total = total
} finally {
this.loading = false
}
},
async openBindDialog () {
if (!this.flowProcessId) return
const res = await getAllWorkingsubclassParams({ process_id: this.flowProcessId })
const data = res && res.data ? res.data : res || {}
this.leftParams = data.left_optional_params || []
this.rightParams = data.right_optional_params || []
this.defaultCheckedKeys = data.default_checked_keys || []
this.selectedCodes = [...this.defaultCheckedKeys]
this.changed = false
this.bindVisible = true
},
onTreeCheck () {
this.selectedCodes = this.$refs.leftTree ? this.$refs.leftTree.getCheckedKeys() : []
this.changed = true
},
syncRightParams () {
const checked = this.$refs.leftTree ? this.$refs.leftTree.getCheckedNodes() : []
this.rightParams = checked.map(item => ({ ...item }))
this.changed = true
},
async submitBindParams () {
if (!this.changed) {
this.$message.error(this.$t(this.key('please_select_param')))
return
}
await addOptionalParams({
process_id: this.flowProcessId,
optional_params: JSON.stringify(this.rightParams)
})
this.$message.success(this.$t(this.key('operation_success')))
this.bindVisible = false
this.fetchData(true)
}
}
}
</script>
<style scoped>
.result-param-layout,
.bind-layout {
display: flex;
gap: 16px;
}
.filter-panel {
width: 220px;
flex: none;
}
.table-panel {
flex: 1;
min-width: 0;
}
.table-actions {
margin-bottom: 10px;
}
.bind-card {
width: 300px;
min-height: 360px;
}
.bind-arrow {
display: flex;
align-items: center;
}
</style>

View File

@@ -0,0 +1,209 @@
<template>
<el-drawer :visible.sync="visible" :wrapper-closable="false" :with-header="false" size="50%">
<div class="drawer-title">
<el-page-header @back="handleClose" :content="$t(title)" />
</div>
<div class="section">
<div class="section-title">| {{ $t(key('temperature_step_range')) }}</div>
<el-form :inline="true" :model="formTemp" size="mini">
<el-form-item :label="$t(key('start_step'))">
<el-select v-model="formTemp.start_work_step_id" :placeholder="$t(key('select_start_step'))" @change="checkStepRange">
<el-option v-for="item in stepOptions" :key="item.id" :label="item.suffix_name || item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item :label="$t(key('end_step'))">
<el-select v-model="formTemp.end_work_step_id" :placeholder="$t(key('select_end_step'))" @change="checkStepRange">
<el-option v-for="item in stepOptions" :key="item.id" :label="item.suffix_name || item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
</div>
<div class="section">
<div class="section-title">| {{ $t(key('temperature_data')) }}</div>
<div class="toolbar">
<el-button size="mini" icon="el-icon-plus" @click="addRow">{{ $t(key('add')) }}</el-button>
<el-button size="mini" icon="el-icon-delete" @click="removeRows">{{ $t(key('remove')) }}</el-button>
<el-button size="mini" type="primary" icon="el-icon-check" @click="saveRows">{{ $t(key('save')) }}</el-button>
<el-button size="mini" icon="el-icon-upload2" @click="importVisible = true">{{ $t(key('import_data')) }}</el-button>
</div>
<el-table ref="table" :data="tableData" border height="560" @selection-change="selectedRows = $event">
<el-table-column type="selection" width="48" />
<el-table-column :label="$t(key('temperature_value'))" min-width="160">
<template slot-scope="{ row }">
<el-input v-model="row.temperature" size="mini" />
</template>
</el-table-column>
<el-table-column :label="$t(key('compensation_value'))" min-width="160">
<template slot-scope="{ row }">
<el-input v-model="row.compensation_value" size="mini" />
</template>
</el-table-column>
</el-table>
</div>
<el-dialog :title="$t(key('import_temperature_data'))" :visible.sync="importVisible" append-to-body width="420px">
<el-upload
drag
action=""
:auto-upload="false"
:show-file-list="false"
accept=".xls,.xlsx"
:on-change="onFileChange"
>
<i class="el-icon-upload" />
<div class="el-upload__text">{{ $t(key('drag_file_here')) }}<em>{{ $t(key('click_to_upload')) }}</em></div>
<div slot="tip" class="el-upload__tip">{{ fileName }}</div>
</el-upload>
<span slot="footer">
<el-button type="primary" :loading="templateLoading" @click="downloadTemplate">{{ $t(key('download_template')) }}</el-button>
</span>
</el-dialog>
</el-drawer>
</template>
<script>
import { i18nMixin } from '@/composables/useI18n'
import { getStep, getTemperatureList, createTemperature, getTemperatureTemplate } from '@/api/production-master-data/process-routing-card'
import { downloadRename, readExcel } from '@/utils/file'
export default {
name: 'ProcessRoutingCardTemperatureCompensation',
mixins: [i18nMixin('page.production_master_data.process_model.process_routing.card')],
props: {
title: { type: String, default: '' },
visible: { type: Boolean, default: false },
flowProcessId: { type: [String, Number], default: '' }
},
data () {
return {
formTemp: { start_work_step_id: '', end_work_step_id: '' },
stepOptions: [],
tableData: [],
selectedRows: [],
importVisible: false,
fileName: '',
templateLoading: false
}
},
watch: {
visible (val) {
if (val) this.bootstrap()
}
},
methods: {
handleClose () {
this.$emit('close')
},
async bootstrap () {
await Promise.all([this.loadSteps(), this.loadTemperatureList()])
},
async loadSteps () {
const res = await getStep({})
this.stepOptions = (res && res.data) || res || []
},
async loadTemperatureList () {
if (!this.flowProcessId) return
const res = await getTemperatureList({ process_id: this.flowProcessId })
const data = (res && res.data) || res || {}
this.tableData = data.data || []
this.formTemp.start_work_step_id = data.start_work_step_id || ''
this.formTemp.end_work_step_id = data.end_work_step_id || ''
},
checkStepRange () {
const start = Number(this.formTemp.start_work_step_id)
const end = Number(this.formTemp.end_work_step_id)
if (start && end && start > end) this.$message.warning(this.$t(this.key('start_step_greater_than_end')))
},
addRow () {
this.tableData.push({ temperature: '', compensation_value: '' })
},
removeRows () {
if (!this.selectedRows.length) {
this.$message.error(this.$t(this.key('select_at_least_one')))
return
}
this.tableData = this.tableData.filter(row => !this.selectedRows.includes(row))
},
validateRows () {
if (!this.formTemp.start_work_step_id) return this.$t(this.key('select_start_step_required'))
if (!this.formTemp.end_work_step_id) return this.$t(this.key('select_end_step_required'))
const seen = {}
for (const row of this.tableData) {
if (row.temperature === '' || row.temperature === null || row.temperature === undefined) return this.$t(this.key('temperature_required'))
if (row.compensation_value === '' || row.compensation_value === null || row.compensation_value === undefined) return this.$t(this.key('compensation_required'))
if (!/^[-]?\d+(\.\d+)?$/.test(String(row.temperature))) return this.$t(this.key('temperature_must_be_numeric'))
if (!/^[-]?\d+(\.\d+)?$/.test(String(row.compensation_value))) return this.$t(this.key('compensation_must_be_numeric'))
if (seen[row.temperature]) return this.$t(this.key('duplicate_temperature_exists'))
seen[row.temperature] = true
}
return ''
},
async saveRows () {
const error = this.validateRows()
if (error) {
this.$message.error(error)
return
}
await createTemperature({
temp_data: this.tableData,
start_work_step_id: this.formTemp.start_work_step_id,
end_work_step_id: this.formTemp.end_work_step_id,
process_id: this.flowProcessId
})
this.$message.success(this.$t(this.key('operation_success')))
this.loadTemperatureList()
},
async downloadTemplate () {
this.templateLoading = true
try {
const res = await getTemperatureTemplate({})
downloadRename(res, 'xlsx', this.$t(this.key('temperature_template_name')))
} finally {
this.templateLoading = false
}
},
async onFileChange (file) {
if (!file || !/\.(xls|xlsx)$/i.test(file.name)) {
this.$message.error(this.$t(this.key('invalid_upload_format')))
return
}
this.fileName = file.name
const rows = await readExcel(file.raw)
const next = [...this.tableData]
for (const row of rows) {
if (!Object.prototype.hasOwnProperty.call(row, '温度') || !Object.prototype.hasOwnProperty.call(row, '温度补偿值')) {
this.$message.error(this.$t(this.key('temperature_import_columns_required')))
return
}
if (next.some(item => String(item.temperature) === String(row['温度']))) {
this.$message.error(this.$t(this.key('duplicate_temperature_exists')))
return
}
next.push({ temperature: row['温度'], compensation_value: row['温度补偿值'] })
}
this.tableData = next.sort((a, b) => Number(a.temperature) - Number(b.temperature))
this.importVisible = false
}
}
}
</script>
<style scoped>
.drawer-title {
padding: 20px 0 20px 20px;
border-bottom: 1px solid #dcdfe6;
}
.section {
margin: 20px;
}
.section-title {
margin-bottom: 16px;
color: #409EFF;
font-size: 14px;
}
.toolbar {
margin-bottom: 10px;
}
</style>

View File

@@ -79,7 +79,7 @@
v-if="$permission(btn.auth) && __judgeRowButton(row, btn.key)"
@click="btn.onClick(row, index)"
>
{{ btn.label }}
{{ $t(btn.label) }}
</i>
</strong>
</template>
@@ -116,9 +116,24 @@
:visible.sync="resultVisible"
:title="resultTitle"
:workingsubclass-id="resultWorkingsubclassId"
:flow-process-id="resultFlowProcessId"
@close="resultVisible = false"
/>
<temperature-compensation
:visible.sync="temperatureVisible"
:title="temperatureTitle"
:flow-process-id="temperatureFlowProcessId"
@close="temperatureVisible = false"
/>
<calculation-script
:visible.sync="calculationVisible"
:title="calculationTitle"
:flow-process-id="calculationFlowProcessId"
@close="calculationVisible = false"
/>
<el-drawer
:title="$t(key('view_log'))"
:visible.sync="drawer"
@@ -177,11 +192,13 @@ import { getWorkingsubclassAll } from '@/api/production-master-data/process-step
import PageTable from '@/components/page-table'
import PageDialogForm from '@/components/page-dialog-form'
import TechnologyFlowModel from '../process-step/components/technology-flow-model.vue'
import ResultParam from '../process-step/components/result-param.vue'
import ResultParam from './components/result-param.vue'
import TemperatureCompensation from './components/temperature-compensation.vue'
import CalculationScript from './components/calculation-script.vue'
export default {
name: 'production-master-data-process-routing-card',
components: { PageTable, PageDialogForm, TechnologyFlowModel, ResultParam },
components: { PageTable, PageDialogForm, TechnologyFlowModel, ResultParam, TemperatureCompensation, CalculationScript },
mixins: [i18nMixin('page.production_master_data.process_model.process_routing.card'), confirmMixin],
data () {
return {
@@ -210,6 +227,13 @@ export default {
resultVisible: false,
resultTitle: '',
resultWorkingsubclassId: '',
resultFlowProcessId: '',
temperatureVisible: false,
temperatureTitle: '',
temperatureFlowProcessId: '',
calculationVisible: false,
calculationTitle: '',
calculationFlowProcessId: '',
formData: {
workingsubclass_id: '',
code: '',
@@ -322,7 +346,7 @@ export default {
this.rowOperationButtons = [
{
key: 'edit',
label: '编辑',
label: this.key('edit'),
icon: 'el-icon-edit',
cssStyle: { marginRight: '10px', cursor: 'pointer' },
auth: '/production_configuration/technology_model/technology_flow_process/edit',
@@ -330,7 +354,7 @@ export default {
},
{
key: 'delete',
label: '删除',
label: this.key('delete'),
icon: 'el-icon-delete',
cssStyle: { color: 'red', marginRight: '10px', cursor: 'pointer' },
auth: '/production_configuration/technology_model/technology_flow_process/delete',
@@ -338,7 +362,7 @@ export default {
},
{
key: 'setting',
label: '设定值',
label: this.key('setting'),
icon: 'el-icon-setting',
cssStyle: { color: '#409EFF', marginRight: '10px', cursor: 'pointer' },
auth: '/production_configuration/technology_model/technology_flow_process/setting',
@@ -346,7 +370,7 @@ export default {
},
{
key: 'resultParam',
label: '结果参数',
label: this.key('result_param'),
icon: 'el-icon-s-promotion',
cssStyle: { color: '#E6A23C', marginRight: '10px', cursor: 'pointer' },
auth: '/production_configuration/technology_model/technology_flow_process/result_params',
@@ -354,7 +378,7 @@ export default {
},
{
key: 'temperature',
label: '温度补偿',
label: this.key('temperature'),
icon: 'el-icon-magic-stick',
cssStyle: { color: '#67C23A', marginRight: '10px', cursor: 'pointer' },
auth: '/production_configuration/technology_model/technology_flow_process/temperature',
@@ -362,7 +386,7 @@ export default {
},
{
key: 'calculationScript',
label: '计算脚本',
label: this.key('calculation_script'),
icon: 'el-icon-monitor',
cssStyle: { color: '#67C23A', marginRight: '10px', cursor: 'pointer' },
auth: '/production_configuration/technology_model/technology_flow_process/calculation_script',
@@ -398,10 +422,10 @@ export default {
if (key === 'delete' && !this.isBindingBatch) return true
if (key === 'temperature' &&
['FORMATION', 'YC', 'HC', 'IC', 'FORMATION_SS'].indexOf(row.device_category_code) !== -1) {
return false
return true
}
if (key === 'resultParam') return true
if (key === 'calculationScript') return false
if (key === 'calculationScript') return true
if (key === 'edit') return true
return false
},
@@ -559,13 +583,18 @@ export default {
openResultParamDrawer (row) {
this.resultTitle = this.key('result_param')
this.resultWorkingsubclassId = row.workingsubclass_id || ''
this.resultFlowProcessId = row.id || ''
this.resultVisible = true
},
openTemperatureDrawer (row) {
this.$message.warning('当前项目暂未接入温度补偿功能')
this.temperatureTitle = this.key('temperature')
this.temperatureFlowProcessId = row.id || ''
this.temperatureVisible = true
},
openCalculationScriptDrawer (row) {
this.$message.warning('当前项目暂未接入计算脚本功能')
this.calculationTitle = this.key('calculation_script')
this.calculationFlowProcessId = row.id || ''
this.calculationVisible = true
},
async handleSettingSubmit (data) {
await setSetting({