优化混炼示方,新增种类配置
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -4,6 +4,8 @@
|
||||
"java.import.maven.enabled": true,
|
||||
"java.configuration.updateBuildConfiguration": "automatic",
|
||||
"java.autobuild.enabled": true,
|
||||
"java.import.maven.offline.enabled": false,
|
||||
"java.configuration.maven.notCoveredPluginExecutionSeverity": "ignore",
|
||||
"java.jdt.ls.java.home": "C:\\Program Files\\Java\\jdk-17",
|
||||
"java.configuration.runtimes": [
|
||||
{
|
||||
|
||||
2
jeecg-boot/.vscode/settings.json
vendored
2
jeecg-boot/.vscode/settings.json
vendored
@@ -4,6 +4,8 @@
|
||||
"java.import.maven.enabled": true,
|
||||
"java.configuration.updateBuildConfiguration": "automatic",
|
||||
"java.autobuild.enabled": true,
|
||||
"java.import.maven.offline.enabled": false,
|
||||
"java.configuration.maven.notCoveredPluginExecutionSeverity": "ignore",
|
||||
"java.jdt.ls.java.home": "C:\\Program Files\\Java\\jdk-17",
|
||||
"java.configuration.runtimes": [
|
||||
{
|
||||
|
||||
18
jeecg-boot/.vscode/tasks.json
vendored
Normal file
18
jeecg-boot/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Maven: 修复 Java Classpath",
|
||||
"type": "shell",
|
||||
"command": "mvn clean install -DskipTests -pl jeecg-boot-base-core,jeecg-boot-module/jeecg-module-print,jeecg-boot-module/jeecg-module-xslmes -am",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": false
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -186,6 +186,95 @@ jeecgboot-vue3/src/views/xslmes/mesXslEquipInspectConfig/components/MesXslEquipm
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingStepSelectCell.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
||||
|
||||
-- author:cursor---date:20260522--for: 【XSLMES-20260522-A37】未选机台点击动作/组合提示请先选择机台 ---
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingStepSelectCell.vue
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A43】混炼示方换算系数联动橡胶及配合剂单重实时计算 ---
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A42】混炼示方橡胶及配合剂明细底部固定合计行 ---
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A41】混炼示方橡胶及配合剂明细累计按种类分组合计 ---
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A44】配合示方生成混炼示方时按设备台账有效体积自动计算填充体积 ---
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslEquipInspectConfig/components/MesXslEquipmentLedgerMultiSelectModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A45】混炼示方保存/批量生成子表改批量插入并延长前端超时 ---
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.api.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpec/MesXslFormulaSpec.api.ts
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A46】混炼示方换算系数/单重/机台有效体积联动填充体积 ---
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslEquipInspectConfig/components/MesXslEquipmentLedgerSelectModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A47】混炼示方保存分步骤性能诊断日志 ---
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixingSpecController.java
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A48】生成混炼示方时同步B/F段胶至密炼物料(母炼胶/A胶、终炼胶/Q胶) ---
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A49】删除混炼示方时同步删除B/F段胶密炼物料 ---
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A50】MES物料小类胶料标记/生成混炼示方种类映射/混炼示方选料弹窗 ---
|
||||
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysCategoryServiceImpl.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
|
||||
jeecgboot-vue3/src/views/system/category/category.constants.ts
|
||||
jeecgboot-vue3/src/views/system/category/category.data.ts
|
||||
jeecgboot-vue3/src/views/system/category/components/CategoryModal.vue
|
||||
jeecgboot-vue3/src/views/mes/material/modules/MesMixerMaterialSysCategoryModal.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialSelectModal.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialCategorySetting.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A50】选料弹窗左侧小类树为空修复(对齐密炼物料列表树加载/异步展开/隐藏配置重置) ---
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialSelectModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A50】选料弹窗小类设置无明细/统一分类加载函数 -----------
|
||||
jeecgboot-vue3/src/views/system/category/category.constants.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialSelectModal.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialCategorySetting.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A50】选料弹窗分类加载改为getChildListBatch两级查询 -----------
|
||||
jeecgboot-vue3/src/views/system/category/category.constants.ts
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A50】选料弹窗openModal未传data导致初始化未执行 -----------
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialSelectModal.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A50】选料弹窗移出父Modal+useModalInner初始化 -----------
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialSelectModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A50】小类展示分组勾选互不覆盖 -----------
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialCategorySetting.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A50】选料弹窗隐藏ERP列并新增自动/人工称量列 -----------
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialSelectModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A51】密炼物料种类配置表及列表批量新增 -----------
|
||||
jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_102__mes_xsl_mixer_material_kind_cfg.sql
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMixerMaterialKindCfg.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslMixerMaterialKindCfgMapper.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMixerMaterialKindCfgService.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixerMaterialKindCfgServiceImpl.java
|
||||
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixerMaterialKindCfgController.java
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixerMaterialKindCfg/MesXslMixerMaterialKindCfgList.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixerMaterialKindCfg/MesXslMixerMaterialKindCfg.data.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixerMaterialKindCfg/MesXslMixerMaterialKindCfg.api.ts
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixerMaterialKindCfg/components/MesXslMixerMaterialKindCfgBatchModal.vue
|
||||
jeecgboot-vue3/src/views/xslmes/mesXslMixerMaterialKindCfg/components/MesXslMixerMaterialKindCfgEditModal.vue
|
||||
|
||||
-- author:cursor---date:20260525--for: 【XSLMES-20260525-A51】密炼物料种类配置菜单ID冲突修复 -----------
|
||||
jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_102__mes_xsl_mixer_material_kind_cfg.sql
|
||||
jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_103__mes_xsl_mixer_material_kind_cfg_menu_fix.sql
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot3</groupId>
|
||||
<artifactId>jeecg-boot-base-core</artifactId>
|
||||
<version>${jeecgboot.version}</version>
|
||||
</dependency>
|
||||
<!-- 复用打印模板模块:打印机枚举、业务绑定、PDF 提交队列 -->
|
||||
<dependency>
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
package org.jeecg.modules.xslmes.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixerMaterialKindCfg;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslMixerMaterialKindCfgService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* MES 密炼物料种类配置
|
||||
*/
|
||||
@Tag(name = "MES密炼物料种类配置")
|
||||
@RestController
|
||||
@RequestMapping("/xslmes/mesXslMixerMaterialKindCfg")
|
||||
@Slf4j
|
||||
public class MesXslMixerMaterialKindCfgController
|
||||
extends JeecgController<MesXslMixerMaterialKindCfg, IMesXslMixerMaterialKindCfgService> {
|
||||
|
||||
@Autowired
|
||||
private IMesXslMixerMaterialKindCfgService mesXslMixerMaterialKindCfgService;
|
||||
|
||||
@Operation(summary = "MES密炼物料种类配置-分页列表查询")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<MesXslMixerMaterialKindCfg>> queryPageList(
|
||||
MesXslMixerMaterialKindCfg model,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
QueryWrapper<MesXslMixerMaterialKindCfg> queryWrapper = QueryGenerator.initQueryWrapper(model, req.getParameterMap());
|
||||
queryWrapper.orderByAsc("priority").orderByDesc("create_time");
|
||||
Page<MesXslMixerMaterialKindCfg> page = new Page<>(pageNo, pageSize);
|
||||
IPage<MesXslMixerMaterialKindCfg> pageList = mesXslMixerMaterialKindCfgService.page(page, queryWrapper);
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置展开与批量保存-----------
|
||||
@Operation(summary = "MES密炼物料种类配置-按根字典/分类展开明细")
|
||||
@GetMapping(value = "/expandLines")
|
||||
public Result<List<MesXslMixerMaterialKindCfg>> expandLines(
|
||||
@RequestParam(name = "sourceType") String sourceType,
|
||||
@RequestParam(name = "sourceRootCode") String sourceRootCode,
|
||||
@RequestParam(name = "tenantId", required = false) Integer tenantId) {
|
||||
List<MesXslMixerMaterialKindCfg> lines =
|
||||
mesXslMixerMaterialKindCfgService.expandLines(sourceType, sourceRootCode, tenantId);
|
||||
return Result.OK(lines);
|
||||
}
|
||||
|
||||
@AutoLog(value = "MES密炼物料种类配置-批量添加")
|
||||
@Operation(summary = "MES密炼物料种类配置-批量添加")
|
||||
@RequiresPermissions("xslmes:mes_xsl_mixer_material_kind_cfg:add")
|
||||
@PostMapping(value = "/addBatch")
|
||||
public Result<String> addBatch(@RequestBody List<MesXslMixerMaterialKindCfg> lines) {
|
||||
mesXslMixerMaterialKindCfgService.saveBatchLines(lines, null);
|
||||
return Result.OK("添加成功!");
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置展开与批量保存-----------
|
||||
|
||||
@AutoLog(value = "MES密炼物料种类配置-添加")
|
||||
@Operation(summary = "MES密炼物料种类配置-添加")
|
||||
@RequiresPermissions("xslmes:mes_xsl_mixer_material_kind_cfg:add")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<String> add(@RequestBody MesXslMixerMaterialKindCfg model) {
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置保存校验-----------
|
||||
String err = validateForSave(model, false);
|
||||
if (err != null) {
|
||||
return Result.error(err);
|
||||
}
|
||||
model.setDelFlag(CommonConstant.DEL_FLAG_0);
|
||||
mesXslMixerMaterialKindCfgService.save(model);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置保存校验-----------
|
||||
return Result.OK("添加成功!");
|
||||
}
|
||||
|
||||
@AutoLog(value = "MES密炼物料种类配置-编辑")
|
||||
@Operation(summary = "MES密炼物料种类配置-编辑")
|
||||
@RequiresPermissions("xslmes:mes_xsl_mixer_material_kind_cfg:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||
public Result<String> edit(@RequestBody MesXslMixerMaterialKindCfg model) {
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置保存校验-----------
|
||||
String err = validateForSave(model, true);
|
||||
if (err != null) {
|
||||
return Result.error(err);
|
||||
}
|
||||
mesXslMixerMaterialKindCfgService.updateById(model);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置保存校验-----------
|
||||
return Result.OK("编辑成功!");
|
||||
}
|
||||
|
||||
@AutoLog(value = "MES密炼物料种类配置-删除")
|
||||
@Operation(summary = "MES密炼物料种类配置-通过id删除")
|
||||
@RequiresPermissions("xslmes:mes_xsl_mixer_material_kind_cfg:delete")
|
||||
@DeleteMapping(value = "/delete")
|
||||
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
|
||||
mesXslMixerMaterialKindCfgService.removeById(id);
|
||||
return Result.OK("删除成功!");
|
||||
}
|
||||
|
||||
@AutoLog(value = "MES密炼物料种类配置-批量删除")
|
||||
@Operation(summary = "MES密炼物料种类配置-批量删除")
|
||||
@RequiresPermissions("xslmes:mes_xsl_mixer_material_kind_cfg:deleteBatch")
|
||||
@DeleteMapping(value = "/deleteBatch")
|
||||
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
|
||||
mesXslMixerMaterialKindCfgService.removeByIds(Arrays.asList(ids.split(",")));
|
||||
return Result.OK("批量删除成功!");
|
||||
}
|
||||
|
||||
@Operation(summary = "MES密炼物料种类配置-通过id查询")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<MesXslMixerMaterialKindCfg> queryById(@RequestParam(name = "id", required = true) String id) {
|
||||
MesXslMixerMaterialKindCfg entity = mesXslMixerMaterialKindCfgService.getById(id);
|
||||
if (entity == null) {
|
||||
return Result.error("未找到对应数据");
|
||||
}
|
||||
return Result.OK(entity);
|
||||
}
|
||||
|
||||
@RequiresPermissions("xslmes:mes_xsl_mixer_material_kind_cfg:exportXls")
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(HttpServletRequest request, MesXslMixerMaterialKindCfg model) {
|
||||
return super.exportXls(request, model, MesXslMixerMaterialKindCfg.class, "密炼物料种类配置");
|
||||
}
|
||||
|
||||
@RequiresPermissions("xslmes:mes_xsl_mixer_material_kind_cfg:importExcel")
|
||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||
return super.importExcel(request, response, MesXslMixerMaterialKindCfg.class);
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置保存校验-----------
|
||||
private String validateForSave(MesXslMixerMaterialKindCfg model, boolean isUpdate) {
|
||||
if (model == null) {
|
||||
return "参数不能为空";
|
||||
}
|
||||
if (oConvertUtils.isEmpty(model.getKindKey())) {
|
||||
return "种类键值不能为空";
|
||||
}
|
||||
if (oConvertUtils.isEmpty(model.getKindName())) {
|
||||
return "种类名称不能为空";
|
||||
}
|
||||
if (oConvertUtils.isEmpty(model.getSourceType())) {
|
||||
return "数据源类型不能为空";
|
||||
}
|
||||
if (oConvertUtils.isEmpty(model.getCategoryRefId())) {
|
||||
return "对应分类不能为空";
|
||||
}
|
||||
if (model.getPriority() == null) {
|
||||
model.setPriority(999);
|
||||
}
|
||||
if (isUpdate && oConvertUtils.isEmpty(model.getId())) {
|
||||
return "主键不能为空";
|
||||
}
|
||||
try {
|
||||
mesXslMixerMaterialKindCfgService.checkDuplicate(model, isUpdate ? model.getId() : null);
|
||||
} catch (Exception e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置保存校验-----------
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
@Tag(name = "MES混炼示方")
|
||||
@RestController
|
||||
@RequestMapping("/xslmes/mesXslMixingSpec")
|
||||
@Slf4j
|
||||
public class MesXslMixingSpecController extends JeecgController<MesXslMixingSpec, IMesXslMixingSpecService> {
|
||||
|
||||
@Autowired
|
||||
@@ -59,13 +61,36 @@ public class MesXslMixingSpecController extends JeecgController<MesXslMixingSpec
|
||||
@RequiresPermissions("xslmes:mes_xsl_mixing_spec:add")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<String> add(@RequestBody MesXslMixingSpecPage page) {
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
long requestStartMs = System.currentTimeMillis();
|
||||
log.info(
|
||||
"[混炼示方保存性能] 接口/add 收到请求 specName={}, payload=[material={}, step={}, downStep={}, tcu={}]",
|
||||
page != null ? page.getSpecName() : null,
|
||||
countChildRows(page != null ? page.getMaterialList() : null),
|
||||
countChildRows(page != null ? page.getStepList() : null),
|
||||
countChildRows(page != null ? page.getDownStepList() : null),
|
||||
countChildRows(page != null ? page.getTcuList() : null));
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
String err = validateMain(page);
|
||||
if (err != null) {
|
||||
return Result.error(err);
|
||||
}
|
||||
long validateMs = System.currentTimeMillis();
|
||||
MesXslMixingSpec main = new MesXslMixingSpec();
|
||||
BeanUtils.copyProperties(page, main);
|
||||
long copyMs = System.currentTimeMillis();
|
||||
mesXslMixingSpecService.saveMain(main, page.getMaterialList(), page.getStepList(), page.getDownStepList(), page.getTcuList());
|
||||
long serviceMs = System.currentTimeMillis();
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
log.info(
|
||||
"[混炼示方保存性能] 接口/add 完成 specName={}, mainId={}, validate={}ms, copy={}ms, service={}ms, total={}ms(返回响应前,不含AutoLog等切面)",
|
||||
main.getSpecName(),
|
||||
main.getId(),
|
||||
validateMs - requestStartMs,
|
||||
copyMs - validateMs,
|
||||
serviceMs - copyMs,
|
||||
serviceMs - requestStartMs);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
return Result.OK("添加成功!");
|
||||
}
|
||||
|
||||
@@ -74,13 +99,37 @@ public class MesXslMixingSpecController extends JeecgController<MesXslMixingSpec
|
||||
@RequiresPermissions("xslmes:mes_xsl_mixing_spec:edit")
|
||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||
public Result<String> edit(@RequestBody MesXslMixingSpecPage page) {
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
long requestStartMs = System.currentTimeMillis();
|
||||
log.info(
|
||||
"[混炼示方保存性能] 接口/edit 收到请求 id={}, specName={}, payload=[material={}, step={}, downStep={}, tcu={}]",
|
||||
page != null ? page.getId() : null,
|
||||
page != null ? page.getSpecName() : null,
|
||||
countChildRows(page != null ? page.getMaterialList() : null),
|
||||
countChildRows(page != null ? page.getStepList() : null),
|
||||
countChildRows(page != null ? page.getDownStepList() : null),
|
||||
countChildRows(page != null ? page.getTcuList() : null));
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
String err = validateMain(page);
|
||||
if (err != null) {
|
||||
return Result.error(err);
|
||||
}
|
||||
long validateMs = System.currentTimeMillis();
|
||||
MesXslMixingSpec main = new MesXslMixingSpec();
|
||||
BeanUtils.copyProperties(page, main);
|
||||
long copyMs = System.currentTimeMillis();
|
||||
mesXslMixingSpecService.updateMain(main, page.getMaterialList(), page.getStepList(), page.getDownStepList(), page.getTcuList());
|
||||
long serviceMs = System.currentTimeMillis();
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
log.info(
|
||||
"[混炼示方保存性能] 接口/edit 完成 id={}, specName={}, validate={}ms, copy={}ms, service={}ms, total={}ms(返回响应前,不含AutoLog等切面)",
|
||||
main.getId(),
|
||||
main.getSpecName(),
|
||||
validateMs - requestStartMs,
|
||||
copyMs - validateMs,
|
||||
serviceMs - copyMs,
|
||||
serviceMs - requestStartMs);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
return Result.OK("编辑成功!");
|
||||
}
|
||||
|
||||
@@ -151,5 +200,11 @@ public class MesXslMixingSpecController extends JeecgController<MesXslMixingSpec
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
private static int countChildRows(List<?> rows) {
|
||||
return rows == null ? 0 : rows.size();
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】混炼示方主子保存校验-----------
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.jeecg.modules.xslmes.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* MES 密炼物料种类配置
|
||||
*/
|
||||
@Data
|
||||
@TableName("mes_xsl_mixer_material_kind_cfg")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Schema(description = "MES密炼物料种类配置")
|
||||
public class MesXslMixerMaterialKindCfg implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String SOURCE_TYPE_DICT = "dict";
|
||||
public static final String SOURCE_TYPE_CATEGORY = "category";
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
@Excel(name = "种类键值", width = 15)
|
||||
@Schema(description = "种类键值")
|
||||
private String kindKey;
|
||||
|
||||
@Excel(name = "种类名称", width = 20)
|
||||
@Schema(description = "种类名称")
|
||||
private String kindName;
|
||||
|
||||
@Excel(name = "数据源类型", width = 12)
|
||||
@Schema(description = "数据源类型:dict/category")
|
||||
private String sourceType;
|
||||
|
||||
@Excel(name = "根编码", width = 15)
|
||||
@Schema(description = "根字典编码或分类pcode")
|
||||
private String sourceRootCode;
|
||||
|
||||
@Excel(name = "根名称", width = 20)
|
||||
@Schema(description = "根名称冗余")
|
||||
private String sourceRootName;
|
||||
|
||||
@Schema(description = "对应分类/字典项ID")
|
||||
private String categoryRefId;
|
||||
|
||||
@Excel(name = "对应编码", width = 15)
|
||||
@Schema(description = "对应分类编码/字典项值")
|
||||
private String categoryRefCode;
|
||||
|
||||
@Excel(name = "对应分类", width = 20)
|
||||
@Schema(description = "对应分类名称冗余")
|
||||
private String categoryRefName;
|
||||
|
||||
@Excel(name = "优先级", width = 10)
|
||||
@Schema(description = "优先级(数字越小越优先)")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
private Integer tenantId;
|
||||
|
||||
private String sysOrgCode;
|
||||
|
||||
@Excel(name = "创建人", width = 15)
|
||||
private String createBy;
|
||||
|
||||
@Excel(name = "创建时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
private Integer delFlag;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.jeecg.modules.xslmes.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixerMaterialKindCfg;
|
||||
|
||||
/**
|
||||
* MES 密炼物料种类配置
|
||||
*/
|
||||
public interface MesXslMixerMaterialKindCfgMapper extends BaseMapper<MesXslMixerMaterialKindCfg> {}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.jeecg.modules.xslmes.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import java.util.List;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixerMaterialKindCfg;
|
||||
|
||||
/**
|
||||
* MES 密炼物料种类配置
|
||||
*/
|
||||
public interface IMesXslMixerMaterialKindCfgService extends IService<MesXslMixerMaterialKindCfg> {
|
||||
|
||||
/**
|
||||
* 按根字典/分类展开明细行(未持久化)
|
||||
*/
|
||||
List<MesXslMixerMaterialKindCfg> expandLines(String sourceType, String sourceRootCode, Integer tenantId);
|
||||
|
||||
/**
|
||||
* 批量新增
|
||||
*/
|
||||
void saveBatchLines(List<MesXslMixerMaterialKindCfg> lines, Integer tenantId);
|
||||
|
||||
/**
|
||||
* 校验租户内种类键值/对应分类是否重复
|
||||
*/
|
||||
void checkDuplicate(MesXslMixerMaterialKindCfg line, String excludeId);
|
||||
}
|
||||
@@ -24,9 +24,11 @@ import org.jeecg.modules.mes.material.entity.MesMaterial;
|
||||
import org.jeecg.modules.mes.material.entity.MesMixerMaterial;
|
||||
import org.jeecg.modules.mes.material.service.IMesMaterialService;
|
||||
import org.jeecg.modules.mes.material.service.IMesMixerMaterialService;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.system.entity.SysCategory;
|
||||
import org.jeecg.modules.system.service.ISysCategoryService;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslEquipmentLedger;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpec;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecLine;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixerPsCompile;
|
||||
@@ -34,6 +36,7 @@ import org.jeecg.modules.xslmes.entity.MesXslMixingSpec;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixingSpecMaterial;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslFormulaSpecLineMapper;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslFormulaSpecMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslEquipmentLedgerService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecSettingService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslMixingSpecService;
|
||||
@@ -44,16 +47,24 @@ import org.jeecg.modules.xslmes.vo.MesXslFormulaMixingGenerateRequestVO;
|
||||
import org.jeecg.modules.xslmes.vo.MesXslFormulaMixingGenerateRowVO;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import org.jeecg.modules.system.service.ISysUserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecMapper, MesXslFormulaSpec>
|
||||
implements IMesXslFormulaSpecService {
|
||||
|
||||
private static final Set<String> RUBBER_CATEGORY_KEYS = Set.of("S", "P", "T", "C");
|
||||
private static final Pattern RUBBER_CODE_VERSION_PATTERN = Pattern.compile("([A-Z])01$");
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
|
||||
private static final String CATEGORY_MASTER_MAJOR = "XSLMES_MATERIAL_MASTER";
|
||||
private static final String CATEGORY_MASTER_MINOR_A = "XSLMES_MATERIAL_MASTER_A";
|
||||
private static final String CATEGORY_FINAL_MAJOR = "XSLMES_MATERIAL_FINAL";
|
||||
private static final String CATEGORY_FINAL_MINOR_Q = "XSLMES_MATERIAL_FINAL_Q";
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
|
||||
|
||||
@Resource
|
||||
private MesXslFormulaSpecLineMapper lineMapper;
|
||||
@@ -73,6 +84,9 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
@Resource
|
||||
private IMesXslMixingSpecService mesXslMixingSpecService;
|
||||
|
||||
@Resource
|
||||
private IMesXslEquipmentLedgerService mesXslEquipmentLedgerService;
|
||||
|
||||
@Resource
|
||||
private ISysCategoryService sysCategoryService;
|
||||
|
||||
@@ -515,9 +529,17 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
Map<String, MesMixerMaterial> mixerCache = new HashMap<>();
|
||||
Map<String, String> categoryNameCache = new HashMap<>();
|
||||
Map<String, Boolean> categoryRubberCache = new HashMap<>();
|
||||
Map<String, MesXslEquipmentLedger> equipmentCache = new HashMap<>();
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A44】F段终炼胶比重按配合示方整体PHR/体积汇总-----------
|
||||
BigDecimal compoundSpecificGravity = resolveCompoundSpecificGravity(formula, lines);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A44】F段终炼胶比重按配合示方整体PHR/体积汇总-----------
|
||||
int created = 0;
|
||||
int totalStages = normalizeMixingStages(formula.getMixingStages());
|
||||
Date today = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
|
||||
Set<String> syncedRubberSpecCodes = new HashSet<>();
|
||||
Map<String, String> categoryIdCache = new HashMap<>();
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
|
||||
for (MesXslFormulaMixingGenerateRowVO row : request.getRows()) {
|
||||
if (row == null || oConvertUtils.isEmpty(row.getSpecCode()) || row.getStageIndex() == null) {
|
||||
continue;
|
||||
@@ -525,6 +547,11 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
if (CollectionUtils.isEmpty(row.getMachines())) {
|
||||
throw new IllegalArgumentException("示方「" + row.getSpecCode() + "」请至少选择一个机台");
|
||||
}
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
|
||||
if (syncedRubberSpecCodes.add(row.getSpecCode().trim())) {
|
||||
syncGeneratedRubberMixerMaterial(row, formula, compoundSpecificGravity, categoryIdCache);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
|
||||
String materialStep = resolveMixingMaterialStep(row);
|
||||
int mixingColumn = resolveMixingColumn(row, formula);
|
||||
List<MesXslMixingSpecMaterial> materials = buildMixingMaterials(
|
||||
@@ -547,11 +574,25 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
mixing.setStageCount(String.valueOf(row.getStageIndex()));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A40】生成时写入段数当前/总如2/3-----------
|
||||
if ("Q".equalsIgnoreCase(row.getStepType())) {
|
||||
mixing.setFinalRubberSg(formula.getQRubberSg());
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A44】生成混炼示方时按设备有效体积计算填充体积-----------
|
||||
mixing.setConvertFactor(BigDecimal.ONE);
|
||||
boolean isFinalStage = "Q".equalsIgnoreCase(row.getStepType());
|
||||
if (isFinalStage) {
|
||||
mixing.setFinalRubberSg(compoundSpecificGravity);
|
||||
} else {
|
||||
mixing.setMotherRubberSg(formula.getARubberSg());
|
||||
}
|
||||
BigDecimal specificGravity = isFinalStage ? compoundSpecificGravity : formula.getARubberSg();
|
||||
String machineLabel = StringUtils.defaultIfBlank(machine.getMachineName(), machine.getMachineId());
|
||||
MesXslEquipmentLedger equipment = equipmentCache.computeIfAbsent(
|
||||
machine.getMachineId(), mesXslEquipmentLedgerService::getById);
|
||||
if (equipment == null) {
|
||||
throw new IllegalArgumentException("机台「" + machineLabel + "」不存在或已删除,无法生成混炼示方");
|
||||
}
|
||||
BigDecimal effectiveVolume = parseEffectiveVolume(equipment.getEffectiveVolume(), machineLabel);
|
||||
BigDecimal totalWeight = sumMaterialUnitWeight(materials);
|
||||
mixing.setFillVolume(calcFillVolume(totalWeight, specificGravity, effectiveVolume, mixing.getConvertFactor()));
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A44】生成混炼示方时按设备有效体积计算填充体积-----------
|
||||
mesXslMixingSpecService.saveMain(mixing, materials, null, null, null);
|
||||
created++;
|
||||
}
|
||||
@@ -562,6 +603,214 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
return created;
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A44】生成混炼示方时按设备有效体积计算填充体积-----------
|
||||
/** 配合示方整体比重(终炼胶比重)= TOTAL PHR ÷ 全部明细体积合计 */
|
||||
private BigDecimal resolveCompoundSpecificGravity(MesXslFormulaSpec formula, List<MesXslFormulaSpecLine> lines) {
|
||||
if (CollectionUtils.isEmpty(lines)) {
|
||||
return null;
|
||||
}
|
||||
BigDecimal totalPhr = formula != null && formula.getTotalPhr() != null && formula.getTotalPhr().compareTo(BigDecimal.ZERO) > 0
|
||||
? formula.getTotalPhr()
|
||||
: BigDecimal.ZERO;
|
||||
if (totalPhr.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
for (MesXslFormulaSpecLine line : lines) {
|
||||
if (line != null && line.getPhr() != null) {
|
||||
totalPhr = totalPhr.add(line.getPhr());
|
||||
}
|
||||
}
|
||||
}
|
||||
BigDecimal totalVolume = BigDecimal.ZERO;
|
||||
for (MesXslFormulaSpecLine line : lines) {
|
||||
BigDecimal volume = resolveLineVolume(line);
|
||||
if (volume != null) {
|
||||
totalVolume = totalVolume.add(volume);
|
||||
}
|
||||
}
|
||||
if (totalPhr.compareTo(BigDecimal.ZERO) <= 0 || totalVolume.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return null;
|
||||
}
|
||||
return totalPhr.divide(totalVolume, 6, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/** 解析设备台账有效体积(支持纯数字或带单位的字符串) */
|
||||
private BigDecimal parseEffectiveVolume(String raw, String machineLabel) {
|
||||
if (StringUtils.isBlank(raw)) {
|
||||
throw new IllegalArgumentException("机台「" + machineLabel + "」未维护有效体积,无法计算填充体积");
|
||||
}
|
||||
String trimmed = raw.trim();
|
||||
try {
|
||||
BigDecimal value = new BigDecimal(trimmed);
|
||||
if (value.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new IllegalArgumentException("机台「" + machineLabel + "」有效体积必须大于 0");
|
||||
}
|
||||
return value;
|
||||
} catch (NumberFormatException ignored) {
|
||||
Matcher matcher = Pattern.compile("([0-9]+(?:\\.[0-9]+)?)").matcher(trimmed);
|
||||
if (matcher.find()) {
|
||||
BigDecimal value = new BigDecimal(matcher.group(1));
|
||||
if (value.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new IllegalArgumentException("机台「" + machineLabel + "」有效体积必须大于 0");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
throw new IllegalArgumentException("机台「" + machineLabel + "」有效体积格式无效:" + raw);
|
||||
}
|
||||
}
|
||||
|
||||
/** 橡胶及配合剂明细单重合计 */
|
||||
private BigDecimal sumMaterialUnitWeight(List<MesXslMixingSpecMaterial> materials) {
|
||||
if (CollectionUtils.isEmpty(materials)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (MesXslMixingSpecMaterial material : materials) {
|
||||
if (material != null && material.getUnitWeight() != null) {
|
||||
total = total.add(material.getUnitWeight());
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充体积(%) = 本段单重合计 ÷ 本段比重 ÷ 机台有效体积(L) × 100 × 换算系数
|
||||
*/
|
||||
private BigDecimal calcFillVolume(
|
||||
BigDecimal totalWeight,
|
||||
BigDecimal specificGravity,
|
||||
BigDecimal effectiveVolume,
|
||||
BigDecimal convertFactor) {
|
||||
if (totalWeight == null || totalWeight.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new IllegalArgumentException("本段橡胶及配合剂单重合计无效,无法计算填充体积");
|
||||
}
|
||||
if (specificGravity == null || specificGravity.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new IllegalArgumentException("本段比重无效,无法计算填充体积");
|
||||
}
|
||||
if (effectiveVolume == null || effectiveVolume.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new IllegalArgumentException("机台有效体积无效,无法计算填充体积");
|
||||
}
|
||||
BigDecimal factor = convertFactor == null || convertFactor.compareTo(BigDecimal.ZERO) <= 0
|
||||
? BigDecimal.ONE
|
||||
: convertFactor;
|
||||
BigDecimal materialVolume = totalWeight.divide(specificGravity, 6, RoundingMode.HALF_UP);
|
||||
return materialVolume
|
||||
.divide(effectiveVolume, 6, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.multiply(factor)
|
||||
.setScale(6, RoundingMode.HALF_UP);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A44】生成混炼示方时按设备有效体积计算填充体积-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
|
||||
/**
|
||||
* 生成混炼示方时,将 B 段/F 段胶料同步写入密炼物料:
|
||||
* B 段 -> 物料大类「母炼胶」+ 小类「A胶」;F 段 -> 物料大类「终炼胶」+ 小类「Q胶」。
|
||||
*/
|
||||
private void syncGeneratedRubberMixerMaterial(
|
||||
MesXslFormulaMixingGenerateRowVO row,
|
||||
MesXslFormulaSpec formula,
|
||||
BigDecimal compoundSpecificGravity,
|
||||
Map<String, String> categoryIdCache) {
|
||||
if (row == null || StringUtils.isBlank(row.getSpecCode())) {
|
||||
return;
|
||||
}
|
||||
String specCode = row.getSpecCode().trim();
|
||||
boolean isFinalStage = "Q".equalsIgnoreCase(resolveMixingMaterialStep(row));
|
||||
String majorCode = isFinalStage ? CATEGORY_FINAL_MAJOR : CATEGORY_MASTER_MAJOR;
|
||||
String minorCode = isFinalStage ? CATEGORY_FINAL_MINOR_Q : CATEGORY_MASTER_MINOR_A;
|
||||
String majorCategoryId = resolveCategoryIdByCode(majorCode, categoryIdCache);
|
||||
String minorCategoryId = resolveCategoryIdByCode(minorCode, categoryIdCache);
|
||||
if (StringUtils.isBlank(majorCategoryId) || StringUtils.isBlank(minorCategoryId)) {
|
||||
throw new IllegalArgumentException(
|
||||
"未找到物料分类「" + (isFinalStage ? "终炼胶/Q胶" : "母炼胶/A胶") + "」,请先维护 MES 物料分类字典");
|
||||
}
|
||||
BigDecimal specificGravity = isFinalStage ? compoundSpecificGravity : formula.getARubberSg();
|
||||
String rubberName = resolveRubberName(formula);
|
||||
String materialDesc = StringUtils.isNotBlank(formula.getPurpose()) ? formula.getPurpose().trim() : rubberName;
|
||||
|
||||
MesMixerMaterial existing = findMixerMaterialByCodeOrName(specCode);
|
||||
Date now = new Date();
|
||||
if (existing != null) {
|
||||
existing.setMajorCategoryId(majorCategoryId);
|
||||
existing.setMinorCategoryId(minorCategoryId);
|
||||
existing.setSpecificGravity(specificGravity);
|
||||
if (StringUtils.isNotBlank(materialDesc)) {
|
||||
existing.setMaterialDesc(materialDesc);
|
||||
}
|
||||
if (StringUtils.isBlank(existing.getMaterialCode())) {
|
||||
existing.setMaterialCode(specCode);
|
||||
}
|
||||
if (StringUtils.isBlank(existing.getMaterialName())) {
|
||||
existing.setMaterialName(specCode);
|
||||
}
|
||||
existing.setUseStatus(existing.getUseStatus() == null ? 1 : existing.getUseStatus());
|
||||
existing.setUpdateTime(now);
|
||||
mesMixerMaterialService.updateById(existing);
|
||||
log.info(
|
||||
"[混炼示方生成] 更新密炼物料 id={}, code={}, major={}, minor={}, sg={}",
|
||||
existing.getId(),
|
||||
specCode,
|
||||
majorCode,
|
||||
minorCode,
|
||||
specificGravity);
|
||||
return;
|
||||
}
|
||||
|
||||
MesMixerMaterial material = new MesMixerMaterial();
|
||||
material.setMaterialCode(specCode);
|
||||
material.setMaterialName(specCode);
|
||||
material.setMaterialDesc(materialDesc);
|
||||
material.setAliasName(StringUtils.isNotBlank(rubberName) ? rubberName : null);
|
||||
material.setMajorCategoryId(majorCategoryId);
|
||||
material.setMinorCategoryId(minorCategoryId);
|
||||
material.setSpecificGravity(specificGravity);
|
||||
material.setFeedManageStatus(0);
|
||||
material.setUseStatus(1);
|
||||
material.setDelFlag(CommonConstant.DEL_FLAG_0);
|
||||
material.setCreateTime(now);
|
||||
material.setUpdateTime(now);
|
||||
mesMixerMaterialService.save(material);
|
||||
log.info(
|
||||
"[混炼示方生成] 新增密炼物料 id={}, code={}, major={}, minor={}, sg={}",
|
||||
material.getId(),
|
||||
specCode,
|
||||
majorCode,
|
||||
minorCode,
|
||||
specificGravity);
|
||||
}
|
||||
|
||||
private MesMixerMaterial findMixerMaterialByCodeOrName(String specCode) {
|
||||
MesMixerMaterial byCode = mesMixerMaterialService.getOne(
|
||||
new LambdaQueryWrapper<MesMixerMaterial>()
|
||||
.eq(MesMixerMaterial::getMaterialCode, specCode)
|
||||
.and(w -> w.eq(MesMixerMaterial::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesMixerMaterial::getDelFlag))
|
||||
.last("limit 1"));
|
||||
if (byCode != null) {
|
||||
return byCode;
|
||||
}
|
||||
return mesMixerMaterialService.getOne(
|
||||
new LambdaQueryWrapper<MesMixerMaterial>()
|
||||
.eq(MesMixerMaterial::getMaterialName, specCode)
|
||||
.and(w -> w.eq(MesMixerMaterial::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesMixerMaterial::getDelFlag))
|
||||
.last("limit 1"));
|
||||
}
|
||||
|
||||
private String resolveCategoryIdByCode(String categoryCode, Map<String, String> cache) {
|
||||
if (StringUtils.isBlank(categoryCode)) {
|
||||
return null;
|
||||
}
|
||||
if (cache != null && cache.containsKey(categoryCode)) {
|
||||
return cache.get(categoryCode);
|
||||
}
|
||||
SysCategory category = sysCategoryService.getOne(
|
||||
new LambdaQueryWrapper<SysCategory>().eq(SysCategory::getCode, categoryCode.trim()).last("limit 1"));
|
||||
String categoryId = category != null ? category.getId() : null;
|
||||
if (cache != null) {
|
||||
cache.put(categoryCode, categoryId);
|
||||
}
|
||||
return categoryId;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
|
||||
|
||||
private int normalizeMixingStages(Integer mixingStages) {
|
||||
if (mixingStages == null || mixingStages <= 0) {
|
||||
return 0;
|
||||
@@ -861,7 +1110,7 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
material.setMixerMaterialDesc(
|
||||
StringUtils.isNotBlank(line.getMaterialDesc()) ? line.getMaterialDesc() : line.getMixerMaterialName());
|
||||
material.setUnitWeight(unitWeight);
|
||||
fillMaterialCategory(material, line.getMixerMaterialId(), mixerCache, categoryNameCache, categoryRubberCache);
|
||||
fillMaterialCategory(material, line.getMixerMaterialId(), line, mixerCache, categoryNameCache, categoryRubberCache);
|
||||
return material;
|
||||
}
|
||||
|
||||
@@ -884,6 +1133,7 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
private void fillMaterialCategory(
|
||||
MesXslMixingSpecMaterial material,
|
||||
String mixerMaterialId,
|
||||
MesXslFormulaSpecLine line,
|
||||
Map<String, MesMixerMaterial> mixerCache,
|
||||
Map<String, String> categoryNameCache,
|
||||
Map<String, Boolean> categoryRubberCache) {
|
||||
@@ -894,18 +1144,27 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
if (mixer == null) {
|
||||
return;
|
||||
}
|
||||
String weighMode = line != null ? line.getWeighMode() : null;
|
||||
String majorName = resolveCategoryName(mixer.getMajorCategoryId(), categoryNameCache);
|
||||
String minorName = resolveCategoryName(mixer.getMinorCategoryId(), categoryNameCache);
|
||||
material.setMaterialMajor(majorName);
|
||||
material.setMaterialMinor(minorName);
|
||||
material.setMaterialKind(resolveMixingMaterialKind(mixer.getMinorCategoryId(), minorName, categoryRubberCache));
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】生成混炼示方时称量方式优先映射种类-----------
|
||||
material.setMaterialKind(
|
||||
resolveMixingMaterialKind(weighMode, mixer.getMinorCategoryId(), minorName, categoryRubberCache));
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】生成混炼示方时称量方式优先映射种类-----------
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】生成混炼示方时称量方式优先映射种类-----------
|
||||
/**
|
||||
* 种类:物料小类在分类字典勾选「胶料」则显示「胶料」,否则显示小类分类名称。
|
||||
* 种类:配合示方称量方式优先;否则物料小类勾选「胶料」则显示「胶料」,否则显示小类名称。
|
||||
*/
|
||||
private String resolveMixingMaterialKind(
|
||||
String minorCategoryId, String minorCategoryName, Map<String, Boolean> categoryRubberCache) {
|
||||
String weighMode, String minorCategoryId, String minorCategoryName, Map<String, Boolean> categoryRubberCache) {
|
||||
String weighKind = resolveWeighModeMaterialKind(weighMode);
|
||||
if (StringUtils.isNotBlank(weighKind)) {
|
||||
return weighKind;
|
||||
}
|
||||
if (isCategoryRubber(minorCategoryId, categoryRubberCache)) {
|
||||
return "胶料";
|
||||
}
|
||||
@@ -915,6 +1174,23 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 配合示方自动/人工称量 -> 混炼示方种类 */
|
||||
private String resolveWeighModeMaterialKind(String weighMode) {
|
||||
if (StringUtils.isBlank(weighMode)) {
|
||||
return null;
|
||||
}
|
||||
String normalized = weighMode.trim();
|
||||
String lower = normalized.toLowerCase();
|
||||
if (lower.startsWith("auto") || normalized.contains("自动")) {
|
||||
return "自动";
|
||||
}
|
||||
if ("manual".equals(lower) || normalized.contains("人工")) {
|
||||
return "人工";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】生成混炼示方时称量方式优先映射种类-----------
|
||||
|
||||
private boolean isCategoryRubber(String categoryId, Map<String, Boolean> cache) {
|
||||
if (oConvertUtils.isEmpty(categoryId)) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
|
||||
import org.jeecg.modules.system.entity.SysCategory;
|
||||
import org.jeecg.modules.system.entity.SysDict;
|
||||
import org.jeecg.modules.system.entity.SysDictItem;
|
||||
import org.jeecg.modules.system.service.ISysCategoryService;
|
||||
import org.jeecg.modules.system.service.ISysDictItemService;
|
||||
import org.jeecg.modules.system.service.ISysDictService;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixerMaterialKindCfg;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslMixerMaterialKindCfgMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslMixerMaterialKindCfgService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* MES 密炼物料种类配置
|
||||
*/
|
||||
@Service
|
||||
public class MesXslMixerMaterialKindCfgServiceImpl
|
||||
extends ServiceImpl<MesXslMixerMaterialKindCfgMapper, MesXslMixerMaterialKindCfg>
|
||||
implements IMesXslMixerMaterialKindCfgService {
|
||||
|
||||
@Autowired
|
||||
private ISysDictService sysDictService;
|
||||
|
||||
@Autowired
|
||||
private ISysDictItemService sysDictItemService;
|
||||
|
||||
@Autowired
|
||||
private ISysCategoryService sysCategoryService;
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置展开与批量保存-----------
|
||||
@Override
|
||||
public List<MesXslMixerMaterialKindCfg> expandLines(String sourceType, String sourceRootCode, Integer tenantId) {
|
||||
if (oConvertUtils.isEmpty(sourceType) || oConvertUtils.isEmpty(sourceRootCode)) {
|
||||
throw new JeecgBootException("请选择数据源类型与根字典/分类");
|
||||
}
|
||||
String normalizedType = sourceType.trim().toLowerCase();
|
||||
String rootCode = sourceRootCode.trim();
|
||||
if (MesXslMixerMaterialKindCfg.SOURCE_TYPE_DICT.equals(normalizedType)) {
|
||||
return expandFromDict(rootCode, tenantId);
|
||||
}
|
||||
if (MesXslMixerMaterialKindCfg.SOURCE_TYPE_CATEGORY.equals(normalizedType)) {
|
||||
return expandFromCategory(rootCode, tenantId);
|
||||
}
|
||||
throw new JeecgBootException("不支持的数据源类型:" + sourceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveBatchLines(List<MesXslMixerMaterialKindCfg> lines, Integer tenantId) {
|
||||
if (lines == null || lines.isEmpty()) {
|
||||
throw new JeecgBootException("请至少维护一条种类配置明细");
|
||||
}
|
||||
Integer resolvedTenantId = resolveTenantId(tenantId);
|
||||
Set<String> kindKeys = new HashSet<>();
|
||||
Set<String> categoryRefIds = new HashSet<>();
|
||||
for (MesXslMixerMaterialKindCfg line : lines) {
|
||||
String err = validateLine(line, kindKeys, categoryRefIds);
|
||||
if (err != null) {
|
||||
throw new JeecgBootException(err);
|
||||
}
|
||||
line.setTenantId(resolvedTenantId);
|
||||
line.setDelFlag(CommonConstant.DEL_FLAG_0);
|
||||
checkDuplicate(line, null);
|
||||
}
|
||||
saveBatch(lines);
|
||||
}
|
||||
|
||||
private List<MesXslMixerMaterialKindCfg> expandFromDict(String dictCode, Integer tenantId) {
|
||||
LambdaQueryWrapper<SysDict> dictQw = new LambdaQueryWrapper<>();
|
||||
dictQw.eq(SysDict::getDictCode, dictCode);
|
||||
SysDict dict = sysDictService.getOne(dictQw, false);
|
||||
if (dict == null) {
|
||||
throw new JeecgBootException("数据字典不存在:" + dictCode);
|
||||
}
|
||||
LambdaQueryWrapper<SysDictItem> itemQw = new LambdaQueryWrapper<>();
|
||||
itemQw.eq(SysDictItem::getDictId, dict.getId());
|
||||
itemQw.eq(SysDictItem::getStatus, 1);
|
||||
itemQw.orderByAsc(SysDictItem::getSortOrder).orderByAsc(SysDictItem::getItemValue);
|
||||
List<SysDictItem> items = sysDictItemService.list(itemQw);
|
||||
if (items == null || items.isEmpty()) {
|
||||
throw new JeecgBootException("该数据字典下没有可用的字典项");
|
||||
}
|
||||
Set<String> existsRefIds = loadExistingCategoryRefIds(MesXslMixerMaterialKindCfg.SOURCE_TYPE_DICT, dictCode, tenantId);
|
||||
List<MesXslMixerMaterialKindCfg> result = new ArrayList<>();
|
||||
int priorityBase = 10;
|
||||
int index = 0;
|
||||
for (SysDictItem item : items) {
|
||||
if (item == null || oConvertUtils.isEmpty(item.getId())) {
|
||||
continue;
|
||||
}
|
||||
if (existsRefIds.contains(item.getId())) {
|
||||
continue;
|
||||
}
|
||||
MesXslMixerMaterialKindCfg cfg = new MesXslMixerMaterialKindCfg();
|
||||
cfg.setSourceType(MesXslMixerMaterialKindCfg.SOURCE_TYPE_DICT);
|
||||
cfg.setSourceRootCode(dictCode);
|
||||
cfg.setSourceRootName(dict.getDictName());
|
||||
cfg.setCategoryRefId(item.getId());
|
||||
cfg.setCategoryRefCode(item.getItemValue());
|
||||
cfg.setCategoryRefName(item.getItemText());
|
||||
cfg.setKindKey(item.getItemValue());
|
||||
cfg.setKindName(item.getItemText());
|
||||
cfg.setPriority(item.getSortOrder() != null ? item.getSortOrder() : priorityBase + index * 10);
|
||||
cfg.setTenantId(resolveTenantId(tenantId));
|
||||
result.add(cfg);
|
||||
index++;
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
throw new JeecgBootException("该数据字典下的字典项均已配置,无需重复带出");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<MesXslMixerMaterialKindCfg> expandFromCategory(String rootCode, Integer tenantId) {
|
||||
LambdaQueryWrapper<SysCategory> rootQw = new LambdaQueryWrapper<>();
|
||||
rootQw.eq(SysCategory::getCode, rootCode);
|
||||
SysCategory root = sysCategoryService.getOne(rootQw, false);
|
||||
if (root == null) {
|
||||
throw new JeecgBootException("分类字典不存在:" + rootCode);
|
||||
}
|
||||
List<SysCategory> descendants = new ArrayList<>();
|
||||
Deque<String> queue = new ArrayDeque<>();
|
||||
queue.add(root.getId());
|
||||
while (!queue.isEmpty()) {
|
||||
String parentId = queue.poll();
|
||||
LambdaQueryWrapper<SysCategory> childQw = new LambdaQueryWrapper<>();
|
||||
childQw.eq(SysCategory::getPid, parentId);
|
||||
childQw.orderByAsc(SysCategory::getCode);
|
||||
List<SysCategory> children = sysCategoryService.list(childQw);
|
||||
if (children == null || children.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (SysCategory child : children) {
|
||||
descendants.add(child);
|
||||
queue.add(child.getId());
|
||||
}
|
||||
}
|
||||
if (descendants.isEmpty()) {
|
||||
throw new JeecgBootException("该分类下没有可用的子分类");
|
||||
}
|
||||
Set<String> existsRefIds = loadExistingCategoryRefIds(MesXslMixerMaterialKindCfg.SOURCE_TYPE_CATEGORY, rootCode, tenantId);
|
||||
List<MesXslMixerMaterialKindCfg> result = new ArrayList<>();
|
||||
int priorityBase = 10;
|
||||
for (int i = 0; i < descendants.size(); i++) {
|
||||
SysCategory category = descendants.get(i);
|
||||
if (category == null || oConvertUtils.isEmpty(category.getId())) {
|
||||
continue;
|
||||
}
|
||||
if (existsRefIds.contains(category.getId())) {
|
||||
continue;
|
||||
}
|
||||
String refCode = oConvertUtils.isNotEmpty(category.getCode()) ? category.getCode() : category.getId();
|
||||
MesXslMixerMaterialKindCfg cfg = new MesXslMixerMaterialKindCfg();
|
||||
cfg.setSourceType(MesXslMixerMaterialKindCfg.SOURCE_TYPE_CATEGORY);
|
||||
cfg.setSourceRootCode(rootCode);
|
||||
cfg.setSourceRootName(root.getName());
|
||||
cfg.setCategoryRefId(category.getId());
|
||||
cfg.setCategoryRefCode(refCode);
|
||||
cfg.setCategoryRefName(category.getName());
|
||||
cfg.setKindKey(refCode);
|
||||
cfg.setKindName(category.getName());
|
||||
cfg.setPriority(priorityBase + i * 10);
|
||||
cfg.setTenantId(resolveTenantId(tenantId));
|
||||
result.add(cfg);
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
throw new JeecgBootException("该分类下的子分类均已配置,无需重复带出");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<String> loadExistingCategoryRefIds(String sourceType, String sourceRootCode, Integer tenantId) {
|
||||
LambdaQueryWrapper<MesXslMixerMaterialKindCfg> qw = new LambdaQueryWrapper<>();
|
||||
qw.eq(MesXslMixerMaterialKindCfg::getSourceType, sourceType);
|
||||
qw.eq(MesXslMixerMaterialKindCfg::getSourceRootCode, sourceRootCode);
|
||||
qw.and(q -> q.eq(MesXslMixerMaterialKindCfg::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslMixerMaterialKindCfg::getDelFlag));
|
||||
Integer resolvedTenantId = resolveTenantId(tenantId);
|
||||
if (resolvedTenantId != null && MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
|
||||
qw.eq(MesXslMixerMaterialKindCfg::getTenantId, resolvedTenantId);
|
||||
}
|
||||
qw.select(MesXslMixerMaterialKindCfg::getCategoryRefId);
|
||||
List<MesXslMixerMaterialKindCfg> exists = list(qw);
|
||||
Set<String> ids = new HashSet<>();
|
||||
if (exists != null) {
|
||||
for (MesXslMixerMaterialKindCfg row : exists) {
|
||||
if (row != null && oConvertUtils.isNotEmpty(row.getCategoryRefId())) {
|
||||
ids.add(row.getCategoryRefId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
private String validateLine(MesXslMixerMaterialKindCfg line, Set<String> kindKeys, Set<String> categoryRefIds) {
|
||||
if (line == null) {
|
||||
return "存在空的明细行";
|
||||
}
|
||||
if (oConvertUtils.isEmpty(line.getKindKey())) {
|
||||
return "种类键值不能为空";
|
||||
}
|
||||
if (oConvertUtils.isEmpty(line.getKindName())) {
|
||||
return "种类名称不能为空";
|
||||
}
|
||||
if (oConvertUtils.isEmpty(line.getSourceType())) {
|
||||
return "数据源类型不能为空";
|
||||
}
|
||||
if (oConvertUtils.isEmpty(line.getCategoryRefId())) {
|
||||
return "对应分类不能为空";
|
||||
}
|
||||
String kindKey = line.getKindKey().trim();
|
||||
if (!kindKeys.add(kindKey)) {
|
||||
return "本次提交存在重复的种类键值:" + kindKey;
|
||||
}
|
||||
String refId = line.getCategoryRefId().trim();
|
||||
if (!categoryRefIds.add(refId)) {
|
||||
return "本次提交存在重复的对应分类";
|
||||
}
|
||||
if (line.getPriority() == null) {
|
||||
line.setPriority(999);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkDuplicate(MesXslMixerMaterialKindCfg line, String excludeId) {
|
||||
Integer tenantId = resolveTenantId(line.getTenantId());
|
||||
LambdaQueryWrapper<MesXslMixerMaterialKindCfg> kindQw = new LambdaQueryWrapper<>();
|
||||
kindQw.eq(MesXslMixerMaterialKindCfg::getKindKey, line.getKindKey().trim());
|
||||
kindQw.and(q -> q.eq(MesXslMixerMaterialKindCfg::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslMixerMaterialKindCfg::getDelFlag));
|
||||
if (tenantId != null && MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
|
||||
kindQw.eq(MesXslMixerMaterialKindCfg::getTenantId, tenantId);
|
||||
}
|
||||
if (oConvertUtils.isNotEmpty(excludeId)) {
|
||||
kindQw.ne(MesXslMixerMaterialKindCfg::getId, excludeId);
|
||||
}
|
||||
if (count(kindQw) > 0) {
|
||||
throw new JeecgBootException("种类键值已存在:" + line.getKindKey());
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<MesXslMixerMaterialKindCfg> refQw = new LambdaQueryWrapper<>();
|
||||
refQw.eq(MesXslMixerMaterialKindCfg::getCategoryRefId, line.getCategoryRefId().trim());
|
||||
refQw.and(q -> q.eq(MesXslMixerMaterialKindCfg::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesXslMixerMaterialKindCfg::getDelFlag));
|
||||
if (tenantId != null && MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
|
||||
refQw.eq(MesXslMixerMaterialKindCfg::getTenantId, tenantId);
|
||||
}
|
||||
if (oConvertUtils.isNotEmpty(excludeId)) {
|
||||
refQw.ne(MesXslMixerMaterialKindCfg::getId, excludeId);
|
||||
}
|
||||
if (count(refQw) > 0) {
|
||||
throw new JeecgBootException("对应分类已配置:" + line.getCategoryRefName());
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer resolveTenantId(Integer tenantId) {
|
||||
if (tenantId != null) {
|
||||
return tenantId;
|
||||
}
|
||||
String ts = TenantContext.getTenant();
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
try {
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
if (request != null) {
|
||||
ts = TokenUtils.getTenantIdByRequest(request);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isEmpty(ts)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(ts);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A51】密炼物料种类配置展开与批量保存-----------
|
||||
}
|
||||
@@ -2,14 +2,24 @@ package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.modules.mes.material.entity.MesMixerMaterial;
|
||||
import org.jeecg.modules.mes.material.service.IMesMixerMaterialService;
|
||||
import org.jeecg.modules.system.entity.SysCategory;
|
||||
import org.jeecg.modules.system.service.ISysCategoryService;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixingSpec;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixingSpecDownStep;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslMixingSpecMaterial;
|
||||
@@ -27,11 +37,19 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMapper, MesXslMixingSpec>
|
||||
implements IMesXslMixingSpecService {
|
||||
|
||||
private static final String TCU_UP = "up_mixer";
|
||||
private static final String TCU_DOWN = "down_mixer";
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A49】删除混炼示方时同步删除B/F段胶密炼物料-----------
|
||||
private static final String CATEGORY_MASTER_MAJOR = "XSLMES_MATERIAL_MASTER";
|
||||
private static final String CATEGORY_MASTER_MINOR_A = "XSLMES_MATERIAL_MASTER_A";
|
||||
private static final String CATEGORY_FINAL_MAJOR = "XSLMES_MATERIAL_FINAL";
|
||||
private static final String CATEGORY_FINAL_MINOR_Q = "XSLMES_MATERIAL_FINAL_Q";
|
||||
private static final Pattern GENERATED_B_RUBBER_SPEC_PATTERN = Pattern.compile("^B\\d", Pattern.CASE_INSENSITIVE);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A49】删除混炼示方时同步删除B/F段胶密炼物料-----------
|
||||
|
||||
@Resource
|
||||
private MesXslMixingSpecMaterialMapper materialMapper;
|
||||
@@ -42,6 +60,12 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
@Resource
|
||||
private MesXslMixingSpecTcuMapper tcuMapper;
|
||||
|
||||
@Resource
|
||||
private IMesMixerMaterialService mesMixerMaterialService;
|
||||
|
||||
@Resource
|
||||
private ISysCategoryService sysCategoryService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveMain(
|
||||
@@ -50,10 +74,17 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
List<MesXslMixingSpecStep> stepList,
|
||||
List<MesXslMixingSpecDownStep> downStepList,
|
||||
List<MesXslMixingSpecTcu> tcuList) {
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
SavePerfTrace trace = new SavePerfTrace("新增", main);
|
||||
trace.logPayloadSize(materialList, stepList, downStepList, tcuList);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】混炼示方主子表新增保存-----------
|
||||
normalizeMain(main);
|
||||
trace.step("normalizeMain");
|
||||
this.save(main);
|
||||
saveChildren(main.getId(), materialList, stepList, downStepList, tcuList);
|
||||
trace.step("saveMain", "mainId", main.getId());
|
||||
saveChildren(main.getId(), materialList, stepList, downStepList, tcuList, trace);
|
||||
trace.finish();
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】混炼示方主子表新增保存-----------
|
||||
}
|
||||
|
||||
@@ -65,19 +96,32 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
List<MesXslMixingSpecStep> stepList,
|
||||
List<MesXslMixingSpecDownStep> downStepList,
|
||||
List<MesXslMixingSpecTcu> tcuList) {
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
SavePerfTrace trace = new SavePerfTrace("编辑", main);
|
||||
trace.logPayloadSize(materialList, stepList, downStepList, tcuList);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】混炼示方主子表编辑保存-----------
|
||||
normalizeMain(main);
|
||||
trace.step("normalizeMain");
|
||||
this.updateById(main);
|
||||
clearChildren(main.getId());
|
||||
saveChildren(main.getId(), materialList, stepList, downStepList, tcuList);
|
||||
trace.step("updateMain");
|
||||
clearChildren(main.getId(), trace);
|
||||
saveChildren(main.getId(), materialList, stepList, downStepList, tcuList, trace);
|
||||
trace.finish();
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】混炼示方主子表编辑保存-----------
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delMain(String id) {
|
||||
MesXslMixingSpec main = this.getById(id);
|
||||
clearChildren(id);
|
||||
this.removeById(id);
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A49】删除混炼示方时同步删除B/F段胶密炼物料-----------
|
||||
if (main != null) {
|
||||
syncDeleteGeneratedRubberMixerMaterial(main.getSpecName());
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A49】删除混炼示方时同步删除B/F段胶密炼物料-----------
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -203,10 +247,26 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
}
|
||||
|
||||
private void clearChildren(String mainId) {
|
||||
clearChildren(mainId, null);
|
||||
}
|
||||
|
||||
private void clearChildren(String mainId, SavePerfTrace trace) {
|
||||
materialMapper.delete(new LambdaQueryWrapper<MesXslMixingSpecMaterial>().eq(MesXslMixingSpecMaterial::getMixingSpecId, mainId));
|
||||
if (trace != null) {
|
||||
trace.step("deleteMaterialChildren");
|
||||
}
|
||||
stepMapper.delete(new LambdaQueryWrapper<MesXslMixingSpecStep>().eq(MesXslMixingSpecStep::getMixingSpecId, mainId));
|
||||
if (trace != null) {
|
||||
trace.step("deleteStepChildren");
|
||||
}
|
||||
downStepMapper.delete(new LambdaQueryWrapper<MesXslMixingSpecDownStep>().eq(MesXslMixingSpecDownStep::getMixingSpecId, mainId));
|
||||
if (trace != null) {
|
||||
trace.step("deleteDownStepChildren");
|
||||
}
|
||||
tcuMapper.delete(new LambdaQueryWrapper<MesXslMixingSpecTcu>().eq(MesXslMixingSpecTcu::getMixingSpecId, mainId));
|
||||
if (trace != null) {
|
||||
trace.step("deleteTcuChildren");
|
||||
}
|
||||
}
|
||||
|
||||
private void saveChildren(
|
||||
@@ -215,54 +275,142 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
List<MesXslMixingSpecStep> stepList,
|
||||
List<MesXslMixingSpecDownStep> downStepList,
|
||||
List<MesXslMixingSpecTcu> tcuList) {
|
||||
saveChildren(mainId, materialList, stepList, downStepList, tcuList, null);
|
||||
}
|
||||
|
||||
private void saveChildren(
|
||||
String mainId,
|
||||
List<MesXslMixingSpecMaterial> materialList,
|
||||
List<MesXslMixingSpecStep> stepList,
|
||||
List<MesXslMixingSpecDownStep> downStepList,
|
||||
List<MesXslMixingSpecTcu> tcuList,
|
||||
SavePerfTrace trace) {
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A45】混炼示方子表批量插入避免保存超时-----------
|
||||
Date now = new Date();
|
||||
saveMaterialChildren(mainId, materialList, now, trace);
|
||||
saveStepChildren(mainId, stepList, now, trace);
|
||||
saveDownStepChildren(mainId, downStepList, now, trace);
|
||||
saveTcuChildren(mainId, tcuList, now, trace);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A45】混炼示方子表批量插入避免保存超时-----------
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A45】混炼示方子表批量插入避免保存超时-----------
|
||||
private void saveMaterialChildren(String mainId, List<MesXslMixingSpecMaterial> materialList, Date now) {
|
||||
saveMaterialChildren(mainId, materialList, now, null);
|
||||
}
|
||||
|
||||
private void saveMaterialChildren(String mainId, List<MesXslMixingSpecMaterial> materialList, Date now, SavePerfTrace trace) {
|
||||
if (CollectionUtils.isEmpty(materialList)) {
|
||||
return;
|
||||
}
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
|
||||
fillMaterialAccumWeight(materialList);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
|
||||
if (trace != null) {
|
||||
trace.step("fillMaterialAccumWeight", "rows", countRows(materialList));
|
||||
}
|
||||
List<MesXslMixingSpecMaterial> rows = new ArrayList<>();
|
||||
int sort = 0;
|
||||
if (!CollectionUtils.isEmpty(materialList)) {
|
||||
for (MesXslMixingSpecMaterial row : materialList) {
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
row.setId(null);
|
||||
row.setMixingSpecId(mainId);
|
||||
row.setSortNo(sort++);
|
||||
if (row.getCreateTime() == null) {
|
||||
row.setCreateTime(now);
|
||||
}
|
||||
materialMapper.insert(row);
|
||||
for (MesXslMixingSpecMaterial row : materialList) {
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
row.setId(null);
|
||||
row.setMixingSpecId(mainId);
|
||||
row.setSortNo(sort++);
|
||||
if (row.getCreateTime() == null) {
|
||||
row.setCreateTime(now);
|
||||
}
|
||||
rows.add(row);
|
||||
}
|
||||
if (trace != null) {
|
||||
trace.step("buildMaterialRows", "rows", rows.size());
|
||||
}
|
||||
if (!rows.isEmpty()) {
|
||||
Db.saveBatch(rows);
|
||||
if (trace != null) {
|
||||
trace.step("batchInsertMaterial", "rows", rows.size());
|
||||
}
|
||||
}
|
||||
sort = 0;
|
||||
if (!CollectionUtils.isEmpty(stepList)) {
|
||||
for (MesXslMixingSpecStep row : stepList) {
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
row.setId(null);
|
||||
row.setMixingSpecId(mainId);
|
||||
row.setSortNo(sort++);
|
||||
if (row.getCreateTime() == null) {
|
||||
row.setCreateTime(now);
|
||||
}
|
||||
stepMapper.insert(row);
|
||||
}
|
||||
|
||||
private void saveStepChildren(String mainId, List<MesXslMixingSpecStep> stepList, Date now) {
|
||||
saveStepChildren(mainId, stepList, now, null);
|
||||
}
|
||||
|
||||
private void saveStepChildren(String mainId, List<MesXslMixingSpecStep> stepList, Date now, SavePerfTrace trace) {
|
||||
if (CollectionUtils.isEmpty(stepList)) {
|
||||
return;
|
||||
}
|
||||
List<MesXslMixingSpecStep> rows = new ArrayList<>();
|
||||
int sort = 0;
|
||||
for (MesXslMixingSpecStep row : stepList) {
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
row.setId(null);
|
||||
row.setMixingSpecId(mainId);
|
||||
row.setSortNo(sort++);
|
||||
if (row.getCreateTime() == null) {
|
||||
row.setCreateTime(now);
|
||||
}
|
||||
rows.add(row);
|
||||
}
|
||||
if (trace != null) {
|
||||
trace.step("buildStepRows", "rows", rows.size());
|
||||
}
|
||||
if (!rows.isEmpty()) {
|
||||
Db.saveBatch(rows);
|
||||
if (trace != null) {
|
||||
trace.step("batchInsertStep", "rows", rows.size());
|
||||
}
|
||||
}
|
||||
sort = 0;
|
||||
if (!CollectionUtils.isEmpty(downStepList)) {
|
||||
for (MesXslMixingSpecDownStep row : downStepList) {
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
row.setId(null);
|
||||
row.setMixingSpecId(mainId);
|
||||
row.setSortNo(sort++);
|
||||
if (row.getCreateTime() == null) {
|
||||
row.setCreateTime(now);
|
||||
}
|
||||
downStepMapper.insert(row);
|
||||
}
|
||||
|
||||
private void saveDownStepChildren(String mainId, List<MesXslMixingSpecDownStep> downStepList, Date now) {
|
||||
saveDownStepChildren(mainId, downStepList, now, null);
|
||||
}
|
||||
|
||||
private void saveDownStepChildren(String mainId, List<MesXslMixingSpecDownStep> downStepList, Date now, SavePerfTrace trace) {
|
||||
if (CollectionUtils.isEmpty(downStepList)) {
|
||||
return;
|
||||
}
|
||||
List<MesXslMixingSpecDownStep> rows = new ArrayList<>();
|
||||
int sort = 0;
|
||||
for (MesXslMixingSpecDownStep row : downStepList) {
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
row.setId(null);
|
||||
row.setMixingSpecId(mainId);
|
||||
row.setSortNo(sort++);
|
||||
if (row.getCreateTime() == null) {
|
||||
row.setCreateTime(now);
|
||||
}
|
||||
rows.add(row);
|
||||
}
|
||||
if (trace != null) {
|
||||
trace.step("buildDownStepRows", "rows", rows.size());
|
||||
}
|
||||
if (!rows.isEmpty()) {
|
||||
Db.saveBatch(rows);
|
||||
if (trace != null) {
|
||||
trace.step("batchInsertDownStep", "rows", rows.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveTcuChildren(String mainId, List<MesXslMixingSpecTcu> tcuList, Date now) {
|
||||
saveTcuChildren(mainId, tcuList, now, null);
|
||||
}
|
||||
|
||||
private void saveTcuChildren(String mainId, List<MesXslMixingSpecTcu> tcuList, Date now, SavePerfTrace trace) {
|
||||
List<MesXslMixingSpecTcu> tcuRows = fillDefaultTcuRows(tcuList);
|
||||
sort = 0;
|
||||
if (trace != null) {
|
||||
trace.step("fillDefaultTcuRows", "rows", tcuRows.size());
|
||||
}
|
||||
List<MesXslMixingSpecTcu> rows = new ArrayList<>();
|
||||
int sort = 0;
|
||||
for (MesXslMixingSpecTcu row : tcuRows) {
|
||||
row.setId(null);
|
||||
row.setMixingSpecId(mainId);
|
||||
@@ -273,9 +421,139 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
if (TCU_DOWN.equals(row.getSectionType())) {
|
||||
row.setDrugWeighPos(null);
|
||||
}
|
||||
tcuMapper.insert(row);
|
||||
rows.add(row);
|
||||
}
|
||||
if (trace != null) {
|
||||
trace.step("buildTcuRows", "rows", rows.size());
|
||||
}
|
||||
if (!rows.isEmpty()) {
|
||||
Db.saveBatch(rows);
|
||||
if (trace != null) {
|
||||
trace.step("batchInsertTcu", "rows", rows.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A45】混炼示方子表批量插入避免保存超时-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
private static int countRows(List<?> rows) {
|
||||
return rows == null ? 0 : rows.size();
|
||||
}
|
||||
|
||||
/** 混炼示方保存性能追踪(临时诊断日志,定位慢步骤) */
|
||||
private static final class SavePerfTrace {
|
||||
private final String action;
|
||||
private final String mainId;
|
||||
private final String specName;
|
||||
private final long startMs = System.currentTimeMillis();
|
||||
private long lastMs = startMs;
|
||||
private final StringBuilder summary = new StringBuilder();
|
||||
|
||||
private SavePerfTrace(String action, MesXslMixingSpec main) {
|
||||
this.action = action;
|
||||
this.mainId = main != null ? main.getId() : null;
|
||||
this.specName = main != null ? main.getSpecName() : null;
|
||||
summary.append("action=").append(action);
|
||||
if (StringUtils.isNotBlank(mainId)) {
|
||||
summary.append(", mainId=").append(mainId);
|
||||
}
|
||||
if (StringUtils.isNotBlank(specName)) {
|
||||
summary.append(", specName=").append(specName);
|
||||
}
|
||||
}
|
||||
|
||||
private void logPayloadSize(
|
||||
List<MesXslMixingSpecMaterial> materialList,
|
||||
List<MesXslMixingSpecStep> stepList,
|
||||
List<MesXslMixingSpecDownStep> downStepList,
|
||||
List<MesXslMixingSpecTcu> tcuList) {
|
||||
log.info(
|
||||
"[混炼示方保存性能] 开始{} specName={}, mainId={}, payload=[material={}, step={}, downStep={}, tcu={}]",
|
||||
action,
|
||||
specName,
|
||||
mainId,
|
||||
countRows(materialList),
|
||||
countRows(stepList),
|
||||
countRows(downStepList),
|
||||
countRows(tcuList));
|
||||
}
|
||||
|
||||
private void step(String stepName, Object... kvPairs) {
|
||||
long now = System.currentTimeMillis();
|
||||
long stepMs = now - lastMs;
|
||||
long totalMs = now - startMs;
|
||||
summary.append(" | ").append(stepName).append('=').append(stepMs).append("ms");
|
||||
if (kvPairs != null && kvPairs.length > 0) {
|
||||
summary.append('(');
|
||||
for (int i = 0; i < kvPairs.length; i += 2) {
|
||||
if (i > 0) {
|
||||
summary.append(", ");
|
||||
}
|
||||
summary.append(kvPairs[i]).append('=').append(kvPairs[i + 1]);
|
||||
}
|
||||
summary.append(')');
|
||||
}
|
||||
log.info("[混炼示方保存性能] {} 步骤={} 本步耗时={}ms 累计={}ms", action, stepName, stepMs, totalMs);
|
||||
lastMs = now;
|
||||
}
|
||||
|
||||
private void finish() {
|
||||
log.info("[混炼示方保存性能] 完成{} 总耗时={}ms | {}", action, System.currentTimeMillis() - startMs, summary);
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A47】混炼示方保存分步骤性能日志-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
|
||||
/** 按种类连续分组,累计写入每组最后一行 */
|
||||
private void fillMaterialAccumWeight(List<MesXslMixingSpecMaterial> materials) {
|
||||
if (CollectionUtils.isEmpty(materials)) {
|
||||
return;
|
||||
}
|
||||
int index = 0;
|
||||
while (index < materials.size()) {
|
||||
MesXslMixingSpecMaterial current = materials.get(index);
|
||||
if (!isMaterialDataRow(current)) {
|
||||
current.setAccumWeight(null);
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
String kind = normalizeMaterialKind(current);
|
||||
int groupEnd = index;
|
||||
BigDecimal sum = BigDecimal.ZERO;
|
||||
while (groupEnd < materials.size()) {
|
||||
MesXslMixingSpecMaterial row = materials.get(groupEnd);
|
||||
if (!isMaterialDataRow(row) || !kind.equals(normalizeMaterialKind(row))) {
|
||||
break;
|
||||
}
|
||||
if (row.getUnitWeight() != null) {
|
||||
sum = sum.add(row.getUnitWeight());
|
||||
}
|
||||
groupEnd++;
|
||||
}
|
||||
for (int rowIndex = index; rowIndex < groupEnd; rowIndex++) {
|
||||
if (rowIndex == groupEnd - 1) {
|
||||
materials.get(rowIndex).setAccumWeight(sum.compareTo(BigDecimal.ZERO) != 0 ? sum : null);
|
||||
} else {
|
||||
materials.get(rowIndex).setAccumWeight(null);
|
||||
}
|
||||
}
|
||||
index = groupEnd;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMaterialDataRow(MesXslMixingSpecMaterial row) {
|
||||
if (row == null) {
|
||||
return false;
|
||||
}
|
||||
return StringUtils.isNotBlank(row.getMixerMaterialName())
|
||||
|| row.getUnitWeight() != null
|
||||
|| StringUtils.isNotBlank(row.getMaterialKind());
|
||||
}
|
||||
|
||||
private String normalizeMaterialKind(MesXslMixingSpecMaterial row) {
|
||||
return StringUtils.isNotBlank(row.getMaterialKind()) ? row.getMaterialKind().trim() : "";
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
|
||||
|
||||
private List<MesXslMixingSpecMaterial> queryMaterialByMainId(String mainId) {
|
||||
return materialMapper.selectList(
|
||||
@@ -336,4 +614,112 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl<MesXslMixingSpecMap
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A49】删除混炼示方时同步删除B/F段胶密炼物料-----------
|
||||
/**
|
||||
* 删除混炼示方后,若该示方编号已无其它混炼示方且未被其它示方明细引用,则同步删除生成时写入的 B/F 段胶密炼物料。
|
||||
*/
|
||||
private void syncDeleteGeneratedRubberMixerMaterial(String specName) {
|
||||
if (StringUtils.isBlank(specName)) {
|
||||
return;
|
||||
}
|
||||
String specCode = specName.trim();
|
||||
Boolean isFinalStage = resolveGeneratedRubberSegment(specCode);
|
||||
if (isFinalStage == null) {
|
||||
return;
|
||||
}
|
||||
long remainingSpecCount =
|
||||
this.count(new LambdaQueryWrapper<MesXslMixingSpec>().eq(MesXslMixingSpec::getSpecName, specCode));
|
||||
if (remainingSpecCount > 0) {
|
||||
log.debug("[混炼示方删除] 示方编号 {} 仍有 {} 条混炼示方,跳过密炼物料同步删除", specCode, remainingSpecCount);
|
||||
return;
|
||||
}
|
||||
long referencedCount = materialMapper.selectCount(
|
||||
new LambdaQueryWrapper<MesXslMixingSpecMaterial>().eq(MesXslMixingSpecMaterial::getMixerMaterialName, specCode));
|
||||
if (referencedCount > 0) {
|
||||
log.info(
|
||||
"[混炼示方删除] 示方编号 {} 仍被 {} 条混炼示方明细引用,跳过密炼物料同步删除",
|
||||
specCode,
|
||||
referencedCount);
|
||||
return;
|
||||
}
|
||||
MesMixerMaterial material = findMixerMaterialByCodeOrName(specCode);
|
||||
if (material == null) {
|
||||
log.debug("[混炼示方删除] 未找到示方编号 {} 对应密炼物料,跳过同步删除", specCode);
|
||||
return;
|
||||
}
|
||||
Map<String, String> categoryIdCache = new HashMap<>();
|
||||
if (!isGeneratedRubberMixerMaterial(material, isFinalStage, categoryIdCache)) {
|
||||
log.info(
|
||||
"[混炼示方删除] 密炼物料 id={}, code={} 分类非生成示方自动同步类型,跳过删除",
|
||||
material.getId(),
|
||||
specCode);
|
||||
return;
|
||||
}
|
||||
mesMixerMaterialService.removeById(material.getId());
|
||||
log.info("[混炼示方删除] 同步删除密炼物料 id={}, code={}", material.getId(), specCode);
|
||||
}
|
||||
|
||||
/** 判断是否为生成混炼示方时自动同步的 B/F 段胶示方编号;true=F段,false=B段,null=非自动生成胶料编号 */
|
||||
private Boolean resolveGeneratedRubberSegment(String specCode) {
|
||||
if (StringUtils.isBlank(specCode)) {
|
||||
return null;
|
||||
}
|
||||
String normalized = specCode.trim();
|
||||
if (normalized.toUpperCase().startsWith("F")) {
|
||||
return true;
|
||||
}
|
||||
if (GENERATED_B_RUBBER_SPEC_PATTERN.matcher(normalized).find()) {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MesMixerMaterial findMixerMaterialByCodeOrName(String specCode) {
|
||||
MesMixerMaterial byCode = mesMixerMaterialService.getOne(
|
||||
new LambdaQueryWrapper<MesMixerMaterial>()
|
||||
.eq(MesMixerMaterial::getMaterialCode, specCode)
|
||||
.and(w -> w.eq(MesMixerMaterial::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesMixerMaterial::getDelFlag))
|
||||
.last("limit 1"));
|
||||
if (byCode != null) {
|
||||
return byCode;
|
||||
}
|
||||
return mesMixerMaterialService.getOne(
|
||||
new LambdaQueryWrapper<MesMixerMaterial>()
|
||||
.eq(MesMixerMaterial::getMaterialName, specCode)
|
||||
.and(w -> w.eq(MesMixerMaterial::getDelFlag, CommonConstant.DEL_FLAG_0).or().isNull(MesMixerMaterial::getDelFlag))
|
||||
.last("limit 1"));
|
||||
}
|
||||
|
||||
private boolean isGeneratedRubberMixerMaterial(
|
||||
MesMixerMaterial material, boolean isFinalStage, Map<String, String> categoryIdCache) {
|
||||
if (material == null) {
|
||||
return false;
|
||||
}
|
||||
String majorCode = isFinalStage ? CATEGORY_FINAL_MAJOR : CATEGORY_MASTER_MAJOR;
|
||||
String minorCode = isFinalStage ? CATEGORY_FINAL_MINOR_Q : CATEGORY_MASTER_MINOR_A;
|
||||
String expectedMajorId = resolveCategoryIdByCode(majorCode, categoryIdCache);
|
||||
String expectedMinorId = resolveCategoryIdByCode(minorCode, categoryIdCache);
|
||||
if (StringUtils.isBlank(expectedMajorId) || StringUtils.isBlank(expectedMinorId)) {
|
||||
return false;
|
||||
}
|
||||
return expectedMajorId.equals(material.getMajorCategoryId()) && expectedMinorId.equals(material.getMinorCategoryId());
|
||||
}
|
||||
|
||||
private String resolveCategoryIdByCode(String categoryCode, Map<String, String> cache) {
|
||||
if (StringUtils.isBlank(categoryCode)) {
|
||||
return null;
|
||||
}
|
||||
if (cache != null && cache.containsKey(categoryCode)) {
|
||||
return cache.get(categoryCode);
|
||||
}
|
||||
SysCategory category = sysCategoryService.getOne(
|
||||
new LambdaQueryWrapper<SysCategory>().eq(SysCategory::getCode, categoryCode.trim()).last("limit 1"));
|
||||
String categoryId = category != null ? category.getId() : null;
|
||||
if (cache != null) {
|
||||
cache.put(categoryCode, categoryId);
|
||||
}
|
||||
return categoryId;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A49】删除混炼示方时同步删除B/F段胶密炼物料-----------
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class SysCategoryServiceImpl extends ServiceImpl<SysCategoryMapper, SysCategory> implements ISysCategoryService {
|
||||
|
||||
/** 原辅材料分类编码(其子类可标记为胶料) */
|
||||
private static final String MATERIAL_RAW_AUX_CODE = "XSLMES_MATERIAL_RAW_AUX";
|
||||
/** MES 物料大类编码前缀(其直接子类为物料小类,可标记胶料) */
|
||||
private static final String MATERIAL_MAJOR_CODE_PREFIX = "XSLMES_MATERIAL_";
|
||||
|
||||
@Override
|
||||
public void addSysCategory(SysCategory sysCategory) {
|
||||
@@ -103,9 +103,9 @@ public class SysCategoryServiceImpl extends ServiceImpl<SysCategoryMapper, SysCa
|
||||
baseMapper.updateById(sysCategory);
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A31】原辅材料子类胶料标记归一化-----------
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】MES物料小类均可维护是否胶料-----------
|
||||
/**
|
||||
* 胶料标记仅对「原辅材料」直接子类生效,其余节点强制为 0。
|
||||
* 胶料标记仅对 MES 物料小类(父节点为物料大类)生效,其余节点强制为 0。
|
||||
*/
|
||||
private void normalizeIsRubber(SysCategory sysCategory) {
|
||||
if (sysCategory == null) {
|
||||
@@ -115,16 +115,22 @@ public class SysCategoryServiceImpl extends ServiceImpl<SysCategoryMapper, SysCa
|
||||
sysCategory.setIsRubber("0");
|
||||
return;
|
||||
}
|
||||
if (oConvertUtils.isEmpty(sysCategory.getPid()) || ISysCategoryService.ROOT_PID_VALUE.equals(sysCategory.getPid())) {
|
||||
sysCategory.setIsRubber("0");
|
||||
return;
|
||||
}
|
||||
SysCategory parent = baseMapper.selectById(sysCategory.getPid());
|
||||
if (parent == null || !MATERIAL_RAW_AUX_CODE.equals(parent.getCode())) {
|
||||
if (!isMaterialMinorCategory(sysCategory)) {
|
||||
sysCategory.setIsRubber("0");
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A31】原辅材料子类胶料标记归一化-----------
|
||||
|
||||
private boolean isMaterialMinorCategory(SysCategory category) {
|
||||
if (category == null || oConvertUtils.isEmpty(category.getPid())) {
|
||||
return false;
|
||||
}
|
||||
SysCategory parent = baseMapper.selectById(category.getPid());
|
||||
if (parent == null || oConvertUtils.isEmpty(parent.getCode())) {
|
||||
return false;
|
||||
}
|
||||
return parent.getCode().startsWith(MATERIAL_MAJOR_CODE_PREFIX);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】MES物料小类均可维护是否胶料-----------
|
||||
|
||||
@Override
|
||||
public List<TreeSelectModel> queryListByCode(String pcode) throws JeecgBootException{
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
-- 密炼物料种类配置:建表 + 菜单(挂 MES技术管理)+ admin 授权
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mes_xsl_mixer_material_kind_cfg` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`kind_key` varchar(100) NOT NULL COMMENT '种类键值',
|
||||
`kind_name` varchar(200) NOT NULL COMMENT '种类名称',
|
||||
`source_type` varchar(20) NOT NULL COMMENT '数据源类型:dict数据字典/category分类字典',
|
||||
`source_root_code` varchar(100) DEFAULT NULL COMMENT '根字典编码或分类pcode',
|
||||
`source_root_name` varchar(200) DEFAULT NULL COMMENT '根名称冗余',
|
||||
`category_ref_id` varchar(32) DEFAULT NULL COMMENT '对应分类/字典项ID',
|
||||
`category_ref_code` varchar(100) DEFAULT NULL COMMENT '对应分类编码/字典项值',
|
||||
`category_ref_name` varchar(200) DEFAULT NULL COMMENT '对应分类名称冗余',
|
||||
`tenant_id` int DEFAULT NULL COMMENT '租户ID',
|
||||
`priority` int DEFAULT '0' COMMENT '优先级(数字越小越优先)',
|
||||
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '所属部门',
|
||||
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(50) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`del_flag` int NOT NULL DEFAULT '0' COMMENT '逻辑删除(0正常 1已删除)',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_mxmmkc_tenant_priority` (`tenant_id`, `priority`),
|
||||
KEY `idx_mxmmkc_source_root` (`source_type`, `source_root_code`),
|
||||
KEY `idx_mxmmkc_category_ref` (`category_ref_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES密炼物料种类配置';
|
||||
|
||||
UPDATE `sys_permission`
|
||||
SET `is_leaf` = 0, `update_time` = NOW()
|
||||
WHERE `id` = '1900000000000000810' AND `is_leaf` = 1;
|
||||
|
||||
INSERT INTO `sys_permission` (
|
||||
`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`,
|
||||
`menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`,
|
||||
`hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`,
|
||||
`del_flag`, `rule_flag`, `status`, `internal_or_external`
|
||||
)
|
||||
SELECT
|
||||
'177925970995550', '1900000000000000810', '密炼物料种类配置', '/xslmes/mesXslMixerMaterialKindCfg',
|
||||
'xslmes/mesXslMixerMaterialKindCfg/MesXslMixerMaterialKindCfgList', 1, 'MesXslMixerMaterialKindCfgList', NULL,
|
||||
1, NULL, '0', 3.00, 0, 'ant-design:tags-outlined', 0, 1,
|
||||
0, 0, 'MES密炼物料种类配置', 'admin', NOW(), 'admin', NOW(),
|
||||
0, 0, '1', 0
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `sys_permission`
|
||||
WHERE `id` = '177925970995550'
|
||||
OR (`del_flag` = 0 AND `menu_type` = 1 AND `name` = '密炼物料种类配置' AND `parent_id` = '1900000000000000810')
|
||||
);
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995551', '177925970995550', '新增', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:add', '1', 1.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995551');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995552', '177925970995550', '编辑', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:edit', '1', 2.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995552');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995553', '177925970995550', '删除', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:delete', '1', 3.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995553');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995554', '177925970995550', '批量删除', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:deleteBatch', '1', 4.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995554');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995555', '177925970995550', '导出', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:exportXls', '1', 5.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995555');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995556', '177925970995550', '导入', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:importExcel', '1', 6.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995556');
|
||||
|
||||
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1'
|
||||
FROM `sys_role` r
|
||||
CROSS JOIN `sys_permission` p
|
||||
WHERE r.`role_code` = 'admin'
|
||||
AND p.`id` IN (
|
||||
'177925970995550',
|
||||
'177925970995551',
|
||||
'177925970995552',
|
||||
'177925970995553',
|
||||
'177925970995554',
|
||||
'177925970995555',
|
||||
'177925970995556'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `sys_role_permission` rp WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||
);
|
||||
@@ -0,0 +1,67 @@
|
||||
-- 密炼物料种类配置:菜单 ID 与配合示方(177925970995530)冲突,补插正确菜单
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
UPDATE `sys_permission`
|
||||
SET `is_leaf` = 0, `update_time` = NOW()
|
||||
WHERE `id` = '1900000000000000810';
|
||||
|
||||
INSERT INTO `sys_permission` (
|
||||
`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`,
|
||||
`menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`,
|
||||
`hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`,
|
||||
`del_flag`, `rule_flag`, `status`, `internal_or_external`
|
||||
)
|
||||
SELECT
|
||||
'177925970995550', '1900000000000000810', '密炼物料种类配置', '/xslmes/mesXslMixerMaterialKindCfg',
|
||||
'xslmes/mesXslMixerMaterialKindCfg/MesXslMixerMaterialKindCfgList', 1, 'MesXslMixerMaterialKindCfgList', NULL,
|
||||
1, NULL, '0', 3.00, 0, 'ant-design:tags-outlined', 0, 1,
|
||||
0, 0, 'MES密炼物料种类配置', 'admin', NOW(), 'admin', NOW(),
|
||||
0, 0, '1', 0
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `sys_permission`
|
||||
WHERE `id` = '177925970995550'
|
||||
OR (`del_flag` = 0 AND `menu_type` = 1 AND `name` = '密炼物料种类配置' AND `parent_id` = '1900000000000000810')
|
||||
);
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995551', '177925970995550', '新增', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:add', '1', 1.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995551');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995552', '177925970995550', '编辑', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:edit', '1', 2.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995552');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995553', '177925970995550', '删除', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:delete', '1', 3.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995553');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995554', '177925970995550', '批量删除', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:deleteBatch', '1', 4.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995554');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995555', '177925970995550', '导出', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:exportXls', '1', 5.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995555');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `create_by`, `create_time`)
|
||||
SELECT '177925970995556', '177925970995550', '导入', 2, 'xslmes:mes_xsl_mixer_material_kind_cfg:importExcel', '1', 6.00, 0, 1, 0, '1', 0, 'admin', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '177925970995556');
|
||||
|
||||
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NULL, NOW(), '127.0.0.1'
|
||||
FROM `sys_role` r
|
||||
CROSS JOIN `sys_permission` p
|
||||
WHERE r.`role_code` = 'admin'
|
||||
AND p.`id` IN (
|
||||
'177925970995550',
|
||||
'177925970995551',
|
||||
'177925970995552',
|
||||
'177925970995553',
|
||||
'177925970995554',
|
||||
'177925970995555',
|
||||
'177925970995556'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `sys_role_permission` rp WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||
);
|
||||
@@ -13,7 +13,8 @@ import { loadTreeData } from '/@/views/system/category/category.api';
|
||||
import {
|
||||
MATERIAL_RAW_AUX_CODE,
|
||||
materialRawAuxCategoryId,
|
||||
isMaterialRawAuxSubCategory,
|
||||
ensureMaterialCategoryContext,
|
||||
isMaterialMinorCategory,
|
||||
toIsRubberFlag,
|
||||
fromIsRubberFlag,
|
||||
} from '/@/views/system/category/category.constants';
|
||||
@@ -48,7 +49,7 @@ const schemas: FormSchema[] = [
|
||||
defaultValue: false,
|
||||
renderComponentContent: '胶料',
|
||||
colProps: { span: 24 },
|
||||
ifShow: ({ values }) => isMaterialRawAuxSubCategory(values.pid),
|
||||
ifShow: ({ values }) => isMaterialMinorCategory(values.pid),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -95,7 +96,7 @@ async function ensureMaterialRawAuxCategoryId() {
|
||||
|
||||
function normalizeSubmitValues(values: Recordable) {
|
||||
const payload = { ...values };
|
||||
payload.isRubber = isMaterialRawAuxSubCategory(payload.pid) ? toIsRubberFlag(payload.isRubber) : '0';
|
||||
payload.isRubber = isMaterialMinorCategory(payload.pid) ? toIsRubberFlag(payload.isRubber) : '0';
|
||||
return payload;
|
||||
}
|
||||
|
||||
@@ -104,6 +105,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
|
||||
setModalProps({ confirmLoading: false });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
await ensureMaterialRawAuxCategoryId();
|
||||
await ensureMaterialCategoryContext();
|
||||
const tree = await buildPidTree();
|
||||
if (!tree.length) {
|
||||
createMessage.warning('未加载到物料分类树,请确认分类字典根编码 XSLMES_MATERIAL 已存在');
|
||||
|
||||
@@ -1,16 +1,78 @@
|
||||
import { ref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { getChildListBatch, loadTreeData } from './category.api';
|
||||
|
||||
/** MES 物料分类 - 原辅材料编码 */
|
||||
/** MES 物料分类根编码 */
|
||||
export const MATERIAL_ROOT_CODE = 'XSLMES_MATERIAL';
|
||||
|
||||
/** MES 物料分类 - 原辅材料编码(兼容旧逻辑) */
|
||||
export const MATERIAL_RAW_AUX_CODE = 'XSLMES_MATERIAL_RAW_AUX';
|
||||
|
||||
/** 原辅材料分类节点 ID(运行时加载) */
|
||||
/** 原辅材料分类节点 ID(运行时加载,兼容旧逻辑) */
|
||||
export const materialRawAuxCategoryId = ref('');
|
||||
|
||||
/** 是否为原辅材料的直接子类 */
|
||||
/** MES 物料分类根节点 ID(运行时加载) */
|
||||
export const materialRootCategoryId = ref('');
|
||||
|
||||
/** MES 物料大类节点 ID 集合(运行时加载) */
|
||||
export const materialMajorCategoryIds = ref<Set<string>>(new Set());
|
||||
|
||||
let materialCategoryContextLoading: Promise<void> | null = null;
|
||||
|
||||
/** 加载 MES 物料分类上下文(根节点 + 大类 ID) */
|
||||
export async function ensureMaterialCategoryContext(force = false) {
|
||||
if (!force && materialRootCategoryId.value && materialMajorCategoryIds.value.size > 0) {
|
||||
return;
|
||||
}
|
||||
if (materialCategoryContextLoading) {
|
||||
await materialCategoryContextLoading;
|
||||
return;
|
||||
}
|
||||
materialCategoryContextLoading = (async () => {
|
||||
const rootRes = await defHttp.get(
|
||||
{ url: '/sys/category/loadOne', params: { field: 'code', val: MATERIAL_ROOT_CODE } },
|
||||
{ isTransformResponse: false },
|
||||
);
|
||||
if (rootRes?.success && rootRes?.result?.id) {
|
||||
materialRootCategoryId.value = String(rootRes.result.id);
|
||||
}
|
||||
const auxRes = await defHttp.get(
|
||||
{ url: '/sys/category/loadOne', params: { field: 'code', val: MATERIAL_RAW_AUX_CODE } },
|
||||
{ isTransformResponse: false },
|
||||
);
|
||||
if (auxRes?.success && auxRes?.result?.id) {
|
||||
materialRawAuxCategoryId.value = String(auxRes.result.id);
|
||||
}
|
||||
const majors = await loadTreeData({ async: false, pcode: MATERIAL_ROOT_CODE });
|
||||
const majorIds = new Set<string>();
|
||||
(Array.isArray(majors) ? majors : []).forEach((node) => {
|
||||
const nodeKey = node?.key ?? node?.value ?? node?.id;
|
||||
if (nodeKey != null) {
|
||||
majorIds.add(String(nodeKey));
|
||||
}
|
||||
});
|
||||
materialMajorCategoryIds.value = majorIds;
|
||||
})();
|
||||
try {
|
||||
await materialCategoryContextLoading;
|
||||
} finally {
|
||||
materialCategoryContextLoading = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 是否为原辅材料的直接子类(兼容旧逻辑) */
|
||||
export function isMaterialRawAuxSubCategory(pid?: string) {
|
||||
return !!materialRawAuxCategoryId.value && pid === materialRawAuxCategoryId.value;
|
||||
}
|
||||
|
||||
/** 是否为 MES 物料小类(父节点为物料大类) */
|
||||
export function isMaterialMinorCategory(pid?: string) {
|
||||
if (!pid) {
|
||||
return false;
|
||||
}
|
||||
return materialMajorCategoryIds.value.has(String(pid));
|
||||
}
|
||||
|
||||
/** 表单 Checkbox 布尔值 -> 数据库存储值 */
|
||||
export function toIsRubberFlag(value: unknown) {
|
||||
return value === true || value === '1' ? '1' : '0';
|
||||
@@ -20,3 +82,206 @@ export function toIsRubberFlag(value: unknown) {
|
||||
export function fromIsRubberFlag(value: unknown) {
|
||||
return value === '1' || value === 1 || value === true;
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】统一加载MES物料分类树(大类+小类)-----------
|
||||
export interface MesMaterialCategoryMinorItem {
|
||||
id: string;
|
||||
name: string;
|
||||
majorId: string;
|
||||
majorName: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface MesMaterialCategoryMajorItem {
|
||||
id: string;
|
||||
name: string;
|
||||
minors: MesMaterialCategoryMinorItem[];
|
||||
}
|
||||
|
||||
export interface MesMaterialCategoryTreeLoadResult {
|
||||
majors: MesMaterialCategoryMajorItem[];
|
||||
minors: MesMaterialCategoryMinorItem[];
|
||||
treeNodes: Recordable[];
|
||||
}
|
||||
|
||||
function normalizeCategoryNodeKey(node: Recordable): string {
|
||||
const key = node?.key ?? node?.value ?? node?.id;
|
||||
return key != null && String(key) !== '' ? String(key) : '';
|
||||
}
|
||||
|
||||
function normalizeCategoryNodeTitle(node: Recordable): string {
|
||||
return String(node?.title ?? node?.name ?? '');
|
||||
}
|
||||
|
||||
function normalizeTreeSelectNodes(nodes: unknown): Recordable[] {
|
||||
if (Array.isArray(nodes)) {
|
||||
return nodes as Recordable[];
|
||||
}
|
||||
if (nodes && typeof nodes === 'object') {
|
||||
const payload = nodes as Recordable;
|
||||
if (Array.isArray(payload.result)) {
|
||||
return payload.result;
|
||||
}
|
||||
if (Array.isArray(payload.records)) {
|
||||
return payload.records;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/** 加载 MES 物料分类树:根下物料大类 + 各物料小类(供密炼物料/混炼示方选料复用) */
|
||||
export async function loadMesMaterialCategoryTreeData(): Promise<MesMaterialCategoryTreeLoadResult> {
|
||||
// 优先 loadTreeRoot(与密炼物料列表页一致,已验证可用)
|
||||
try {
|
||||
const treeRes = await loadTreeData({ async: false, pcode: MATERIAL_ROOT_CODE });
|
||||
const treeResult = buildMesMaterialCategoryTreeFromTreeSelect(normalizeTreeSelectNodes(treeRes));
|
||||
if (treeResult.minors.length) {
|
||||
if (!materialRootCategoryId.value) {
|
||||
await ensureMaterialCategoryContext();
|
||||
}
|
||||
return treeResult;
|
||||
}
|
||||
} catch {
|
||||
// 继续走 batch 兜底
|
||||
}
|
||||
|
||||
let rootId = materialRootCategoryId.value;
|
||||
if (!rootId) {
|
||||
try {
|
||||
const root = await defHttp.get<Recordable>({
|
||||
url: '/sys/category/loadOne',
|
||||
params: { field: 'code', val: MATERIAL_ROOT_CODE },
|
||||
});
|
||||
if (root?.id) {
|
||||
rootId = String(root.id);
|
||||
materialRootCategoryId.value = rootId;
|
||||
}
|
||||
} catch {
|
||||
rootId = '';
|
||||
}
|
||||
}
|
||||
if (!rootId) {
|
||||
return { majors: [], minors: [], treeNodes: [] };
|
||||
}
|
||||
|
||||
let majorRecords: Recordable[] = [];
|
||||
let minorRecords: Recordable[] = [];
|
||||
try {
|
||||
const majorBatch = await getChildListBatch({ parentIds: rootId });
|
||||
if (majorBatch?.success === false) {
|
||||
return { majors: [], minors: [], treeNodes: [] };
|
||||
}
|
||||
majorRecords = (majorBatch?.result?.records || majorBatch?.records || []) as Recordable[];
|
||||
if (majorRecords.length) {
|
||||
const majorIds = majorRecords.map((item) => String(item.id)).filter(Boolean).join(',');
|
||||
if (majorIds) {
|
||||
const minorBatch = await getChildListBatch({ parentIds: majorIds });
|
||||
if (minorBatch?.success !== false) {
|
||||
minorRecords = (minorBatch?.result?.records || minorBatch?.records || []) as Recordable[];
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return { majors: [], minors: [], treeNodes: [] };
|
||||
}
|
||||
|
||||
const result = buildMesMaterialCategoryTreeFromRecords(majorRecords, minorRecords);
|
||||
materialMajorCategoryIds.value = new Set(result.majors.map((item) => item.id));
|
||||
return result;
|
||||
}
|
||||
|
||||
function buildMesMaterialCategoryTreeFromRecords(majorRecords: Recordable[], minorRecords: Recordable[]) {
|
||||
const majorMap = new Map<string, { id: string; name: string; minors: MesMaterialCategoryMinorItem[] }>();
|
||||
majorRecords.forEach((record) => {
|
||||
const majorId = normalizeCategoryNodeKey(record);
|
||||
if (!majorId) {
|
||||
return;
|
||||
}
|
||||
majorMap.set(majorId, {
|
||||
id: majorId,
|
||||
name: normalizeCategoryNodeTitle(record),
|
||||
minors: [],
|
||||
});
|
||||
});
|
||||
|
||||
minorRecords.forEach((record) => {
|
||||
const majorId = record?.pid != null ? String(record.pid) : '';
|
||||
const minorId = normalizeCategoryNodeKey(record);
|
||||
const major = majorMap.get(majorId);
|
||||
if (!major || !minorId) {
|
||||
return;
|
||||
}
|
||||
major.minors.push({
|
||||
id: minorId,
|
||||
name: normalizeCategoryNodeTitle(record),
|
||||
majorId: major.id,
|
||||
majorName: major.name,
|
||||
label: major.name && normalizeCategoryNodeTitle(record)
|
||||
? `${major.name} / ${normalizeCategoryNodeTitle(record)}`
|
||||
: normalizeCategoryNodeTitle(record) || major.name,
|
||||
});
|
||||
});
|
||||
|
||||
const majors: MesMaterialCategoryMajorItem[] = [];
|
||||
const minors: MesMaterialCategoryMinorItem[] = [];
|
||||
const treeNodes: Recordable[] = [];
|
||||
|
||||
majorMap.forEach((major) => {
|
||||
if (!major.minors.length) {
|
||||
return;
|
||||
}
|
||||
majors.push({ id: major.id, name: major.name, minors: major.minors });
|
||||
minors.push(...major.minors);
|
||||
treeNodes.push({
|
||||
key: major.id,
|
||||
title: major.name,
|
||||
children: major.minors.map((minor) => ({ key: minor.id, title: minor.name })),
|
||||
});
|
||||
});
|
||||
|
||||
return { majors, minors, treeNodes };
|
||||
}
|
||||
|
||||
function buildMesMaterialCategoryTreeFromTreeSelect(majorRaw: Recordable[]) {
|
||||
const majors: MesMaterialCategoryMajorItem[] = [];
|
||||
const minors: MesMaterialCategoryMinorItem[] = [];
|
||||
const treeNodes: Recordable[] = [];
|
||||
|
||||
majorRaw.forEach((major) => {
|
||||
const majorId = normalizeCategoryNodeKey(major);
|
||||
const majorName = normalizeCategoryNodeTitle(major);
|
||||
if (!majorId) {
|
||||
return;
|
||||
}
|
||||
const majorMinors: MesMaterialCategoryMinorItem[] = [];
|
||||
(Array.isArray(major.children) ? major.children : []).forEach((child) => {
|
||||
const minorId = normalizeCategoryNodeKey(child);
|
||||
const minorName = normalizeCategoryNodeTitle(child);
|
||||
if (!minorId) {
|
||||
return;
|
||||
}
|
||||
const item: MesMaterialCategoryMinorItem = {
|
||||
id: minorId,
|
||||
name: minorName,
|
||||
majorId,
|
||||
majorName,
|
||||
label: majorName && minorName ? `${majorName} / ${minorName}` : minorName || majorName,
|
||||
};
|
||||
majorMinors.push(item);
|
||||
minors.push(item);
|
||||
});
|
||||
if (!majorMinors.length) {
|
||||
return;
|
||||
}
|
||||
majors.push({ id: majorId, name: majorName, minors: majorMinors });
|
||||
treeNodes.push({
|
||||
key: majorId,
|
||||
title: majorName,
|
||||
children: majorMinors.map((minor) => ({ key: minor.id, title: minor.name })),
|
||||
});
|
||||
});
|
||||
|
||||
materialMajorCategoryIds.value = new Set(majors.map((item) => item.id));
|
||||
return { majors, minors, treeNodes };
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】统一加载MES物料分类树(大类+小类)-----------
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import { isMaterialRawAuxSubCategory } from './category.constants';
|
||||
import { isMaterialMinorCategory } from './category.constants';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
@@ -81,6 +81,6 @@ export const formSchema: FormSchema[] = [
|
||||
defaultValue: false,
|
||||
renderComponentContent: '胶料',
|
||||
colProps: { span: 24 },
|
||||
show: ({ values }) => isMaterialRawAuxSubCategory(values.pid),
|
||||
show: ({ values }) => isMaterialMinorCategory(values.pid),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
import {
|
||||
MATERIAL_RAW_AUX_CODE,
|
||||
materialRawAuxCategoryId,
|
||||
isMaterialRawAuxSubCategory,
|
||||
ensureMaterialCategoryContext,
|
||||
isMaterialMinorCategory,
|
||||
toIsRubberFlag,
|
||||
fromIsRubberFlag,
|
||||
} from '../category.constants';
|
||||
@@ -40,7 +41,7 @@
|
||||
|
||||
function normalizeSubmitValues(values: Recordable) {
|
||||
const payload = { ...values };
|
||||
payload.isRubber = isMaterialRawAuxSubCategory(payload.pid) ? toIsRubberFlag(payload.isRubber) : '0';
|
||||
payload.isRubber = isMaterialMinorCategory(payload.pid) ? toIsRubberFlag(payload.isRubber) : '0';
|
||||
return payload;
|
||||
}
|
||||
//表单配置
|
||||
@@ -64,6 +65,7 @@
|
||||
setModalProps({ confirmLoading: false, minHeight: 80 });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
await ensureMaterialRawAuxCategoryId();
|
||||
await ensureMaterialCategoryContext();
|
||||
// 代码逻辑说明: 分类字典data.record为空报错------------
|
||||
isSubAdd.value = !data?.isUpdate && data.record && data.record.id;
|
||||
if (data?.record) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
columns: [
|
||||
{ title: '设备名称', dataIndex: 'equipmentName', width: 160 },
|
||||
{ title: '设备编号', dataIndex: 'equipmentCode', width: 140 },
|
||||
{ title: '有效体积', dataIndex: 'effectiveVolume', width: 100 },
|
||||
{ title: '设备类别', dataIndex: 'equipmentCategoryName', width: 120 },
|
||||
{ title: '设备类型', dataIndex: 'equipmentTypeName', width: 120 },
|
||||
],
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
}
|
||||
}
|
||||
if (!row?.id) {
|
||||
emit('select', { equipmentLedgerId: '', equipmentName: '', equipmentCode: '' });
|
||||
emit('select', { equipmentLedgerId: '', equipmentName: '', equipmentCode: '', effectiveVolume: '' });
|
||||
closeModal();
|
||||
return;
|
||||
}
|
||||
@@ -87,6 +87,7 @@
|
||||
equipmentLedgerId: row.id,
|
||||
equipmentName: row.equipmentName || '',
|
||||
equipmentCode: row.equipmentCode || '',
|
||||
effectiveVolume: row.effectiveVolume || '',
|
||||
});
|
||||
closeModal();
|
||||
}
|
||||
|
||||
@@ -47,5 +47,8 @@ export const saveOrUpdate = (params, isUpdate) => defHttp.post({ url: isUpdate ?
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A38】配合示方生成混炼示方-----------
|
||||
export const buildMixingGeneratePreview = (params) =>
|
||||
defHttp.get({ url: Api.buildMixingGeneratePreview, params }, { successMessageMode: 'none' });
|
||||
export const generateMixingSpec = (params) => defHttp.post({ url: Api.generateMixingSpec, params });
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A45】批量生成混炼示方延长超时避免误报失败-----------
|
||||
export const generateMixingSpec = (params) =>
|
||||
defHttp.post({ url: Api.generateMixingSpec, params, timeout: 120 * 1000 });
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A45】批量生成混炼示方延长超时避免误报失败-----------
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A38】配合示方生成混炼示方-----------
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
enum Api {
|
||||
list = '/xslmes/mesXslMixerMaterialKindCfg/list',
|
||||
save = '/xslmes/mesXslMixerMaterialKindCfg/add',
|
||||
edit = '/xslmes/mesXslMixerMaterialKindCfg/edit',
|
||||
addBatch = '/xslmes/mesXslMixerMaterialKindCfg/addBatch',
|
||||
expandLines = '/xslmes/mesXslMixerMaterialKindCfg/expandLines',
|
||||
deleteOne = '/xslmes/mesXslMixerMaterialKindCfg/delete',
|
||||
deleteBatch = '/xslmes/mesXslMixerMaterialKindCfg/deleteBatch',
|
||||
importExcel = '/xslmes/mesXslMixerMaterialKindCfg/importExcel',
|
||||
exportXls = '/xslmes/mesXslMixerMaterialKindCfg/exportXls',
|
||||
queryById = '/xslmes/mesXslMixerMaterialKindCfg/queryById',
|
||||
}
|
||||
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
export const queryById = (params) => defHttp.get({ url: Api.queryById, params });
|
||||
|
||||
export const expandLines = (params) => defHttp.get({ url: Api.expandLines, params });
|
||||
|
||||
export const addBatch = (params) => defHttp.post({ url: Api.addBatch, params }, { successMessageMode: 'none' });
|
||||
|
||||
export const deleteOne = (params, handleSuccess) =>
|
||||
defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => handleSuccess());
|
||||
|
||||
export const batchDelete = (params, handleSuccess) =>
|
||||
defHttp.delete({ url: Api.deleteBatch, params }, { joinParamsToUrl: true }).then(() => handleSuccess());
|
||||
|
||||
export const saveOrUpdate = (params, isUpdate) => {
|
||||
const url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url, params }, { successMessageMode: 'none' });
|
||||
};
|
||||
|
||||
export const getExportUrl = Api.exportXls;
|
||||
export const getImportUrl = Api.importExcel;
|
||||
@@ -0,0 +1,156 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { JVxeColumn, JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
|
||||
import { list as dictList } from '/@/views/system/dict/dict.api';
|
||||
import { loadTreeData } from '/@/api/common/api';
|
||||
|
||||
export const SOURCE_TYPE_OPTIONS = [
|
||||
{ label: '数据字典', value: 'dict' },
|
||||
{ label: '分类字典', value: 'category' },
|
||||
];
|
||||
|
||||
export const sourceTypeTextMap: Record<string, string> = {
|
||||
dict: '数据字典',
|
||||
category: '分类字典',
|
||||
};
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{ title: '种类键值', align: 'center', dataIndex: 'kindKey', width: 120 },
|
||||
{ title: '种类名称', align: 'center', dataIndex: 'kindName', width: 140 },
|
||||
{
|
||||
title: '数据源',
|
||||
align: 'center',
|
||||
dataIndex: 'sourceType',
|
||||
width: 100,
|
||||
customRender: ({ text }) => sourceTypeTextMap[String(text || '')] || text || '',
|
||||
},
|
||||
{ title: '根名称', align: 'center', dataIndex: 'sourceRootName', width: 140 },
|
||||
{ title: '对应分类', align: 'center', dataIndex: 'categoryRefName', width: 140 },
|
||||
{ title: '优先级', align: 'center', dataIndex: 'priority', width: 80 },
|
||||
{ title: '租户ID', align: 'center', dataIndex: 'tenantId', width: 80, defaultHidden: true },
|
||||
{ title: '创建时间', align: 'center', dataIndex: 'createTime', width: 165 },
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{ label: '种类键值', field: 'kindKey', component: 'Input', colProps: { span: 6 } },
|
||||
{ label: '种类名称', field: 'kindName', component: 'Input', colProps: { span: 6 } },
|
||||
{
|
||||
label: '数据源',
|
||||
field: 'sourceType',
|
||||
component: 'Select',
|
||||
componentProps: { options: SOURCE_TYPE_OPTIONS, allowClear: true },
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{ label: '根编码', field: 'sourceRootCode', component: 'Input', colProps: { span: 6 } },
|
||||
];
|
||||
|
||||
export const batchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '数据源',
|
||||
field: 'sourceType',
|
||||
component: 'Select',
|
||||
required: true,
|
||||
defaultValue: 'category',
|
||||
componentProps: { options: SOURCE_TYPE_OPTIONS },
|
||||
},
|
||||
{
|
||||
label: '数据字典',
|
||||
field: 'dictRootCode',
|
||||
component: 'ApiSelect',
|
||||
required: true,
|
||||
ifShow: ({ values }) => values.sourceType === 'dict',
|
||||
componentProps: {
|
||||
api: () => dictList({ pageNo: 1, pageSize: 500 }),
|
||||
resultField: 'records',
|
||||
labelField: 'dictName',
|
||||
valueField: 'dictCode',
|
||||
showSearch: true,
|
||||
placeholder: '请选择数据字典根',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '分类字典',
|
||||
field: 'categoryRootCode',
|
||||
component: 'ApiSelect',
|
||||
required: true,
|
||||
ifShow: ({ values }) => values.sourceType === 'category',
|
||||
componentProps: {
|
||||
api: loadTreeData,
|
||||
params: { async: false, pcode: '0' },
|
||||
resultField: '',
|
||||
labelField: 'title',
|
||||
valueField: 'code',
|
||||
showSearch: true,
|
||||
placeholder: '请选择分类字典根',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const editFormSchema: FormSchema[] = [
|
||||
{ label: '', field: 'id', component: 'Input', show: false },
|
||||
{ label: '', field: 'sourceType', component: 'Input', show: false },
|
||||
{ label: '', field: 'sourceRootCode', component: 'Input', show: false },
|
||||
{ label: '', field: 'sourceRootName', component: 'Input', show: false },
|
||||
{ label: '', field: 'categoryRefId', component: 'Input', show: false },
|
||||
{ label: '', field: 'categoryRefCode', component: 'Input', show: false },
|
||||
{
|
||||
label: '种类键值',
|
||||
field: 'kindKey',
|
||||
component: 'Input',
|
||||
componentProps: { disabled: true },
|
||||
},
|
||||
{
|
||||
label: '种类名称',
|
||||
field: 'kindName',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: '对应分类',
|
||||
field: 'categoryRefName',
|
||||
component: 'Input',
|
||||
componentProps: { disabled: true },
|
||||
},
|
||||
{
|
||||
label: '优先级',
|
||||
field: 'priority',
|
||||
component: 'InputNumber',
|
||||
required: true,
|
||||
componentProps: { min: 0, precision: 0, style: { width: '100%' } },
|
||||
},
|
||||
{
|
||||
label: '租户ID',
|
||||
field: 'tenantId',
|
||||
component: 'InputNumber',
|
||||
componentProps: { disabled: true, style: { width: '100%' } },
|
||||
},
|
||||
];
|
||||
|
||||
export const batchJVxeColumns: JVxeColumn[] = [
|
||||
{ title: '', key: 'categoryRefId', type: JVxeTypes.hidden },
|
||||
{ title: '', key: 'categoryRefCode', type: JVxeTypes.hidden },
|
||||
{ title: '', key: 'sourceType', type: JVxeTypes.hidden },
|
||||
{ title: '', key: 'sourceRootCode', type: JVxeTypes.hidden },
|
||||
{ title: '', key: 'sourceRootName', type: JVxeTypes.hidden },
|
||||
{ title: '', key: 'tenantId', type: JVxeTypes.hidden },
|
||||
{ title: '种类键值', key: 'kindKey', type: JVxeTypes.normal, width: 200, minWidth: 160, disabled: true },
|
||||
{ title: '种类名称', key: 'kindName', type: JVxeTypes.input, width: 180, minWidth: 140 },
|
||||
{ title: '对应分类', key: 'categoryRefName', type: JVxeTypes.normal, width: 180, minWidth: 140, disabled: true },
|
||||
{
|
||||
title: '优先级',
|
||||
key: 'priority',
|
||||
type: JVxeTypes.inputNumber,
|
||||
width: 110,
|
||||
minWidth: 90,
|
||||
align: 'center',
|
||||
validateRules: [{ required: true, message: '请输入优先级' }],
|
||||
},
|
||||
];
|
||||
|
||||
export const superQuerySchema = {
|
||||
kindKey: { title: '种类键值', order: 0, view: 'text' },
|
||||
kindName: { title: '种类名称', order: 1, view: 'text' },
|
||||
sourceType: { title: '数据源', order: 2, view: 'list', enum: SOURCE_TYPE_OPTIONS },
|
||||
sourceRootCode: { title: '根编码', order: 3, view: 'text' },
|
||||
categoryRefName: { title: '对应分类', order: 4, view: 'text' },
|
||||
priority: { title: '优先级', order: 5, view: 'number' },
|
||||
};
|
||||
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
type="primary"
|
||||
v-auth="'xslmes:mes_xsl_mixer_material_kind_cfg:add'"
|
||||
@click="handleBatchAdd"
|
||||
preIcon="ant-design:plus-outlined"
|
||||
>
|
||||
新增
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
v-auth="'xslmes:mes_xsl_mixer_material_kind_cfg:exportXls'"
|
||||
preIcon="ant-design:export-outlined"
|
||||
@click="onExportXls"
|
||||
>
|
||||
导出
|
||||
</a-button>
|
||||
<j-upload-button
|
||||
type="primary"
|
||||
v-auth="'xslmes:mes_xsl_mixer_material_kind_cfg:importExcel'"
|
||||
preIcon="ant-design:import-outlined"
|
||||
@click="onImportXls"
|
||||
>
|
||||
导入
|
||||
</j-upload-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button v-auth="'xslmes:mes_xsl_mixer_material_kind_cfg:deleteBatch'">
|
||||
批量操作
|
||||
<Icon icon="mdi:chevron-down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
auth: 'xslmes:mes_xsl_mixer_material_kind_cfg:edit',
|
||||
},
|
||||
]"
|
||||
:dropDownActions="getDropDownAction(record)"
|
||||
/>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<MesXslMixerMaterialKindCfgBatchModal @register="registerBatchModal" @success="handleSuccess" />
|
||||
<MesXslMixerMaterialKindCfgEditModal @register="registerEditModal" @success="handleSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="xslmes-mesXslMixerMaterialKindCfg" setup>
|
||||
import { reactive } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import Icon from '/@/components/Icon';
|
||||
import MesXslMixerMaterialKindCfgBatchModal from './components/MesXslMixerMaterialKindCfgBatchModal.vue';
|
||||
import MesXslMixerMaterialKindCfgEditModal from './components/MesXslMixerMaterialKindCfgEditModal.vue';
|
||||
import { columns, searchFormSchema, superQuerySchema } from './MesXslMixerMaterialKindCfg.data';
|
||||
import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './MesXslMixerMaterialKindCfg.api';
|
||||
|
||||
const queryParam = reactive<any>({});
|
||||
const [registerBatchModal, { openModal: openBatchModal }] = useModal();
|
||||
const [registerEditModal, { openModal: openEditModal }] = useModal();
|
||||
|
||||
const { tableContext, onExportXls, onImportXls } = useListPage({
|
||||
tableProps: {
|
||||
title: '密炼物料种类配置',
|
||||
api: list,
|
||||
columns,
|
||||
canResize: true,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
labelWidth: 100,
|
||||
autoSubmitOnEnter: true,
|
||||
showAdvancedButton: true,
|
||||
},
|
||||
actionColumn: {
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 160,
|
||||
fixed: 'right',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
beforeFetch: (params) => Object.assign(params, queryParam),
|
||||
},
|
||||
exportConfig: {
|
||||
name: '密炼物料种类配置',
|
||||
url: getExportUrl,
|
||||
params: queryParam,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
success: handleSuccess,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
const superQueryConfig = reactive(superQuerySchema);
|
||||
|
||||
function handleSuperQuery(params) {
|
||||
Object.keys(params).forEach((k) => {
|
||||
queryParam[k] = params[k];
|
||||
});
|
||||
reload();
|
||||
}
|
||||
|
||||
function handleBatchAdd() {
|
||||
openBatchModal(true, { showFooter: true });
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
openEditModal(true, { record, isUpdate: true, showFooter: true });
|
||||
}
|
||||
|
||||
function handleDetail(record: Recordable) {
|
||||
openEditModal(true, { record, isUpdate: true, showFooter: false });
|
||||
}
|
||||
|
||||
async function handleDelete(record) {
|
||||
await deleteOne({ id: record.id }, handleSuccess);
|
||||
}
|
||||
|
||||
async function batchHandleDelete() {
|
||||
await batchDelete({ ids: selectedRowKeys.value.join(',') }, handleSuccess);
|
||||
}
|
||||
|
||||
function handleSuccess() {
|
||||
selectedRowKeys.value = [];
|
||||
reload();
|
||||
}
|
||||
|
||||
function getDropDownAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '详情',
|
||||
onClick: handleDetail.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
placement: 'topLeft',
|
||||
},
|
||||
auth: 'xslmes:mes_xsl_mixer_material_kind_cfg:delete',
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
destroyOnClose
|
||||
title="新增密炼物料种类配置"
|
||||
:width="'92%'"
|
||||
:defaultFullscreen="true"
|
||||
wrapClassName="mes-xsl-mixer-material-kind-batch-modal"
|
||||
@register="registerModal"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<div class="batch-modal-body">
|
||||
<BasicForm @register="registerForm">
|
||||
<template #expandAction>
|
||||
<a-button type="primary" :loading="expanding" @click="handleExpand">带出明细</a-button>
|
||||
</template>
|
||||
</BasicForm>
|
||||
<a-divider orientation="left">种类配置明细</a-divider>
|
||||
<div class="batch-table-wrap">
|
||||
<JVxeTable
|
||||
v-if="tableReady"
|
||||
ref="lineTableRef"
|
||||
toolbar
|
||||
row-number
|
||||
rowSelection
|
||||
keep-source
|
||||
:insert-row="false"
|
||||
:max-height="tableMaxHeight"
|
||||
:loading="lineLoading"
|
||||
:columns="batchJVxeColumns"
|
||||
:dataSource="lineDataSource"
|
||||
:toolbar-config="{ btn: ['remove'] }"
|
||||
:add-btn-cfg="{ enabled: false }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import type { JVxeTableInstance } from '/@/components/jeecg/JVxeTable/types';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { batchFormSchema, batchJVxeColumns } from '../MesXslMixerMaterialKindCfg.data';
|
||||
import { addBatch, expandLines } from '../MesXslMixerMaterialKindCfg.api';
|
||||
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const { createMessage } = useMessage();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const tableReady = ref(false);
|
||||
const lineLoading = ref(false);
|
||||
const expanding = ref(false);
|
||||
const lineDataSource = ref<Recordable[]>([]);
|
||||
const lineTableRef = ref<JVxeTableInstance>();
|
||||
const tableMaxHeight = ref(560);
|
||||
|
||||
function refreshTableHeight() {
|
||||
// 预留顶部表单、标题与底部按钮区域,表格尽量占满可视区
|
||||
tableMaxHeight.value = Math.max(420, window.innerHeight - 300);
|
||||
}
|
||||
|
||||
const [registerForm, { resetFields, validate, getFieldsValue }] = useForm({
|
||||
labelWidth: 110,
|
||||
schemas: [
|
||||
...batchFormSchema,
|
||||
{
|
||||
label: '',
|
||||
field: 'expandAction',
|
||||
component: 'Input',
|
||||
slot: 'expandAction',
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
],
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: { span: 12 },
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async () => {
|
||||
tableReady.value = false;
|
||||
lineDataSource.value = [];
|
||||
refreshTableHeight();
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false, showCancelBtn: true, showOkBtn: true });
|
||||
tableReady.value = true;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
refreshTableHeight();
|
||||
window.addEventListener('resize', refreshTableHeight);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', refreshTableHeight);
|
||||
});
|
||||
|
||||
function resolveTenantId() {
|
||||
const tenant = userStore.getTenant;
|
||||
if (tenant == null || tenant === '') {
|
||||
return undefined;
|
||||
}
|
||||
const num = Number(tenant);
|
||||
return Number.isNaN(num) ? undefined : num;
|
||||
}
|
||||
|
||||
function resolveSourceRootCode(values: Recordable) {
|
||||
if (values.sourceType === 'dict') {
|
||||
return values.dictRootCode;
|
||||
}
|
||||
return values.categoryRootCode;
|
||||
}
|
||||
|
||||
async function handleExpand() {
|
||||
try {
|
||||
const values = await validate();
|
||||
const sourceRootCode = resolveSourceRootCode(values);
|
||||
if (!sourceRootCode) {
|
||||
createMessage.warning(values.sourceType === 'dict' ? '请选择数据字典根' : '请选择分类字典根');
|
||||
return;
|
||||
}
|
||||
expanding.value = true;
|
||||
const tenantId = resolveTenantId();
|
||||
const raw = await expandLines({
|
||||
sourceType: values.sourceType,
|
||||
sourceRootCode,
|
||||
tenantId,
|
||||
});
|
||||
const rows = Array.isArray(raw) ? raw : (raw as Recordable)?.result ?? [];
|
||||
if (!rows.length) {
|
||||
createMessage.warning('未带出任何明细,可能均已配置');
|
||||
return;
|
||||
}
|
||||
const mergedTenant = tenantId ?? rows[0]?.tenantId;
|
||||
lineDataSource.value = rows.map((row) => ({
|
||||
...row,
|
||||
tenantId: mergedTenant,
|
||||
}));
|
||||
createMessage.success(`已带出 ${rows.length} 条明细`);
|
||||
} catch (e: any) {
|
||||
createMessage.error(e?.message || '带出明细失败');
|
||||
} finally {
|
||||
expanding.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
const lineRef = lineTableRef.value as any;
|
||||
const tableData = (lineRef?.getTableData?.() || lineDataSource.value || []) as Recordable[];
|
||||
if (!tableData.length) {
|
||||
createMessage.warning('请先选择根字典/分类并带出明细');
|
||||
return;
|
||||
}
|
||||
const errMap = await lineRef?.validateTable?.();
|
||||
if (errMap) {
|
||||
createMessage.warning('请完善明细中的种类名称与优先级');
|
||||
return;
|
||||
}
|
||||
const tenantId = resolveTenantId();
|
||||
const payload = tableData.map((row) => ({
|
||||
...row,
|
||||
tenantId: row.tenantId ?? tenantId,
|
||||
}));
|
||||
try {
|
||||
setModalProps({ confirmLoading: true });
|
||||
await addBatch(payload);
|
||||
createMessage.success('新增成功');
|
||||
closeModal();
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.batch-modal-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: calc(100vh - 200px);
|
||||
}
|
||||
|
||||
.batch-table-wrap {
|
||||
flex: 1;
|
||||
min-height: 420px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" destroyOnClose :title="title" width="640px" @register="registerModal" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { editFormSchema } from '../MesXslMixerMaterialKindCfg.data';
|
||||
import { saveOrUpdate } from '../MesXslMixerMaterialKindCfg.api';
|
||||
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const { createMessage } = useMessage();
|
||||
const isUpdate = ref(true);
|
||||
const isDetail = ref(false);
|
||||
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, setProps }] = useForm({
|
||||
labelWidth: 110,
|
||||
schemas: editFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: { span: 24 },
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
isDetail.value = !data?.showFooter;
|
||||
if (data?.record) {
|
||||
await setFieldsValue({ ...data.record });
|
||||
}
|
||||
setProps({ disabled: !data?.showFooter });
|
||||
});
|
||||
|
||||
const title = computed(() => (!unref(isUpdate) ? '新增' : unref(isDetail) ? '详情' : '编辑'));
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
await saveOrUpdate(values, true);
|
||||
createMessage.success('编辑成功');
|
||||
closeModal();
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -18,7 +18,10 @@ export const getExportUrl = Api.exportXls;
|
||||
export const getImportUrl = Api.importExcel;
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
export const queryById = (params) => defHttp.get({ url: Api.queryById, params });
|
||||
export const saveOrUpdate = (params, isUpdate) => defHttp.post({ url: isUpdate ? Api.edit : Api.save, params });
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A45】混炼示方主子表保存延长超时避免误报失败-----------
|
||||
export const saveOrUpdate = (params, isUpdate) =>
|
||||
defHttp.post({ url: isUpdate ? Api.edit : Api.save, params, timeout: 60 * 1000 });
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A45】混炼示方主子表保存延长超时避免误报失败-----------
|
||||
export const queryIssueNumberOptions = (params) => defHttp.get({ url: Api.queryIssueNumberOptions, params });
|
||||
export const queryPurposeOptions = (params) => defHttp.get({ url: Api.queryPurposeOptions, params });
|
||||
|
||||
|
||||
@@ -91,10 +91,27 @@ export const materialColumns: JVxeColumn[] = [
|
||||
{ title: '物料大类', key: 'materialMajor', type: JVxeTypes.input, width: 100, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
|
||||
{ title: '物料小类', key: 'materialMinor', type: JVxeTypes.input, width: 120, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
|
||||
{ title: '种类', key: 'materialKind', type: JVxeTypes.input, width: 80, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
|
||||
{ title: '密炼物料名称', key: 'mixerMaterialName', type: JVxeTypes.input, width: 160, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
|
||||
{
|
||||
title: '密炼物料名称',
|
||||
key: 'mixerMaterialName',
|
||||
type: JVxeTypes.slot,
|
||||
slotName: 'mixerMaterialNameSlot',
|
||||
width: 160,
|
||||
minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH,
|
||||
},
|
||||
{ title: '密炼物料描述', key: 'mixerMaterialDesc', type: JVxeTypes.input, width: 220, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH },
|
||||
{ title: '单重', key: 'unitWeight', type: JVxeTypes.inputNumber, width: 72, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH, align: 'center' },
|
||||
{ title: '累计', key: 'accumWeight', type: JVxeTypes.inputNumber, width: 72, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH, align: 'center' },
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】累计列按种类分组合计只读展示-----------
|
||||
{
|
||||
title: '累计',
|
||||
key: 'accumWeight',
|
||||
type: JVxeTypes.inputNumber,
|
||||
width: 72,
|
||||
minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH,
|
||||
align: 'center',
|
||||
disabled: true,
|
||||
},
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】累计列按种类分组合计只读展示-----------
|
||||
{ title: '顺序', key: 'seqNo', type: JVxeTypes.inputNumber, width: 64, minWidth: MIXING_MATERIAL_MIN_COLUMN_WIDTH, align: 'center' },
|
||||
];
|
||||
|
||||
@@ -196,6 +213,280 @@ export function calcMixingMaterialTableWidth(columns: JVxeColumn[], widthMap: Re
|
||||
}
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】橡胶及配合剂明细列展示设置-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
|
||||
/** 是否为有效明细行(参与种类分组) */
|
||||
function isMixingMaterialDataRow(row: Recordable): boolean {
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
return !!(row.mixerMaterialName || row.materialKind || row.unitWeight != null && row.unitWeight !== '');
|
||||
}
|
||||
|
||||
/** 规范化种类字段,用于连续行分组 */
|
||||
function normalizeMixingMaterialKind(row: Recordable): string {
|
||||
const kind = row?.materialKind;
|
||||
return kind != null && String(kind).trim() !== '' ? String(kind).trim() : '';
|
||||
}
|
||||
|
||||
/** 按种类连续分组,累计写入每组最后一行 */
|
||||
export function fillMixingMaterialAccumWeight(rows: Recordable[] = []): Recordable[] {
|
||||
if (!rows?.length) {
|
||||
return rows;
|
||||
}
|
||||
let index = 0;
|
||||
while (index < rows.length) {
|
||||
const current = rows[index];
|
||||
if (!isMixingMaterialDataRow(current)) {
|
||||
current.accumWeight = null;
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
const kind = normalizeMixingMaterialKind(current);
|
||||
let groupEnd = index;
|
||||
let sum = 0;
|
||||
while (groupEnd < rows.length) {
|
||||
const row = rows[groupEnd];
|
||||
if (!isMixingMaterialDataRow(row) || normalizeMixingMaterialKind(row) !== kind) {
|
||||
break;
|
||||
}
|
||||
const weight = toMixingMaterialNumber(row.unitWeight);
|
||||
if (weight != null) {
|
||||
sum += weight;
|
||||
}
|
||||
groupEnd++;
|
||||
}
|
||||
for (let rowIndex = index; rowIndex < groupEnd; rowIndex++) {
|
||||
rows[rowIndex].accumWeight =
|
||||
rowIndex === groupEnd - 1 && sum !== 0 ? roundMixingMaterialNumber(sum) : null;
|
||||
}
|
||||
index = groupEnd;
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** 安全解析明细数值,避免字符串拼接 */
|
||||
function toMixingMaterialNumber(value: unknown): number | null {
|
||||
if (value == null || value === '') {
|
||||
return null;
|
||||
}
|
||||
const num = Number(value);
|
||||
return Number.isFinite(num) ? num : null;
|
||||
}
|
||||
|
||||
/** 混炼示方重量小数位(与后端 BigDecimal 精度一致) */
|
||||
const MIXING_MATERIAL_WEIGHT_SCALE = 6;
|
||||
|
||||
/** 重量四舍五入,消除浮点累加误差 */
|
||||
function roundMixingMaterialNumber(value: number): number {
|
||||
return Number(value.toFixed(MIXING_MATERIAL_WEIGHT_SCALE));
|
||||
}
|
||||
|
||||
/** 格式化重量展示文本 */
|
||||
function formatMixingMaterialWeight(value: unknown): string {
|
||||
const num = toMixingMaterialNumber(value);
|
||||
if (num == null) {
|
||||
return '';
|
||||
}
|
||||
return String(roundMixingMaterialNumber(num));
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A43】换算系数联动明细单重实时计算-----------
|
||||
/** 规范化换算系数,空值或非正数按 1 处理 */
|
||||
export function normalizeMixingConvertFactor(factor: unknown): number {
|
||||
const num = toMixingMaterialNumber(factor);
|
||||
if (num == null || num <= 0) {
|
||||
return 1;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/** 基准单重 × 换算系数 */
|
||||
export function calcMixingMaterialConvertedWeight(base: unknown, factor: unknown): number | null {
|
||||
const baseNum = toMixingMaterialNumber(base);
|
||||
if (baseNum == null) {
|
||||
return null;
|
||||
}
|
||||
return Number((baseNum * normalizeMixingConvertFactor(factor)).toFixed(MIXING_MATERIAL_WEIGHT_SCALE));
|
||||
}
|
||||
|
||||
/** 从当前显示单重反推基准单重 */
|
||||
export function syncMaterialBaseUnitWeightFromDisplay(row: Recordable, factor: unknown) {
|
||||
if (!row) {
|
||||
return;
|
||||
}
|
||||
const unit = toMixingMaterialNumber(row.unitWeight);
|
||||
if (unit == null) {
|
||||
row.baseUnitWeight = null;
|
||||
return;
|
||||
}
|
||||
row.baseUnitWeight = Number((unit / normalizeMixingConvertFactor(factor)).toFixed(MIXING_MATERIAL_WEIGHT_SCALE));
|
||||
}
|
||||
|
||||
/** 初始化明细行基准单重(编辑加载时由已保存单重反推) */
|
||||
export function initMaterialBaseUnitWeight(row: Recordable, factor: unknown, force = false) {
|
||||
if (!isMixingMaterialDataRow(row)) {
|
||||
row.baseUnitWeight = null;
|
||||
return;
|
||||
}
|
||||
if (!force && toMixingMaterialNumber(row.baseUnitWeight) != null) {
|
||||
return;
|
||||
}
|
||||
syncMaterialBaseUnitWeightFromDisplay(row, factor);
|
||||
}
|
||||
|
||||
/** 批量初始化基准单重 */
|
||||
export function initMaterialBaseUnitWeights(rows: Recordable[] = [], factor: unknown, force = false) {
|
||||
for (const row of rows) {
|
||||
initMaterialBaseUnitWeight(row, factor, force);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** 按换算系数重算所有明细单重 */
|
||||
export function applyConvertFactorToMaterialRows(
|
||||
rows: Recordable[] = [],
|
||||
factor: unknown,
|
||||
prevFactor?: unknown,
|
||||
): Recordable[] {
|
||||
const nextFactor = normalizeMixingConvertFactor(factor);
|
||||
const oldFactor = prevFactor != null ? normalizeMixingConvertFactor(prevFactor) : nextFactor;
|
||||
for (const row of rows) {
|
||||
if (!isMixingMaterialDataRow(row)) {
|
||||
continue;
|
||||
}
|
||||
let base = toMixingMaterialNumber(row.baseUnitWeight);
|
||||
if (base == null) {
|
||||
const unit = toMixingMaterialNumber(row.unitWeight);
|
||||
if (unit == null) {
|
||||
continue;
|
||||
}
|
||||
base = roundMixingMaterialNumber(unit / oldFactor);
|
||||
row.baseUnitWeight = base;
|
||||
}
|
||||
row.unitWeight = calcMixingMaterialConvertedWeight(base, nextFactor);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A43】换算系数联动明细单重实时计算-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A46】填充体积按单重/比重/机台有效体积自动计算-----------
|
||||
/** 解析设备有效体积(支持纯数字或带单位字符串) */
|
||||
export function parseMixingEffectiveVolume(raw: unknown): number | null {
|
||||
if (raw == null || raw === '') {
|
||||
return null;
|
||||
}
|
||||
const text = String(raw).trim();
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
const direct = toMixingMaterialNumber(text);
|
||||
if (direct != null && direct > 0) {
|
||||
return direct;
|
||||
}
|
||||
const matched = text.match(/([0-9]+(?:\.[0-9]+)?)/);
|
||||
if (!matched) {
|
||||
return null;
|
||||
}
|
||||
const parsed = Number(matched[1]);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
}
|
||||
|
||||
/** 按段数与比重字段选择本段计算用比重 */
|
||||
export function resolveMixingSpecificGravity(form: Recordable = {}): number | null {
|
||||
const motherSg = toMixingMaterialNumber(form.motherRubberSg);
|
||||
const finalSg = toMixingMaterialNumber(form.finalRubberSg);
|
||||
const stageCount = String(form.stageCount || '').trim();
|
||||
const stageMatch = stageCount.match(/^(\d+)\/(\d+)$/);
|
||||
const isFinalStage = stageMatch ? stageMatch[1] === stageMatch[2] : false;
|
||||
if (isFinalStage && finalSg != null && finalSg > 0) {
|
||||
return finalSg;
|
||||
}
|
||||
if (motherSg != null && motherSg > 0) {
|
||||
return motherSg;
|
||||
}
|
||||
if (finalSg != null && finalSg > 0) {
|
||||
return finalSg;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充体积(%) = 单重合计 ÷ 比重 ÷ 机台有效体积(L) × 100
|
||||
* 单重合计已含换算系数,此处不再重复乘换算系数
|
||||
*/
|
||||
export function calcMixingFillVolume(totalWeight: unknown, specificGravity: unknown, effectiveVolume: unknown): number | null {
|
||||
const weight = toMixingMaterialNumber(totalWeight);
|
||||
const sg = toMixingMaterialNumber(specificGravity);
|
||||
const volume = parseMixingEffectiveVolume(effectiveVolume);
|
||||
if (weight == null || weight <= 0 || sg == null || sg <= 0 || volume == null || volume <= 0) {
|
||||
return null;
|
||||
}
|
||||
const materialVolume = weight / sg;
|
||||
return Number(((materialVolume / volume) * 100).toFixed(6));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A46】填充体积按单重/比重/机台有效体积自动计算-----------
|
||||
|
||||
/** 汇总有效明细行的单重合计 */
|
||||
export function calcMixingMaterialUnitWeightTotal(rows: Recordable[] = []): number | null {
|
||||
let sum = 0;
|
||||
let hasAny = false;
|
||||
for (const row of rows) {
|
||||
if (!isMixingMaterialDataRow(row)) {
|
||||
continue;
|
||||
}
|
||||
const weight = toMixingMaterialNumber(row.unitWeight);
|
||||
if (weight != null) {
|
||||
sum += weight;
|
||||
hasAny = true;
|
||||
}
|
||||
}
|
||||
return hasAny ? roundMixingMaterialNumber(sum) : null;
|
||||
}
|
||||
|
||||
/** 汇总有效明细行的累计合计(与单重合计一致) */
|
||||
export function calcMixingMaterialAccumWeightTotal(rows: Recordable[] = []): number | null {
|
||||
return calcMixingMaterialUnitWeightTotal(rows);
|
||||
}
|
||||
|
||||
export interface MixingMaterialFooterCell {
|
||||
key: string;
|
||||
width: number;
|
||||
text: string;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
isLabel?: boolean;
|
||||
isTotal?: boolean;
|
||||
}
|
||||
|
||||
/** 构建橡胶及配合剂明细底部合计行单元格(列宽与明细表同步) */
|
||||
export function buildMixingMaterialFooterCells(
|
||||
columns: JVxeColumn[],
|
||||
widthMap: Record<string, number>,
|
||||
totals: { unitWeight?: number | null; accumWeight?: number | null },
|
||||
): MixingMaterialFooterCell[] {
|
||||
const unitWeightIndex = columns.findIndex((col) => String(col.key) === 'unitWeight');
|
||||
const formatTotal = (value: number | null | undefined) => formatMixingMaterialWeight(value);
|
||||
|
||||
return columns.map((col, index) => {
|
||||
const key = String(col.key);
|
||||
const width = widthMap[key] ?? Number(col.width) ?? 80;
|
||||
if (key === 'unitWeight') {
|
||||
return { key, width, text: formatTotal(totals.unitWeight), align: 'center', isTotal: true };
|
||||
}
|
||||
if (key === 'accumWeight') {
|
||||
return { key, width, text: formatTotal(totals.accumWeight), align: 'center', isTotal: true };
|
||||
}
|
||||
const isLabelCol = unitWeightIndex > 0 && index === unitWeightIndex - 1;
|
||||
return {
|
||||
key,
|
||||
width,
|
||||
text: isLabelCol ? '合计' : '',
|
||||
align: 'center',
|
||||
isLabel: isLabelCol,
|
||||
};
|
||||
});
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A20】明细表默认列宽对齐参考图-----------
|
||||
/** 混合步骤/下密炼机明细列可缩小到的最小宽度 */
|
||||
export const MIXING_STEP_MIN_COLUMN_WIDTH = 48;
|
||||
@@ -412,6 +703,9 @@ export const MIXING_VXE_MINI_HEADER_HEIGHT = 36;
|
||||
/** vxe mini 行高 */
|
||||
export const MIXING_VXE_MINI_ROW_HEIGHT = 32;
|
||||
|
||||
/** 橡胶及配合剂明细合计行高度(含边框) */
|
||||
export const MIXING_MATERIAL_FOOTER_ROW_HEIGHT = MIXING_VXE_MINI_ROW_HEIGHT + 1;
|
||||
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A29】胶料/混合步骤表格高度按行数完整展示-----------
|
||||
/** 计算橡胶及配合剂明细表格展示高度 */
|
||||
export function calcMixingMaterialTableHeight(rowCount = MIXING_MATERIAL_VISIBLE_ROW_COUNT) {
|
||||
@@ -637,3 +931,99 @@ export function ensureMixingDetailRows(rows: Recordable[] = [], defaultCount: nu
|
||||
}
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A39】编辑页明细补齐默认空行与新增一致-----------
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A22】明细表默认空行数-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】混炼示方密炼物料选料弹窗与种类解析-----------
|
||||
/** 混炼示方选料弹窗:隐藏的小类 ID 偏好 localStorage 键 */
|
||||
export const MIXING_MATERIAL_PICKER_HIDDEN_CATEGORY_CACHE_KEY = 'mes_xsl_mixing_spec_material_picker_hidden_categories';
|
||||
|
||||
export interface MixingMaterialPickerCategoryItem {
|
||||
id: string;
|
||||
name: string;
|
||||
majorId: string;
|
||||
majorName: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const mixingMaterialPickerStorage = createLocalStorage();
|
||||
|
||||
export function loadMixingMaterialPickerHiddenCategoryIds(): string[] {
|
||||
const raw = mixingMaterialPickerStorage.get(MIXING_MATERIAL_PICKER_HIDDEN_CATEGORY_CACHE_KEY);
|
||||
return Array.isArray(raw) ? raw.map(String) : [];
|
||||
}
|
||||
|
||||
export function saveMixingMaterialPickerHiddenCategoryIds(ids: string[]) {
|
||||
mixingMaterialPickerStorage.set(MIXING_MATERIAL_PICKER_HIDDEN_CATEGORY_CACHE_KEY, ids || []);
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】选料弹窗小类树为空时重置隐藏配置-----------
|
||||
/** 过滤无效隐藏项;若全部小类被隐藏则自动重置,避免左侧树只剩「全部小类」 */
|
||||
export function sanitizeMixingMaterialPickerHiddenCategoryIds(allMinorIds: string[], hidden: string[]) {
|
||||
const allSet = new Set((allMinorIds || []).map(String));
|
||||
const filtered = (hidden || []).map(String).filter((id) => allSet.has(id));
|
||||
if (allSet.size > 0 && filtered.length >= allSet.size) {
|
||||
return [];
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】选料弹窗小类树为空时重置隐藏配置-----------
|
||||
|
||||
/** 解析混炼示方明细种类:小类勾选胶料则显示「胶料」,否则显示小类名 */
|
||||
export function resolveMixingMaterialKindFromCategory(isRubber?: unknown, minorName?: string) {
|
||||
if (isRubber === '1' || isRubber === 1 || isRubber === true) {
|
||||
return '胶料';
|
||||
}
|
||||
return minorName != null && String(minorName).trim() !== '' ? String(minorName).trim() : '';
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】选料弹窗自动/人工称量列与种类映射-----------
|
||||
/** 与配合示方「自动/人工」列相同字典 */
|
||||
export const MIXING_MATERIAL_PICKER_WEIGH_MODE_DICT = 'xslmes_formula_spec_weigh_mode';
|
||||
|
||||
/** 选料弹窗表格列(隐藏 ERP 编号,新增仅本次有效的自动/人工称量) */
|
||||
export const mixingMaterialPickerTableColumns: BasicColumn[] = [
|
||||
{ title: '物料编码', align: 'center', width: 120, dataIndex: 'materialCode' },
|
||||
{ title: '物料名称', align: 'center', width: 160, dataIndex: 'materialName' },
|
||||
{ title: '自动/人工称量', align: 'center', width: 132, dataIndex: 'pickerWeighMode' },
|
||||
{ title: '物料大类', align: 'center', width: 120, dataIndex: 'majorCategoryId_dictText' },
|
||||
{ title: '物料小类', align: 'center', width: 120, dataIndex: 'minorCategoryId_dictText' },
|
||||
{ title: '物料描述', align: 'center', width: 180, ellipsis: true, dataIndex: 'materialDesc' },
|
||||
];
|
||||
|
||||
/** 配合示方称量方式 -> 混炼示方种类(与后端 resolveWeighModeMaterialKind 一致) */
|
||||
export function resolveMixingMaterialKindFromWeighMode(weighMode?: string) {
|
||||
if (weighMode == null || String(weighMode).trim() === '') {
|
||||
return '';
|
||||
}
|
||||
const normalized = String(weighMode).trim();
|
||||
const lower = normalized.toLowerCase();
|
||||
if (lower.startsWith('auto') || normalized.includes('自动')) {
|
||||
return '自动';
|
||||
}
|
||||
if (lower === 'manual' || normalized.includes('人工')) {
|
||||
return '人工';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/** 选料确认时种类:称量方式优先,否则按小类胶料/小类名 */
|
||||
export function resolveMixingMaterialKindForPicker(weighMode: string | undefined, isRubber?: unknown, minorName?: string) {
|
||||
const fromWeighMode = resolveMixingMaterialKindFromWeighMode(weighMode);
|
||||
if (fromWeighMode) {
|
||||
return fromWeighMode;
|
||||
}
|
||||
return resolveMixingMaterialKindFromCategory(isRubber, minorName);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】选料弹窗自动/人工称量列与种类映射-----------
|
||||
|
||||
/** 选择密炼物料后回填混炼示方橡胶及配合剂明细行 */
|
||||
export function applyMixingMaterialFromSelection(row: Recordable, material: Recordable, materialKind: string) {
|
||||
if (!row || !material) {
|
||||
return;
|
||||
}
|
||||
row.mixerMaterialName = material.materialName || material.materialCode || '';
|
||||
row.mixerMaterialDesc = material.materialDesc || material.materialName || material.materialCode || '';
|
||||
row.materialMajor = material.majorCategoryId_dictText || '';
|
||||
row.materialMinor = material.minorCategoryId_dictText || '';
|
||||
row.materialKind = materialKind || row.materialMinor || '';
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】混炼示方密炼物料选料弹窗与种类解析-----------
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<Popover
|
||||
v-model:open="popoverOpen"
|
||||
trigger="click"
|
||||
placement="bottomRight"
|
||||
:overlayClassName="`${prefixCls}__popover`"
|
||||
@open-change="handleOpenChange"
|
||||
>
|
||||
<template #title>
|
||||
<div :class="`${prefixCls}__title`">
|
||||
<Checkbox :indeterminate="indeterminate" :checked="checkAll" :disabled="loading || !allCategoryIds.length" @change="onCheckAllChange">
|
||||
小类展示
|
||||
</Checkbox>
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
<Spin :spinning="loading">
|
||||
<div v-if="!loading && !groupedCategories.length" :class="`${prefixCls}__empty`">
|
||||
暂无物料小类,请确认分类字典 XSLMES_MATERIAL 已配置
|
||||
</div>
|
||||
<div v-else :class="`${prefixCls}__list`">
|
||||
<div v-for="group in groupedCategories" :key="group.majorId" :class="`${prefixCls}__group`">
|
||||
<div :class="`${prefixCls}__group-title`">{{ group.majorName }}</div>
|
||||
<div :class="`${prefixCls}__group-options`">
|
||||
<Checkbox
|
||||
v-for="opt in group.options"
|
||||
:key="opt.value"
|
||||
:checked="isMinorVisible(opt.value)"
|
||||
@change="(e) => onMinorVisibleChange(opt.value, e.target.checked)"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
<div :class="`${prefixCls}__footer`">
|
||||
<a-button size="small" :disabled="loading || !allCategoryIds.length" @click="handleReset">重置</a-button>
|
||||
<a-button size="small" type="primary" :disabled="loading || !allCategoryIds.length" @click="handleSave">保存</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<a-tooltip title="小类展示设置">
|
||||
<a-button size="small" class="mixing-material-category-setting-btn" :disabled="loading" @click.stop>
|
||||
<Icon icon="ant-design:setting-outlined" />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</Popover>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch, type PropType } from 'vue';
|
||||
import { Popover, Checkbox, Spin } from 'ant-design-vue';
|
||||
import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { saveMixingMaterialPickerHiddenCategoryIds, type MixingMaterialPickerCategoryItem } from '../MesXslMixingSpec.data';
|
||||
|
||||
const prefixCls = 'mixing-material-category-setting';
|
||||
const { createMessage } = useMessage();
|
||||
|
||||
const props = defineProps({
|
||||
categories: {
|
||||
type: Array as PropType<MixingMaterialPickerCategoryItem[]>,
|
||||
default: () => [],
|
||||
},
|
||||
hiddenCategoryIds: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:hiddenCategoryIds', 'change']);
|
||||
|
||||
const popoverOpen = ref(false);
|
||||
const draftVisibleIds = ref<string[]>([]);
|
||||
|
||||
const allCategoryIds = computed(() => (props.categories || []).map((item) => item.id));
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】选料弹窗小类设置按大类分组展示-----------
|
||||
const groupedCategories = computed(() => {
|
||||
const groupMap = new Map<string, { majorId: string; majorName: string; options: { label: string; value: string }[] }>();
|
||||
(props.categories || []).forEach((item) => {
|
||||
const majorId = item.majorId || 'unknown';
|
||||
const majorName = item.majorName || '其他';
|
||||
const group = groupMap.get(majorId) || { majorId, majorName, options: [] };
|
||||
group.options.push({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
});
|
||||
groupMap.set(majorId, group);
|
||||
});
|
||||
return Array.from(groupMap.values());
|
||||
});
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】选料弹窗小类设置按大类分组展示-----------
|
||||
|
||||
const checkAll = computed(() => {
|
||||
const all = allCategoryIds.value;
|
||||
if (!all.length) {
|
||||
return false;
|
||||
}
|
||||
return draftVisibleIds.value.length === all.length;
|
||||
});
|
||||
|
||||
const indeterminate = computed(() => {
|
||||
const total = allCategoryIds.value.length;
|
||||
const checked = draftVisibleIds.value.length;
|
||||
return checked > 0 && checked < total;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [props.hiddenCategoryIds, props.categories],
|
||||
() => {
|
||||
syncDraftFromHidden();
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
function syncDraftFromHidden() {
|
||||
const hidden = new Set((props.hiddenCategoryIds || []).map(String));
|
||||
draftVisibleIds.value = allCategoryIds.value.filter((id) => !hidden.has(String(id)));
|
||||
}
|
||||
|
||||
function handleOpenChange(open: boolean) {
|
||||
popoverOpen.value = open;
|
||||
if (open) {
|
||||
syncDraftFromHidden();
|
||||
}
|
||||
}
|
||||
|
||||
function onCheckAllChange(e: CheckboxChangeEvent) {
|
||||
draftVisibleIds.value = e.target.checked ? [...allCategoryIds.value] : [];
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】小类展示分组勾选互不覆盖-----------
|
||||
function isMinorVisible(id: string) {
|
||||
return draftVisibleIds.value.map(String).includes(String(id));
|
||||
}
|
||||
|
||||
function onMinorVisibleChange(id: string, checked: boolean) {
|
||||
const visibleSet = new Set(draftVisibleIds.value.map(String));
|
||||
const key = String(id);
|
||||
if (checked) {
|
||||
visibleSet.add(key);
|
||||
} else {
|
||||
visibleSet.delete(key);
|
||||
}
|
||||
draftVisibleIds.value = allCategoryIds.value.filter((itemId) => visibleSet.has(String(itemId)));
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】小类展示分组勾选互不覆盖-----------
|
||||
|
||||
function handleReset() {
|
||||
draftVisibleIds.value = [...allCategoryIds.value];
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
const visibleSet = new Set(draftVisibleIds.value.map(String));
|
||||
const hidden = allCategoryIds.value.filter((id) => !visibleSet.has(String(id)));
|
||||
saveMixingMaterialPickerHiddenCategoryIds(hidden);
|
||||
emit('update:hiddenCategoryIds', hidden);
|
||||
emit('change', hidden);
|
||||
popoverOpen.value = false;
|
||||
createMessage.success('小类展示设置已保存');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.mixing-material-category-setting-btn {
|
||||
padding-inline: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.mixing-material-category-setting__popover {
|
||||
.ant-popover-inner-content {
|
||||
width: 320px;
|
||||
max-width: 80vw;
|
||||
}
|
||||
}
|
||||
|
||||
.mixing-material-category-setting__empty {
|
||||
padding: 12px 0;
|
||||
color: var(--text-color-secondary, rgba(0, 0, 0, 0.45));
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mixing-material-category-setting__list {
|
||||
max-height: 320px;
|
||||
overflow: auto;
|
||||
padding: 4px 0 8px;
|
||||
}
|
||||
|
||||
.mixing-material-category-setting__group + .mixing-material-category-setting__group {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.mixing-material-category-setting__group-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 6px;
|
||||
color: var(--text-color, rgba(0, 0, 0, 0.88));
|
||||
}
|
||||
|
||||
.mixing-material-category-setting__group {
|
||||
.mixing-material-category-setting__group-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.mixing-material-category-setting__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid var(--border-color-base, #f0f0f0);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,406 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
title="选择密炼物料"
|
||||
:width="1180"
|
||||
:getContainer="getModalContainer"
|
||||
@register="registerModal"
|
||||
@ok="handleOk"
|
||||
>
|
||||
<div class="mixing-material-picker">
|
||||
<div class="mixing-material-picker-toolbar">
|
||||
<a-input
|
||||
v-model:value="keyword"
|
||||
allow-clear
|
||||
placeholder="关键字(物料编码/名称/描述)"
|
||||
style="width: 280px"
|
||||
@pressEnter="reloadTable"
|
||||
/>
|
||||
<a-button type="primary" @click="reloadTable">搜索</a-button>
|
||||
<MesXslMixingMaterialCategorySetting
|
||||
v-model:hiddenCategoryIds="hiddenCategoryIds"
|
||||
:categories="allMinorCategories"
|
||||
:loading="treeLoading"
|
||||
@change="handleCategoryVisibilityChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="mixing-material-picker-body">
|
||||
<aside class="mixing-material-picker-sider">
|
||||
<div class="mixing-material-picker-sider-title">物料小类</div>
|
||||
<Spin :spinning="treeLoading">
|
||||
<BasicTree
|
||||
:treeData="visibleCategoryTree"
|
||||
:selectedKeys="selectedCategoryKeys"
|
||||
:expandedKeys="expandedCategoryKeys"
|
||||
defaultExpandLevel="2"
|
||||
@update:selectedKeys="onCategorySelect"
|
||||
@update:expandedKeys="onExpandedKeysChange"
|
||||
/>
|
||||
</Spin>
|
||||
</aside>
|
||||
<div class="mixing-material-picker-main">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'pickerWeighMode'">
|
||||
<div class="mixing-material-picker-weigh-mode" @click.stop>
|
||||
<JDictSelectTag
|
||||
:value="getPickerWeighMode(record.id)"
|
||||
:dictCode="MIXING_MATERIAL_PICKER_WEIGH_MODE_DICT"
|
||||
:getPopupContainer="getSelectPopupContainer"
|
||||
:showChooseOption="false"
|
||||
placeholder="请选择"
|
||||
popupClassName="mixing-material-picker-weigh-mode-dropdown"
|
||||
style="width: 100%"
|
||||
@change="(val) => setPickerWeighMode(record.id, val)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { Spin } from 'ant-design-vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable } from '/@/components/Table';
|
||||
import { BasicTree } from '/@/components/Tree';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { loadMesMaterialCategoryTreeData } from '/@/views/system/category/category.constants';
|
||||
import { list as mixerList, queryById as queryMixerById } from '/@/views/mes/material/MesMixerMaterial.api';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||
import MesXslMixingMaterialCategorySetting from './MesXslMixingMaterialCategorySetting.vue';
|
||||
import {
|
||||
applyMixingMaterialFromSelection,
|
||||
loadMixingMaterialPickerHiddenCategoryIds,
|
||||
MIXING_MATERIAL_PICKER_WEIGH_MODE_DICT,
|
||||
mixingMaterialPickerTableColumns,
|
||||
resolveMixingMaterialKindForPicker,
|
||||
sanitizeMixingMaterialPickerHiddenCategoryIds,
|
||||
saveMixingMaterialPickerHiddenCategoryIds,
|
||||
type MixingMaterialPickerCategoryItem,
|
||||
} from '../MesXslMixingSpec.data';
|
||||
import type { KeyType } from '/@/components/Tree/src/types/tree';
|
||||
|
||||
const TREE_ALL = 'ALL';
|
||||
const emit = defineEmits(['register', 'select']);
|
||||
const { createMessage } = useMessage();
|
||||
|
||||
const keyword = ref('');
|
||||
const treeLoading = ref(false);
|
||||
const rawCategoryTree = ref<Recordable[]>([]);
|
||||
const allMajorCategories = ref<Array<{ id: string; name: string; minors: MixingMaterialPickerCategoryItem[] }>>([]);
|
||||
const allMinorCategories = ref<MixingMaterialPickerCategoryItem[]>([]);
|
||||
const hiddenCategoryIds = ref<string[]>(loadMixingMaterialPickerHiddenCategoryIds());
|
||||
const selectedCategoryKeys = ref<KeyType[]>([TREE_ALL]);
|
||||
const expandedCategoryKeys = ref<KeyType[]>([TREE_ALL]);
|
||||
const selectedRow = ref<Recordable | null>(null);
|
||||
const categoryRubberMap = ref<Record<string, boolean>>({});
|
||||
const pickerWeighModeMap = ref<Record<string, string>>({});
|
||||
|
||||
const hiddenCategoryIdSet = computed(() => new Set(hiddenCategoryIds.value.map(String)));
|
||||
|
||||
function getModalContainer() {
|
||||
return document.body;
|
||||
}
|
||||
|
||||
function getSelectPopupContainer() {
|
||||
return document.body;
|
||||
}
|
||||
|
||||
function filterHiddenCategoryTree(nodes: Recordable[], hidden: Set<string>): Recordable[] {
|
||||
return (nodes || [])
|
||||
.map((major) => {
|
||||
const children = (major.children || [])
|
||||
.filter((minor) => !hidden.has(String(minor.key)))
|
||||
.map((minor) => ({
|
||||
key: minor.key,
|
||||
title: minor.title,
|
||||
}));
|
||||
if (!children.length) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
key: major.key,
|
||||
title: major.title,
|
||||
children,
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as Recordable[];
|
||||
}
|
||||
|
||||
const visibleCategoryTree = computed(() => [
|
||||
{
|
||||
key: TREE_ALL,
|
||||
title: '全部小类',
|
||||
children: filterHiddenCategoryTree(rawCategoryTree.value, hiddenCategoryIdSet.value),
|
||||
},
|
||||
]);
|
||||
|
||||
function syncExpandedCategoryKeys() {
|
||||
const keys: KeyType[] = [TREE_ALL];
|
||||
for (const major of visibleCategoryTree.value[0]?.children || []) {
|
||||
keys.push(major.key);
|
||||
}
|
||||
expandedCategoryKeys.value = keys;
|
||||
}
|
||||
|
||||
watch(
|
||||
visibleCategoryTree,
|
||||
() => {
|
||||
syncExpandedCategoryKeys();
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const selectedCategoryFilter = computed(() => {
|
||||
const key = selectedCategoryKeys.value[0];
|
||||
if (!key || key === TREE_ALL) {
|
||||
return {};
|
||||
}
|
||||
const keyStr = String(key);
|
||||
const major = allMajorCategories.value.find((item) => item.id === keyStr);
|
||||
if (major) {
|
||||
return { majorCategoryId: major.id };
|
||||
}
|
||||
return { minorCategoryId: keyStr };
|
||||
});
|
||||
|
||||
const [registerTable, { reload, getSelectRowKeys, getSelectRows, clearSelectedRowKeys }] = useTable({
|
||||
api: mixerList,
|
||||
columns: mixingMaterialPickerTableColumns,
|
||||
rowKey: 'id',
|
||||
useSearchForm: false,
|
||||
pagination: { pageSize: 10 },
|
||||
canResize: false,
|
||||
showIndexColumn: true,
|
||||
immediate: true,
|
||||
beforeFetch: (params) => {
|
||||
const next = { ...params, ...selectedCategoryFilter.value };
|
||||
const kw = keyword.value?.trim();
|
||||
if (kw) {
|
||||
next.materialName = `*${kw}*`;
|
||||
}
|
||||
return next;
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'radio',
|
||||
columnWidth: 48,
|
||||
onChange: (_keys, rows) => {
|
||||
selectedRow.value = rows?.[0] ?? null;
|
||||
},
|
||||
},
|
||||
clickToRowSelect: true,
|
||||
});
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】选料弹窗打开时初始化(对齐其他SelectModal)-----------
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async () => {
|
||||
await initPickerModal();
|
||||
});
|
||||
|
||||
async function initPickerModal() {
|
||||
selectedRow.value = null;
|
||||
keyword.value = '';
|
||||
pickerWeighModeMap.value = {};
|
||||
clearSelectedRowKeys?.();
|
||||
hiddenCategoryIds.value = loadMixingMaterialPickerHiddenCategoryIds();
|
||||
selectedCategoryKeys.value = [TREE_ALL];
|
||||
setModalProps({ confirmLoading: false });
|
||||
await loadMaterialCategoryTree();
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadMaterialCategoryTree();
|
||||
});
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A50】选料弹窗打开时初始化(对齐其他SelectModal)-----------
|
||||
|
||||
async function loadMaterialCategoryTree() {
|
||||
treeLoading.value = true;
|
||||
try {
|
||||
const { majors, minors, treeNodes } = await loadMesMaterialCategoryTreeData();
|
||||
rawCategoryTree.value = treeNodes;
|
||||
allMajorCategories.value = majors;
|
||||
allMinorCategories.value = minors;
|
||||
|
||||
const sanitizedHidden = sanitizeMixingMaterialPickerHiddenCategoryIds(
|
||||
minors.map((item) => item.id),
|
||||
hiddenCategoryIds.value,
|
||||
);
|
||||
if (sanitizedHidden.length !== hiddenCategoryIds.value.length) {
|
||||
hiddenCategoryIds.value = sanitizedHidden;
|
||||
saveMixingMaterialPickerHiddenCategoryIds(sanitizedHidden);
|
||||
}
|
||||
|
||||
categoryRubberMap.value = {};
|
||||
syncExpandedCategoryKeys();
|
||||
|
||||
if (!minors.length) {
|
||||
createMessage.warning('未加载到物料小类,请确认分类字典根编码 XSLMES_MATERIAL 及其下级分类已配置。');
|
||||
}
|
||||
} catch {
|
||||
rawCategoryTree.value = [];
|
||||
allMajorCategories.value = [];
|
||||
allMinorCategories.value = [];
|
||||
createMessage.warning('加载物料分类树失败,请检查分类根编码 XSLMES_MATERIAL 是否存在。');
|
||||
} finally {
|
||||
treeLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function reloadTable() {
|
||||
reload();
|
||||
}
|
||||
|
||||
function getPickerWeighMode(materialId?: string) {
|
||||
if (!materialId) {
|
||||
return undefined;
|
||||
}
|
||||
return pickerWeighModeMap.value[String(materialId)];
|
||||
}
|
||||
|
||||
function setPickerWeighMode(materialId: string | undefined, value?: string) {
|
||||
if (!materialId) {
|
||||
return;
|
||||
}
|
||||
const key = String(materialId);
|
||||
const next = { ...pickerWeighModeMap.value };
|
||||
if (value == null || value === '') {
|
||||
delete next[key];
|
||||
} else {
|
||||
next[key] = String(value);
|
||||
}
|
||||
pickerWeighModeMap.value = next;
|
||||
}
|
||||
|
||||
function onCategorySelect(keys: KeyType[]) {
|
||||
selectedCategoryKeys.value = keys?.length ? keys : [TREE_ALL];
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function onExpandedKeysChange(keys: KeyType[]) {
|
||||
expandedCategoryKeys.value = keys?.length ? keys : [TREE_ALL];
|
||||
}
|
||||
|
||||
function handleCategoryVisibilityChange() {
|
||||
const key = selectedCategoryKeys.value[0];
|
||||
if (!key || key === TREE_ALL) {
|
||||
syncExpandedCategoryKeys();
|
||||
reloadTable();
|
||||
return;
|
||||
}
|
||||
const keyStr = String(key);
|
||||
const hidden = hiddenCategoryIdSet.value;
|
||||
const major = allMajorCategories.value.find((item) => item.id === keyStr);
|
||||
if (major) {
|
||||
const hasVisibleMinor = major.minors.some((minor) => !hidden.has(String(minor.id)));
|
||||
if (!hasVisibleMinor) {
|
||||
selectedCategoryKeys.value = [TREE_ALL];
|
||||
}
|
||||
} else if (hidden.has(keyStr)) {
|
||||
selectedCategoryKeys.value = [TREE_ALL];
|
||||
}
|
||||
syncExpandedCategoryKeys();
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
async function resolveKindForMaterial(material: Recordable, weighMode?: string) {
|
||||
const minorId = material?.minorCategoryId ? String(material.minorCategoryId) : '';
|
||||
const minorName = material?.minorCategoryId_dictText || '';
|
||||
if (!minorId) {
|
||||
return resolveMixingMaterialKindForPicker(weighMode, false, minorName);
|
||||
}
|
||||
if (categoryRubberMap.value[minorId] === undefined) {
|
||||
try {
|
||||
const cat = await defHttp.get<Recordable>({ url: '/sys/category/queryById', params: { id: minorId } });
|
||||
categoryRubberMap.value[minorId] = cat?.isRubber === '1' || cat?.isRubber === 1;
|
||||
} catch {
|
||||
categoryRubberMap.value[minorId] = false;
|
||||
}
|
||||
}
|
||||
return resolveMixingMaterialKindForPicker(
|
||||
weighMode,
|
||||
categoryRubberMap.value[minorId] ? '1' : '0',
|
||||
minorName,
|
||||
);
|
||||
}
|
||||
|
||||
async function handleOk() {
|
||||
const keys = (getSelectRowKeys?.() || []) as string[];
|
||||
let row = selectedRow.value || ((getSelectRows?.() || []) as Recordable[])[0];
|
||||
if (!row && keys.length) {
|
||||
try {
|
||||
const raw = await queryMixerById({ id: keys[0] });
|
||||
row = (raw as any)?.id != null ? raw : (raw as any)?.result;
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (!row?.id) {
|
||||
createMessage.warning('请选择一条密炼物料');
|
||||
return;
|
||||
}
|
||||
const weighMode = getPickerWeighMode(row.id);
|
||||
const payload: Recordable = { ...row, pickerWeighMode: weighMode };
|
||||
const materialKind = await resolveKindForMaterial(row, weighMode);
|
||||
applyMixingMaterialFromSelection(payload, row, materialKind);
|
||||
emit('select', payload);
|
||||
closeModal();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.mixing-material-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-height: 520px;
|
||||
}
|
||||
|
||||
.mixing-material-picker-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mixing-material-picker-body {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
min-height: 480px;
|
||||
}
|
||||
|
||||
.mixing-material-picker-sider {
|
||||
width: 240px;
|
||||
flex-shrink: 0;
|
||||
border: 1px solid var(--border-color-base, #f0f0f0);
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.mixing-material-picker-sider-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mixing-material-picker-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mixing-material-picker-weigh-mode {
|
||||
min-width: 108px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
/* 下拉挂到 body,避免表格 overflow 裁剪;层级高于 Modal */
|
||||
.mixing-material-picker-weigh-mode-dropdown {
|
||||
z-index: 2100 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -55,7 +55,15 @@
|
||||
<tr>
|
||||
<th class="formTitle" colspan="1">换算系数</th>
|
||||
<td class="formValue" colspan="2">
|
||||
<a-input-number v-model:value="sheetForm.convertFactor" :disabled="!showFooter" :precision="6" :bordered="false" class="form-input" style="width: 100%" />
|
||||
<a-input-number
|
||||
v-model:value="sheetForm.convertFactor"
|
||||
:disabled="!showFooter"
|
||||
:precision="6"
|
||||
:bordered="false"
|
||||
class="form-input"
|
||||
style="width: 100%"
|
||||
@update:value="handleConvertFactorChange"
|
||||
/>
|
||||
</td>
|
||||
<th class="formTitle" colspan="1">填充体积</th>
|
||||
<td class="formValue" colspan="1">
|
||||
@@ -80,7 +88,7 @@
|
||||
</td>
|
||||
<th class="formTitle" colspan="1">母胶比重</th>
|
||||
<td class="formValue">
|
||||
<a-input-number v-model:value="sheetForm.motherRubberSg" :disabled="!showFooter" :precision="6" :bordered="false" class="form-input" style="width: 100%" />
|
||||
<a-input-number v-model:value="sheetForm.motherRubberSg" :disabled="!showFooter" :precision="6" :bordered="false" class="form-input" style="width: 100%" @update:value="recalcFillVolume" />
|
||||
</td>
|
||||
<th class="formTitle">段数</th>
|
||||
<td class="formValue" colspan="2">
|
||||
@@ -104,7 +112,7 @@
|
||||
<tr>
|
||||
<th class="formTitle" colspan="1">终炼胶比重</th>
|
||||
<td class="formValue">
|
||||
<a-input-number v-model:value="sheetForm.finalRubberSg" :disabled="!showFooter" :precision="6" :bordered="false" class="form-input" style="width: 100%" />
|
||||
<a-input-number v-model:value="sheetForm.finalRubberSg" :disabled="!showFooter" :precision="6" :bordered="false" class="form-input" style="width: 100%" @update:value="recalcFillVolume" />
|
||||
</td>
|
||||
<th class="formTitle" colspan="1">适用工厂</th>
|
||||
<td class="formValue" colspan="2">
|
||||
@@ -181,25 +189,60 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="material-table-wrap" :style="{ height: `${materialMainTableHeight}px` }">
|
||||
<JVxeTable
|
||||
:key="materialTableLayoutKey"
|
||||
ref="materialRef"
|
||||
row-number
|
||||
keep-source
|
||||
bordered
|
||||
:fit="false"
|
||||
:column-config="{ resizable: true }"
|
||||
:row-config="materialRowConfig"
|
||||
size="mini"
|
||||
:height="materialMainTableHeight"
|
||||
:scroll-x="{ enabled: false }"
|
||||
:scroll-y="{ enabled: true }"
|
||||
:columns="visibleMaterialColumns"
|
||||
:dataSource="materialData"
|
||||
:disabled="!showFooter"
|
||||
@resizable-change="handleMaterialColumnResize"
|
||||
@column-resizable-change="handleMaterialColumnResize"
|
||||
/>
|
||||
<!--update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A42】橡胶及配合剂明细底部固定合计行----------- -->
|
||||
<div class="material-table-stack" :style="{ width: `${materialTableWidth}px` }">
|
||||
<div class="material-table-body" :style="{ height: `${materialBodyTableHeight}px` }">
|
||||
<JVxeTable
|
||||
:key="materialTableLayoutKey"
|
||||
ref="materialRef"
|
||||
row-number
|
||||
keep-source
|
||||
bordered
|
||||
:fit="false"
|
||||
:column-config="{ resizable: true }"
|
||||
:row-config="materialRowConfig"
|
||||
size="mini"
|
||||
:height="materialBodyTableHeight"
|
||||
:scroll-x="{ enabled: false }"
|
||||
:scroll-y="{ enabled: true }"
|
||||
:columns="visibleMaterialColumns"
|
||||
:dataSource="materialData"
|
||||
:disabled="!showFooter"
|
||||
@value-change="handleMaterialValueChange"
|
||||
@resizable-change="handleMaterialColumnResize"
|
||||
@column-resizable-change="handleMaterialColumnResize"
|
||||
>
|
||||
<template #mixerMaterialNameSlot="{ row }">
|
||||
<div
|
||||
class="mixing-material-name-cell"
|
||||
:class="{ 'is-disabled': !showFooter }"
|
||||
:style="{ minHeight: `${materialHeightPref.rowHeight}px` }"
|
||||
@click.stop="openMixingMaterialPicker(row)"
|
||||
>
|
||||
<span v-if="row.mixerMaterialName" class="mixing-material-name-text">{{ row.mixerMaterialName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</JVxeTable>
|
||||
</div>
|
||||
<div class="material-table-footer">
|
||||
<div class="material-table-footer-row">
|
||||
<div
|
||||
class="material-footer-seq"
|
||||
:style="{ width: `${MIXING_MATERIAL_ROW_NUMBER_WIDTH}px`, height: `${materialHeightPref.rowHeight}px` }"
|
||||
></div>
|
||||
<div
|
||||
v-for="cell in materialFooterCells"
|
||||
:key="cell.key"
|
||||
class="material-footer-cell"
|
||||
:class="{ 'is-label': cell.isLabel, 'is-total': cell.isTotal }"
|
||||
:style="{ width: `${cell.width}px`, height: `${materialHeightPref.rowHeight}px` }"
|
||||
>
|
||||
{{ cell.text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A42】橡胶及配合剂明细底部固定合计行----------- -->
|
||||
</div>
|
||||
<!--update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A18】TCU温度条件表移至橡胶及配合剂下方----------- -->
|
||||
<div class="left-panel-section left-panel-section-tcu">
|
||||
@@ -368,11 +411,11 @@
|
||||
</div>
|
||||
|
||||
<BasicForm v-show="false" @register="registerForm" />
|
||||
|
||||
<MesXslEquipmentLedgerSelectModal @register="registerMachineModal" @select="onMachineSelect" />
|
||||
<MesXslMixerPsCompileSelectModal @register="registerIssueNumberModal" @select="onIssueNumberSelect" />
|
||||
</div>
|
||||
</BasicModal>
|
||||
<MesXslEquipmentLedgerSelectModal @register="registerMachineModal" @select="onMachineSelect" />
|
||||
<MesXslMixerPsCompileSelectModal @register="registerIssueNumberModal" @select="onIssueNumberSelect" />
|
||||
<MesXslMixingMaterialSelectModal @register="registerMixingMaterialModal" @select="onMixingMaterialSelect" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -411,6 +454,19 @@ import {
|
||||
DEFAULT_MIXING_STEP_ROW_COUNT,
|
||||
DEFAULT_MIXING_DOWN_STEP_ROW_COUNT,
|
||||
buildDefaultMixingTcuRows,
|
||||
applyMixingMaterialFromSelection,
|
||||
fillMixingMaterialAccumWeight,
|
||||
calcMixingMaterialUnitWeightTotal,
|
||||
calcMixingMaterialAccumWeightTotal,
|
||||
buildMixingMaterialFooterCells,
|
||||
normalizeMixingConvertFactor,
|
||||
initMaterialBaseUnitWeights,
|
||||
applyConvertFactorToMaterialRows,
|
||||
syncMaterialBaseUnitWeightFromDisplay,
|
||||
calcMixingFillVolume,
|
||||
resolveMixingSpecificGravity,
|
||||
MIXING_MATERIAL_ROW_NUMBER_WIDTH,
|
||||
MIXING_MATERIAL_FOOTER_ROW_HEIGHT,
|
||||
MIXING_MATERIAL_MIN_COLUMN_WIDTH,
|
||||
MIXING_TCU_MIN_COLUMN_WIDTH,
|
||||
MIXING_STEP_MIN_COLUMN_WIDTH,
|
||||
@@ -422,7 +478,9 @@ import MesXslMixingStepSelectCell from './MesXslMixingStepSelectCell.vue';
|
||||
import { list as mixerActionList } from '/@/views/xslmes/mesXslMixerAction/MesXslMixerAction.api';
|
||||
import { list as mixerConditionList } from '/@/views/xslmes/mesXslMixerCondition/MesXslMixerCondition.api';
|
||||
import MesXslEquipmentLedgerSelectModal from '/@/views/xslmes/mesXslEquipInspectConfig/components/MesXslEquipmentLedgerSelectModal.vue';
|
||||
import { queryById as queryEquipmentById } from '/@/views/xslmes/mesXslEquipmentLedger/MesXslEquipmentLedger.api';
|
||||
import MesXslMixerPsCompileSelectModal from '/@/views/xslmes/mesXslMixerPsCompile/components/MesXslMixerPsCompileSelectModal.vue';
|
||||
import MesXslMixingMaterialSelectModal from './MesXslMixingMaterialSelectModal.vue';
|
||||
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const { createMessage } = useMessage();
|
||||
@@ -437,6 +495,7 @@ const mixerConditionOptions = ref<{ title: string; value: string }[]>([]);
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A34】混合步骤动作/组合下拉选项-----------
|
||||
|
||||
const materialRef = ref();
|
||||
const materialPickerRow = ref<Recordable | null>(null);
|
||||
const stepRef = ref();
|
||||
const downStepRef = ref();
|
||||
const tcuRef = ref();
|
||||
@@ -465,6 +524,22 @@ const stepHeightPref = ref(loadMixingTableHeightPreference('step'));
|
||||
const downStepHeightPref = ref(loadMixingTableHeightPreference('downStep'));
|
||||
|
||||
const materialMainTableHeight = computed(() => calcMixingDetailTableViewportHeight('material', materialHeightPref.value));
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A42】橡胶及配合剂明细底部固定合计行-----------
|
||||
const materialBodyTableHeight = computed(() =>
|
||||
Math.max(materialMainTableHeight.value - MIXING_MATERIAL_FOOTER_ROW_HEIGHT, 80),
|
||||
);
|
||||
const materialUnitWeightTotal = ref<number | null>(null);
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A46】换算系数/单重/机台有效体积联动填充体积-----------
|
||||
const machineEffectiveVolume = ref('');
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A46】换算系数/单重/机台有效体积联动填充体积-----------
|
||||
const materialAccumWeightTotal = ref<number | null>(null);
|
||||
const materialFooterCells = computed(() =>
|
||||
buildMixingMaterialFooterCells(visibleMaterialColumns.value, materialColumnWidths.value, {
|
||||
unitWeight: materialUnitWeightTotal.value,
|
||||
accumWeight: materialAccumWeightTotal.value,
|
||||
}),
|
||||
);
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A42】橡胶及配合剂明细底部固定合计行-----------
|
||||
const stepMainTableHeight = computed(() => calcMixingDetailTableViewportHeight('step', stepHeightPref.value));
|
||||
const tcuTableHeight = computed(() => calcMixingDetailTableViewportHeight('tcu', tcuHeightPref.value));
|
||||
const downStepTableHeight = computed(() => calcMixingDetailTableViewportHeight('downStep', downStepHeightPref.value));
|
||||
@@ -509,6 +584,105 @@ function handleMaterialColumnResize(params: Recordable) {
|
||||
};
|
||||
saveMixingMaterialColumnWidths(materialColumnWidths.value);
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
|
||||
/** JVxe getTableData 为浅拷贝,批量改值需用 fullData 原行并强制 refresh */
|
||||
function resolveMaterialTableRawRows(): Recordable[] {
|
||||
const fullData = materialRef.value?.getXTable?.()?.getTableData?.()?.fullData as Recordable[] | undefined;
|
||||
if (Array.isArray(fullData) && fullData.length) {
|
||||
return fullData;
|
||||
}
|
||||
return (materialRef.value?.getTableData?.() || materialData.value || []) as Recordable[];
|
||||
}
|
||||
|
||||
function refreshMaterialTableView() {
|
||||
materialRef.value?.getXTable?.()?.updateData?.();
|
||||
}
|
||||
|
||||
function applyMaterialAccumWeight(rows?: Recordable[]) {
|
||||
const targetRows = rows || resolveMaterialTableRawRows();
|
||||
fillMixingMaterialAccumWeight(targetRows);
|
||||
materialUnitWeightTotal.value = calcMixingMaterialUnitWeightTotal(targetRows);
|
||||
materialAccumWeightTotal.value = calcMixingMaterialAccumWeightTotal(targetRows);
|
||||
refreshMaterialTableView();
|
||||
recalcFillVolume();
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A46】换算系数/单重/机台有效体积联动填充体积-----------
|
||||
async function loadMachineEffectiveVolume(machineId?: string) {
|
||||
const id = machineId || sheetForm.machineId;
|
||||
if (!id) {
|
||||
machineEffectiveVolume.value = '';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const raw = await queryEquipmentById({ id });
|
||||
const row = (raw as Recordable)?.id != null ? raw : (raw as Recordable)?.result;
|
||||
machineEffectiveVolume.value = row?.effectiveVolume || '';
|
||||
} catch {
|
||||
machineEffectiveVolume.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function recalcFillVolume() {
|
||||
if (!showFooter.value) {
|
||||
return;
|
||||
}
|
||||
const totalWeight =
|
||||
materialUnitWeightTotal.value ?? calcMixingMaterialUnitWeightTotal(resolveMaterialTableRawRows());
|
||||
const specificGravity = resolveMixingSpecificGravity(sheetForm);
|
||||
const next = calcMixingFillVolume(totalWeight, specificGravity, machineEffectiveVolume.value);
|
||||
if (next != null) {
|
||||
sheetForm.fillVolume = next;
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A46】换算系数/单重/机台有效体积联动填充体积-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A43】换算系数联动明细单重实时计算-----------
|
||||
const lastConvertFactor = ref<number>(1);
|
||||
const convertFactorApplying = ref(false);
|
||||
|
||||
function applyConvertFactorToMaterials(factor: unknown) {
|
||||
const rows = resolveMaterialTableRawRows();
|
||||
applyConvertFactorToMaterialRows(rows, factor, lastConvertFactor.value);
|
||||
lastConvertFactor.value = normalizeMixingConvertFactor(factor);
|
||||
applyMaterialAccumWeight(rows);
|
||||
}
|
||||
|
||||
function handleConvertFactorChange(value: unknown) {
|
||||
if (!showFooter.value || convertFactorApplying.value) {
|
||||
return;
|
||||
}
|
||||
applyConvertFactorToMaterials(value);
|
||||
recalcFillVolume();
|
||||
}
|
||||
|
||||
function stripMaterialRowForSave(row: Recordable) {
|
||||
if (!row) {
|
||||
return row;
|
||||
}
|
||||
const { baseUnitWeight: _baseUnitWeight, ...rest } = row;
|
||||
return rest;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A43】换算系数联动明细单重实时计算-----------
|
||||
|
||||
function recalcMaterialAccumWeight() {
|
||||
applyMaterialAccumWeight();
|
||||
}
|
||||
|
||||
function handleMaterialValueChange(event) {
|
||||
const key = event?.column?.key;
|
||||
const row = event?.row;
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A43】换算系数联动明细单重实时计算-----------
|
||||
if (key === 'unitWeight' && row) {
|
||||
syncMaterialBaseUnitWeightFromDisplay(row, sheetForm.convertFactor);
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A43】换算系数联动明细单重实时计算-----------
|
||||
if (key === 'unitWeight' || key === 'materialKind') {
|
||||
recalcMaterialAccumWeight();
|
||||
}
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A41】橡胶及配合剂明细累计按种类分组合计-----------
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】橡胶及配合剂明细列展示设置-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A19】TCU温度条件表列宽可调且表头换行-----------
|
||||
@@ -710,6 +884,7 @@ const [registerForm, { resetFields, setFieldsValue, validate, setProps }] = useF
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A33】混炼示方主表选择弹窗-----------
|
||||
const [registerMachineModal, { openModal: openMachineModalInner }] = useModal();
|
||||
const [registerIssueNumberModal, { openModal: openIssueNumberModalInner }] = useModal();
|
||||
const [registerMixingMaterialModal, { openModal: openMixingMaterialModalInner }] = useModal();
|
||||
|
||||
function openMachinePicker() {
|
||||
if (!showFooter.value) {
|
||||
@@ -721,6 +896,11 @@ function openMachinePicker() {
|
||||
async function onMachineSelect(payload: Recordable | null) {
|
||||
sheetForm.machineId = payload?.equipmentLedgerId || '';
|
||||
sheetForm.machineName = payload?.equipmentName || '';
|
||||
machineEffectiveVolume.value = payload?.effectiveVolume || '';
|
||||
if (sheetForm.machineId && !machineEffectiveVolume.value) {
|
||||
await loadMachineEffectiveVolume(sheetForm.machineId);
|
||||
}
|
||||
recalcFillVolume();
|
||||
await loadMixerStepOptions(sheetForm.machineId);
|
||||
}
|
||||
|
||||
@@ -738,6 +918,27 @@ function onIssueNumberSelect(payload: Recordable | null) {
|
||||
mixerPsCompilePickerId.value = payload.psCompileId || '';
|
||||
sheetForm.issueNumber = payload.psCode || '';
|
||||
}
|
||||
|
||||
function openMixingMaterialPicker(row: Recordable) {
|
||||
if (!showFooter.value || !row) {
|
||||
return;
|
||||
}
|
||||
materialPickerRow.value = row;
|
||||
openMixingMaterialModalInner(true, { picker: true, ts: Date.now() });
|
||||
}
|
||||
|
||||
function onMixingMaterialSelect(payload: Recordable | null) {
|
||||
if (!payload || !materialPickerRow.value) {
|
||||
return;
|
||||
}
|
||||
applyMixingMaterialFromSelection(
|
||||
materialPickerRow.value,
|
||||
payload,
|
||||
payload.materialKind || payload.materialMinor || '',
|
||||
);
|
||||
recalcMaterialAccumWeight();
|
||||
materialPickerRow.value = null;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A33】混炼示方主表选择弹窗-----------
|
||||
|
||||
function ensureTcuDefaultRows(rows: Recordable[] = []) {
|
||||
@@ -783,6 +984,7 @@ function resetSheetForm() {
|
||||
sheetForm.approveTime = '';
|
||||
sheetForm.changeDate = '';
|
||||
mixerPsCompilePickerId.value = '';
|
||||
machineEffectiveVolume.value = '';
|
||||
refreshSignDisplay({});
|
||||
}
|
||||
|
||||
@@ -806,6 +1008,9 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
|
||||
await resetFields();
|
||||
resetSheetForm();
|
||||
materialData.value = [];
|
||||
materialUnitWeightTotal.value = null;
|
||||
materialAccumWeightTotal.value = null;
|
||||
lastConvertFactor.value = 1;
|
||||
stepData.value = [];
|
||||
downStepData.value = [];
|
||||
tcuData.value = ensureTcuDefaultRows([]);
|
||||
@@ -819,10 +1024,16 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
|
||||
const row = raw?.result || raw;
|
||||
Object.assign(sheetForm, row || {});
|
||||
refreshSignDisplay(row || {});
|
||||
await loadMachineEffectiveVolume(sheetForm.machineId);
|
||||
await loadMixerStepOptions(sheetForm.machineId);
|
||||
await syncSheetToForm();
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A39】编辑页明细补齐默认空行与新增一致-----------
|
||||
materialData.value = ensureMixingDetailRows(row?.materialList || [], DEFAULT_MIXING_MATERIAL_ROW_COUNT);
|
||||
convertFactorApplying.value = true;
|
||||
initMaterialBaseUnitWeights(materialData.value, sheetForm.convertFactor, true);
|
||||
lastConvertFactor.value = normalizeMixingConvertFactor(sheetForm.convertFactor);
|
||||
applyMaterialAccumWeight(materialData.value);
|
||||
convertFactorApplying.value = false;
|
||||
stepData.value = ensureMixingDetailRows(row?.stepList || [], DEFAULT_MIXING_STEP_ROW_COUNT);
|
||||
downStepData.value = ensureMixingDetailRows(row?.downStepList || [], DEFAULT_MIXING_DOWN_STEP_ROW_COUNT);
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A39】编辑页明细补齐默认空行与新增一致-----------
|
||||
@@ -838,6 +1049,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
|
||||
});
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A22】明细表默认空行数-----------
|
||||
materialData.value = createEmptyMaterialRows();
|
||||
lastConvertFactor.value = normalizeMixingConvertFactor(sheetForm.convertFactor);
|
||||
stepData.value = createEmptyStepRows();
|
||||
downStepData.value = createEmptyDownStepRows();
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A22】明细表默认空行数-----------
|
||||
@@ -850,14 +1062,15 @@ const title = computed(() => (!showFooter.value && unref(isUpdate) ? '混炼示
|
||||
async function handleSubmit() {
|
||||
await syncSheetToForm();
|
||||
const formValues = await validate();
|
||||
const materialList = materialRef.value?.getTableData?.() || materialData.value;
|
||||
const materialList = resolveMaterialTableRawRows();
|
||||
applyMaterialAccumWeight(materialList);
|
||||
const stepList = stepRef.value?.getTableData?.() || stepData.value;
|
||||
const downStepList = downStepRef.value?.getTableData?.() || downStepData.value;
|
||||
const tcuList = ensureTcuDefaultRows((tcuRef.value?.getTableData?.() || tcuData.value) as Recordable[]);
|
||||
const cleanRows = (rows: Recordable[]) => (rows || []).filter((row) => Object.values(row || {}).some((v) => v != null && v !== ''));
|
||||
const payload = {
|
||||
...formValues,
|
||||
materialList: cleanRows(materialList),
|
||||
materialList: cleanRows(materialList).map(stripMaterialRowForSave),
|
||||
stepList: cleanRows(stepList),
|
||||
downStepList: cleanRows(downStepList),
|
||||
tcuList: tcuList.map((row) => ({
|
||||
@@ -1008,6 +1221,25 @@ async function handleSubmit() {
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
:deep(.mixing-material-name-cell) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 2px 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:deep(.mixing-material-name-cell.is-disabled) {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
:deep(.mixing-material-name-text) {
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
}
|
||||
//update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A17】顶部施工表对齐旧系统13列表格-----------
|
||||
|
||||
@@ -1027,6 +1259,9 @@ async function handleSubmit() {
|
||||
|
||||
.material-table-wrap {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
:deep(.jeecg-j-vxe-table),
|
||||
:deep(.j-vxe-table-box),
|
||||
@@ -1040,6 +1275,76 @@ async function handleSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
//update-begin---author:cursor ---date:20260525 for:【XSLMES-20260525-A42】橡胶及配合剂明细底部固定合计行-----------
|
||||
.material-table-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
min-width: min-content;
|
||||
}
|
||||
|
||||
.material-table-body {
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.vxe-table--footer-wrapper) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.col--mixerMaterialName .vxe-cell) {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.col--mixerMaterialName .vxe-cell > div) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.material-table-footer {
|
||||
flex-shrink: 0;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-top: none;
|
||||
background: #fafafa;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.material-table-footer-row {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.material-footer-seq,
|
||||
.material-footer-cell {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
box-sizing: border-box;
|
||||
padding: 0 2px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.material-footer-seq {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.material-footer-cell.is-label,
|
||||
.material-footer-cell.is-total {
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
}
|
||||
//update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A42】橡胶及配合剂明细底部固定合计行-----------
|
||||
|
||||
//update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A26】TCU紧凑两行且胶料表向下扩展-----------
|
||||
.material-table-wrap--fill {
|
||||
flex: 1;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"java.import.maven.enabled": true,
|
||||
"java.configuration.updateBuildConfiguration": "automatic",
|
||||
"java.autobuild.enabled": true,
|
||||
"java.import.maven.offline.enabled": false,
|
||||
"java.configuration.maven.notCoveredPluginExecutionSeverity": "ignore",
|
||||
"java.jdt.ls.java.home": "C:\\Program Files\\Java\\jdk-17",
|
||||
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable",
|
||||
"java.import.exclusions": [
|
||||
|
||||
Reference in New Issue
Block a user