新增物料类型处理逻辑,确保在保存和编辑称重记录时自动设置默认物料类型。更新前端表单以支持密炼物料的选择和显示,优化用户体验。添加分类字典和数据字典的事件广播功能,增强系统的实时数据同步能力。
This commit is contained in:
@@ -379,6 +379,7 @@ public class MesXslDesktopAnonController {
|
|||||||
mesXslWeightRecord.getGrossWeight().subtract(mesXslWeightRecord.getTareWeight()));
|
mesXslWeightRecord.getGrossWeight().subtract(mesXslWeightRecord.getTareWeight()));
|
||||||
}
|
}
|
||||||
applyWeightBillType(mesXslWeightRecord);
|
applyWeightBillType(mesXslWeightRecord);
|
||||||
|
applyMaterialTypeDefault(mesXslWeightRecord);
|
||||||
weightRecordService.save(mesXslWeightRecord);
|
weightRecordService.save(mesXslWeightRecord);
|
||||||
stompNotify.publishWeightRecordChanged("add", mesXslWeightRecord.getId());
|
stompNotify.publishWeightRecordChanged("add", mesXslWeightRecord.getId());
|
||||||
return Result.OK("添加成功!");
|
return Result.OK("添加成功!");
|
||||||
@@ -396,6 +397,7 @@ public class MesXslDesktopAnonController {
|
|||||||
mesXslWeightRecord.getGrossWeight().subtract(mesXslWeightRecord.getTareWeight()));
|
mesXslWeightRecord.getGrossWeight().subtract(mesXslWeightRecord.getTareWeight()));
|
||||||
}
|
}
|
||||||
applyWeightBillType(mesXslWeightRecord);
|
applyWeightBillType(mesXslWeightRecord);
|
||||||
|
applyMaterialTypeDefault(mesXslWeightRecord);
|
||||||
boolean ok = weightRecordService.updateById(mesXslWeightRecord);
|
boolean ok = weightRecordService.updateById(mesXslWeightRecord);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return Result.error("数据已被他人修改,请刷新后重试");
|
return Result.error("数据已被他人修改,请刷新后重试");
|
||||||
@@ -436,6 +438,13 @@ public class MesXslDesktopAnonController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyMaterialTypeDefault(MesXslWeightRecord record) {
|
||||||
|
if (oConvertUtils.isEmpty(record.getMaterialType())) {
|
||||||
|
// 默认“自动”,桌面端手动勾选时会传“2”
|
||||||
|
record.setMaterialType("1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void applyVehicleBelong(MesXslVehicle v) {
|
private void applyVehicleBelong(MesXslVehicle v) {
|
||||||
if (oConvertUtils.isEmpty(v.getVehicleBelong())) {
|
if (oConvertUtils.isEmpty(v.getVehicleBelong())) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package org.jeecg.modules.xslmes.controller;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
import org.jeecg.common.system.query.QueryGenerator;
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
|
||||||
|
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialEntryService;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.jeecg.common.system.base.controller.JeecgController;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 原料入场记录
|
||||||
|
* @Author: jeecg-boot
|
||||||
|
* @Date: 2026-05-07
|
||||||
|
* @Version: V1.0
|
||||||
|
*/
|
||||||
|
@Tag(name = "原料入场记录")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/xslmes/mesXslRawMaterialEntry")
|
||||||
|
@Slf4j
|
||||||
|
public class MesXslRawMaterialEntryController extends JeecgController<MesXslRawMaterialEntry, IMesXslRawMaterialEntryService> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IMesXslRawMaterialEntryService mesXslRawMaterialEntryService;
|
||||||
|
|
||||||
|
@Operation(summary = "原料入场记录-分页列表查询")
|
||||||
|
@GetMapping(value = "/list")
|
||||||
|
public Result<IPage<MesXslRawMaterialEntry>> queryPageList(MesXslRawMaterialEntry mesXslRawMaterialEntry,
|
||||||
|
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||||
|
HttpServletRequest req) {
|
||||||
|
QueryWrapper<MesXslRawMaterialEntry> queryWrapper = QueryGenerator.initQueryWrapper(mesXslRawMaterialEntry, req.getParameterMap());
|
||||||
|
Page<MesXslRawMaterialEntry> page = new Page<>(pageNo, pageSize);
|
||||||
|
IPage<MesXslRawMaterialEntry> pageList = mesXslRawMaterialEntryService.page(page, queryWrapper);
|
||||||
|
return Result.OK(pageList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AutoLog(value = "原料入场记录-添加")
|
||||||
|
@Operation(summary = "原料入场记录-添加")
|
||||||
|
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:add")
|
||||||
|
@PostMapping(value = "/add")
|
||||||
|
public Result<String> add(@RequestBody MesXslRawMaterialEntry mesXslRawMaterialEntry) {
|
||||||
|
mesXslRawMaterialEntryService.save(mesXslRawMaterialEntry);
|
||||||
|
return Result.OK("添加成功!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@AutoLog(value = "原料入场记录-编辑")
|
||||||
|
@Operation(summary = "原料入场记录-编辑")
|
||||||
|
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:edit")
|
||||||
|
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||||
|
public Result<String> edit(@RequestBody MesXslRawMaterialEntry mesXslRawMaterialEntry) {
|
||||||
|
mesXslRawMaterialEntryService.updateById(mesXslRawMaterialEntry);
|
||||||
|
return Result.OK("编辑成功!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@AutoLog(value = "原料入场记录-通过id删除")
|
||||||
|
@Operation(summary = "原料入场记录-通过id删除")
|
||||||
|
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:delete")
|
||||||
|
@DeleteMapping(value = "/delete")
|
||||||
|
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
|
||||||
|
mesXslRawMaterialEntryService.removeById(id);
|
||||||
|
return Result.OK("删除成功!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@AutoLog(value = "原料入场记录-批量删除")
|
||||||
|
@Operation(summary = "原料入场记录-批量删除")
|
||||||
|
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:deleteBatch")
|
||||||
|
@DeleteMapping(value = "/deleteBatch")
|
||||||
|
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
|
||||||
|
this.mesXslRawMaterialEntryService.removeByIds(Arrays.asList(ids.split(",")));
|
||||||
|
return Result.OK("批量删除成功!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "原料入场记录-通过id查询")
|
||||||
|
@GetMapping(value = "/queryById")
|
||||||
|
public Result<MesXslRawMaterialEntry> queryById(@RequestParam(name = "id", required = true) String id) {
|
||||||
|
MesXslRawMaterialEntry mesXslRawMaterialEntry = mesXslRawMaterialEntryService.getById(id);
|
||||||
|
if (mesXslRawMaterialEntry == null) {
|
||||||
|
return Result.error("未找到对应数据");
|
||||||
|
}
|
||||||
|
return Result.OK(mesXslRawMaterialEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:exportXls")
|
||||||
|
@RequestMapping(value = "/exportXls")
|
||||||
|
public ModelAndView exportXls(HttpServletRequest request, MesXslRawMaterialEntry mesXslRawMaterialEntry) {
|
||||||
|
return super.exportXls(request, mesXslRawMaterialEntry, MesXslRawMaterialEntry.class, "原料入场记录");
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:importExcel")
|
||||||
|
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||||
|
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
return super.importExcel(request, response, MesXslRawMaterialEntry.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,7 @@ public class MesXslWeightRecordController extends JeecgController<MesXslWeightRe
|
|||||||
mesXslWeightRecord.setBillNo("BDH-" + dateStr + seq);
|
mesXslWeightRecord.setBillNo("BDH-" + dateStr + seq);
|
||||||
}
|
}
|
||||||
computeBillType(mesXslWeightRecord);
|
computeBillType(mesXslWeightRecord);
|
||||||
|
applyMaterialTypeDefault(mesXslWeightRecord);
|
||||||
computeNetWeight(mesXslWeightRecord);
|
computeNetWeight(mesXslWeightRecord);
|
||||||
mesXslWeightRecordService.save(mesXslWeightRecord);
|
mesXslWeightRecordService.save(mesXslWeightRecord);
|
||||||
stompNotifyService.publishWeightRecordChanged("add", mesXslWeightRecord.getId());
|
stompNotifyService.publishWeightRecordChanged("add", mesXslWeightRecord.getId());
|
||||||
@@ -77,6 +78,7 @@ public class MesXslWeightRecordController extends JeecgController<MesXslWeightRe
|
|||||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||||
public Result<String> edit(@RequestBody MesXslWeightRecord mesXslWeightRecord) {
|
public Result<String> edit(@RequestBody MesXslWeightRecord mesXslWeightRecord) {
|
||||||
computeBillType(mesXslWeightRecord);
|
computeBillType(mesXslWeightRecord);
|
||||||
|
applyMaterialTypeDefault(mesXslWeightRecord);
|
||||||
computeNetWeight(mesXslWeightRecord);
|
computeNetWeight(mesXslWeightRecord);
|
||||||
mesXslWeightRecordService.updateById(mesXslWeightRecord);
|
mesXslWeightRecordService.updateById(mesXslWeightRecord);
|
||||||
stompNotifyService.publishWeightRecordChanged("edit", mesXslWeightRecord.getId());
|
stompNotifyService.publishWeightRecordChanged("edit", mesXslWeightRecord.getId());
|
||||||
@@ -147,4 +149,11 @@ public class MesXslWeightRecordController extends JeecgController<MesXslWeightRe
|
|||||||
record.setBillType("3");
|
record.setBillType("3");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyMaterialTypeDefault(MesXslWeightRecord record) {
|
||||||
|
if (record.getMaterialType() == null || record.getMaterialType().isBlank()) {
|
||||||
|
// 默认“自动”,手动模式由前端显式传“2”
|
||||||
|
record.setMaterialType("1");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
package org.jeecg.modules.xslmes.entity;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||||
|
import org.jeecg.common.aspect.annotation.Dict;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 原料入场记录
|
||||||
|
* @Author: jeecg-boot
|
||||||
|
* @Date: 2026-05-07
|
||||||
|
* @Version: V1.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("mes_xsl_raw_material_entry")
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Schema(description = "原料入场记录")
|
||||||
|
public class MesXslRawMaterialEntry implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
@Schema(description = "主键")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Excel(name = "条码", width = 20)
|
||||||
|
@Schema(description = "条码")
|
||||||
|
private String barcode;
|
||||||
|
|
||||||
|
@Excel(name = "批次号", width = 20)
|
||||||
|
@Schema(description = "批次号")
|
||||||
|
private String batchNo;
|
||||||
|
|
||||||
|
@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")
|
||||||
|
@Schema(description = "入场时间")
|
||||||
|
private Date entryTime;
|
||||||
|
|
||||||
|
@Schema(description = "榜单ID")
|
||||||
|
private String weightRecordId;
|
||||||
|
|
||||||
|
@Excel(name = "榜单号", width = 20)
|
||||||
|
@Schema(description = "榜单号")
|
||||||
|
private String billNo;
|
||||||
|
|
||||||
|
@Schema(description = "物料ID")
|
||||||
|
private String materialId;
|
||||||
|
|
||||||
|
@Excel(name = "物料名称", width = 20)
|
||||||
|
@Schema(description = "物料名称")
|
||||||
|
private String materialName;
|
||||||
|
|
||||||
|
@Excel(name = "供料客户", width = 20)
|
||||||
|
@Schema(description = "供料客户")
|
||||||
|
private String supplyCustomer;
|
||||||
|
|
||||||
|
@Schema(description = "供应商ID")
|
||||||
|
private String supplierId;
|
||||||
|
|
||||||
|
@Excel(name = "供应商名称", width = 25)
|
||||||
|
@Schema(description = "供应商名称")
|
||||||
|
private String supplierName;
|
||||||
|
|
||||||
|
@Excel(name = "厂家物料名称", width = 25)
|
||||||
|
@Schema(description = "厂家物料名称")
|
||||||
|
private String manufacturerMaterialName;
|
||||||
|
|
||||||
|
@Excel(name = "保质期", width = 15)
|
||||||
|
@Schema(description = "保质期")
|
||||||
|
private String shelfLife;
|
||||||
|
|
||||||
|
@Excel(name = "总重(KG)", width = 12)
|
||||||
|
@Schema(description = "总重(KG)")
|
||||||
|
private BigDecimal totalWeight;
|
||||||
|
|
||||||
|
@Excel(name = "总份数", width = 10)
|
||||||
|
@Schema(description = "总份数")
|
||||||
|
private Integer totalPortions;
|
||||||
|
|
||||||
|
@Excel(name = "每份总重(KG)", width = 12)
|
||||||
|
@Schema(description = "每份总重(KG)")
|
||||||
|
private BigDecimal portionWeight;
|
||||||
|
|
||||||
|
@Excel(name = "每份包数", width = 10)
|
||||||
|
@Schema(description = "每份包数")
|
||||||
|
private Integer portionPackages;
|
||||||
|
|
||||||
|
@Excel(name = "检测结果", width = 12, dicCode = "xslmes_test_result")
|
||||||
|
@Dict(dicCode = "xslmes_test_result")
|
||||||
|
@Schema(description = "检测结果(字典 xslmes_test_result:0未检 1合格 2不合格)")
|
||||||
|
private String testResult;
|
||||||
|
|
||||||
|
@Excel(name = "检测状态", width = 12, dicCode = "xslmes_test_status")
|
||||||
|
@Dict(dicCode = "xslmes_test_status")
|
||||||
|
@Schema(description = "检测状态(字典 xslmes_test_status:0送样 1已批准)")
|
||||||
|
private String testStatus;
|
||||||
|
|
||||||
|
@Excel(name = "打印标记", width = 12, dicCode = "xslmes_print_flag")
|
||||||
|
@Dict(dicCode = "xslmes_print_flag")
|
||||||
|
@Schema(description = "打印标记(字典 xslmes_print_flag:1已打印 0未打印)")
|
||||||
|
private String printFlag;
|
||||||
|
|
||||||
|
@Excel(name = "入库结存", width = 10, dicCode = "yn")
|
||||||
|
@Dict(dicCode = "yn")
|
||||||
|
@Schema(description = "入库结存(字典 yn,1是 0否)")
|
||||||
|
private String stockBalance;
|
||||||
|
|
||||||
|
@Excel(name = "库位", width = 15)
|
||||||
|
@Schema(description = "库位")
|
||||||
|
private String warehouseLocation;
|
||||||
|
|
||||||
|
@Excel(name = "卸货人", width = 15)
|
||||||
|
@Schema(description = "卸货人")
|
||||||
|
private String unloadOperator;
|
||||||
|
|
||||||
|
@Excel(name = "是否特采", width = 10, dicCode = "yn")
|
||||||
|
@Dict(dicCode = "yn")
|
||||||
|
@Schema(description = "是否特采(字典 yn,1是 0否)")
|
||||||
|
private String isSpecialAdoption;
|
||||||
|
|
||||||
|
@Excel(name = "特采操作人", width = 15)
|
||||||
|
@Schema(description = "特采操作人")
|
||||||
|
private String specialAdoptionOperator;
|
||||||
|
|
||||||
|
@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")
|
||||||
|
@Schema(description = "特采时间")
|
||||||
|
private Date specialAdoptionTime;
|
||||||
|
|
||||||
|
@Excel(name = "特采原因", width = 30)
|
||||||
|
@Schema(description = "特采原因")
|
||||||
|
private String specialAdoptionReason;
|
||||||
|
|
||||||
|
@Excel(name = "状态", width = 10, dicCode = "xslmes_entry_status")
|
||||||
|
@Dict(dicCode = "xslmes_entry_status")
|
||||||
|
@Schema(description = "状态(字典 xslmes_entry_status)")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Excel(name = "备注", width = 30)
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**创建人*/
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
/**创建日期*/
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Schema(description = "创建日期")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**更新人*/
|
||||||
|
@Schema(description = "更新人")
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
/**更新日期*/
|
||||||
|
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Schema(description = "更新日期")
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
private Integer tenantId;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.jeecg.modules.xslmes.entity;
|
package org.jeecg.modules.xslmes.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -23,6 +24,26 @@ import java.util.Date;
|
|||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
@TableName("mes_xsl_weight_record")
|
@TableName("mes_xsl_weight_record")
|
||||||
@Schema(description = "地磅数据记录")
|
@Schema(description = "地磅数据记录")
|
||||||
|
@JsonPropertyOrder({
|
||||||
|
"billNo",
|
||||||
|
"weighDate",
|
||||||
|
"inoutDirection",
|
||||||
|
"vehicleId",
|
||||||
|
"plateNumber",
|
||||||
|
"senderUnit",
|
||||||
|
"receiverUnit",
|
||||||
|
// 将“密炼物料”放到“毛重”之前:桌面端可能按 JSON 字段顺序动态生成列
|
||||||
|
"mixerMaterialIds",
|
||||||
|
"mixerMaterialNames",
|
||||||
|
"materialType",
|
||||||
|
"grossWeight",
|
||||||
|
"tareWeight",
|
||||||
|
"netWeight",
|
||||||
|
"driverName",
|
||||||
|
"driverPhone",
|
||||||
|
"billType",
|
||||||
|
"tenantId"
|
||||||
|
})
|
||||||
public class MesXslWeightRecord extends JeecgEntity implements Serializable {
|
public class MesXslWeightRecord extends JeecgEntity implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@@ -57,10 +78,6 @@ public class MesXslWeightRecord extends JeecgEntity implements Serializable {
|
|||||||
@Schema(description = "收货单位(出厂时为客户简称)")
|
@Schema(description = "收货单位(出厂时为客户简称)")
|
||||||
private String receiverUnit;
|
private String receiverUnit;
|
||||||
|
|
||||||
@Excel(name = "货物名称", width = 20)
|
|
||||||
@Schema(description = "货物名称")
|
|
||||||
private String goodsName;
|
|
||||||
|
|
||||||
@Excel(name = "毛重(KG)", width = 12)
|
@Excel(name = "毛重(KG)", width = 12)
|
||||||
@Schema(description = "毛重(KG),实际称量")
|
@Schema(description = "毛重(KG),实际称量")
|
||||||
private BigDecimal grossWeight;
|
private BigDecimal grossWeight;
|
||||||
@@ -86,6 +103,19 @@ public class MesXslWeightRecord extends JeecgEntity implements Serializable {
|
|||||||
@Schema(description = "单据类型:1已称毛重 2称重完成")
|
@Schema(description = "单据类型:1已称毛重 2称重完成")
|
||||||
private String billType;
|
private String billType;
|
||||||
|
|
||||||
|
@Excel(name = "密炼物料ID", width = 30)
|
||||||
|
@Schema(description = "密炼物料ID(分号分隔)")
|
||||||
|
private String mixerMaterialIds;
|
||||||
|
|
||||||
|
@Excel(name = "密炼物料名称", width = 30)
|
||||||
|
@Schema(description = "密炼物料名称(分号分隔)")
|
||||||
|
private String mixerMaterialNames;
|
||||||
|
|
||||||
|
@Excel(name = "类型", width = 10, dicCode = "xslmes_weight_material_type")
|
||||||
|
@Dict(dicCode = "xslmes_weight_material_type")
|
||||||
|
@Schema(description = "磅单物料类型:1自动 2手动")
|
||||||
|
private String materialType;
|
||||||
|
|
||||||
@Schema(description = "租户ID")
|
@Schema(description = "租户ID")
|
||||||
private Integer tenantId;
|
private Integer tenantId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.jeecg.modules.xslmes.mapper;
|
||||||
|
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 原料入场记录
|
||||||
|
* @Author: jeecg-boot
|
||||||
|
* @Date: 2026-05-07
|
||||||
|
* @Version: V1.0
|
||||||
|
*/
|
||||||
|
public interface MesXslRawMaterialEntryMapper extends BaseMapper<MesXslRawMaterialEntry> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="org.jeecg.modules.xslmes.mapper.MesXslRawMaterialEntryMapper">
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.jeecg.modules.xslmes.service;
|
||||||
|
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 原料入场记录
|
||||||
|
* @Author: jeecg-boot
|
||||||
|
* @Date: 2026-05-07
|
||||||
|
* @Version: V1.0
|
||||||
|
*/
|
||||||
|
public interface IMesXslRawMaterialEntryService extends IService<MesXslRawMaterialEntry> {
|
||||||
|
}
|
||||||
@@ -45,6 +45,16 @@ public class MesXslStompNotifyService {
|
|||||||
publish("/topic/sync/mes-mixer-materials", "MIXER_MATERIAL_CHANGED", "mixerMaterialId", mixerMaterialId, action);
|
publish("/topic/sync/mes-mixer-materials", "MIXER_MATERIAL_CHANGED", "mixerMaterialId", mixerMaterialId, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 广播分类字典变更事件到 /topic/sync/sys-categories */
|
||||||
|
public void publishSysCategoryChanged(String action, String categoryId) {
|
||||||
|
publish("/topic/sync/sys-categories", "SYS_CATEGORY_CHANGED", "categoryId", categoryId, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 广播数据字典变更事件到 /topic/sync/sys-dicts */
|
||||||
|
public void publishSysDictChanged(String action, String dictId) {
|
||||||
|
publish("/topic/sync/sys-dicts", "SYS_DICT_CHANGED", "dictId", dictId, action);
|
||||||
|
}
|
||||||
|
|
||||||
// ─────────────────────────── 私有辅助 ────────────────────────────
|
// ─────────────────────────── 私有辅助 ────────────────────────────
|
||||||
|
|
||||||
private void publish(String topic, String cmd, String idKey, String idValue, String action) {
|
private void publish(String topic, String cmd, String idKey, String idValue, String action) {
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.jeecg.modules.xslmes.service.impl;
|
||||||
|
|
||||||
|
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
|
||||||
|
import org.jeecg.modules.xslmes.mapper.MesXslRawMaterialEntryMapper;
|
||||||
|
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialEntryService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 原料入场记录
|
||||||
|
* @Author: jeecg-boot
|
||||||
|
* @Date: 2026-05-07
|
||||||
|
* @Version: V1.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class MesXslRawMaterialEntryServiceImpl extends ServiceImpl<MesXslRawMaterialEntryMapper, MesXslRawMaterialEntry> implements IMesXslRawMaterialEntryService {
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import org.jeecgframework.poi.excel.entity.ImportParams;
|
|||||||
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
|
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
|
||||||
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
|
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||||
@@ -51,6 +52,8 @@ import java.util.stream.Collectors;
|
|||||||
public class SysCategoryController {
|
public class SysCategoryController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISysCategoryService sysCategoryService;
|
private ISysCategoryService sysCategoryService;
|
||||||
|
@Autowired
|
||||||
|
private SimpMessagingTemplate stompTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分类编码0
|
* 分类编码0
|
||||||
@@ -128,6 +131,7 @@ public class SysCategoryController {
|
|||||||
try {
|
try {
|
||||||
sysCategoryService.addSysCategory(sysCategory);
|
sysCategoryService.addSysCategory(sysCategory);
|
||||||
result.success("添加成功!");
|
result.success("添加成功!");
|
||||||
|
publishCategoryChanged("add", sysCategory.getId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(),e);
|
log.error(e.getMessage(),e);
|
||||||
result.error500("操作失败");
|
result.error500("操作失败");
|
||||||
@@ -149,6 +153,7 @@ public class SysCategoryController {
|
|||||||
}else {
|
}else {
|
||||||
sysCategoryService.updateSysCategory(sysCategory);
|
sysCategoryService.updateSysCategory(sysCategory);
|
||||||
result.success("修改成功!");
|
result.success("修改成功!");
|
||||||
|
publishCategoryChanged("edit", sysCategory.getId());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -167,8 +172,8 @@ public class SysCategoryController {
|
|||||||
}else {
|
}else {
|
||||||
this.sysCategoryService.deleteSysCategory(id);
|
this.sysCategoryService.deleteSysCategory(id);
|
||||||
result.success("删除成功!");
|
result.success("删除成功!");
|
||||||
|
publishCategoryChanged("delete", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +190,7 @@ public class SysCategoryController {
|
|||||||
}else {
|
}else {
|
||||||
this.sysCategoryService.deleteSysCategory(ids);
|
this.sysCategoryService.deleteSysCategory(ids);
|
||||||
result.success("删除成功!");
|
result.success("删除成功!");
|
||||||
|
publishCategoryChanged("deleteBatch", null);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -625,5 +631,16 @@ public class SysCategoryController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void publishCategoryChanged(String action, String categoryId) {
|
||||||
|
try {
|
||||||
|
String json = "{\"cmd\":\"SYS_CATEGORY_CHANGED\",\"action\":\"" + action + "\""
|
||||||
|
+ (categoryId != null ? ",\"categoryId\":\"" + categoryId + "\"" : "")
|
||||||
|
+ ",\"timestamp\":" + System.currentTimeMillis() + "}";
|
||||||
|
stompTemplate.convertAndSend("/topic/sync/sys-categories", json);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("广播分类字典变更失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
@@ -77,6 +78,8 @@ public class SysDictController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private SimpMessagingTemplate stompTemplate;
|
||||||
|
@Autowired
|
||||||
private ShiroRealm shiroRealm;
|
private ShiroRealm shiroRealm;
|
||||||
|
|
||||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||||
@@ -502,6 +505,7 @@ public class SysDictController {
|
|||||||
sysDict.setDelFlag(CommonConstant.DEL_FLAG_0);
|
sysDict.setDelFlag(CommonConstant.DEL_FLAG_0);
|
||||||
sysDictService.save(sysDict);
|
sysDictService.save(sysDict);
|
||||||
result.success("保存成功!");
|
result.success("保存成功!");
|
||||||
|
publishDictChanged("add", sysDict.getId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(),e);
|
log.error(e.getMessage(),e);
|
||||||
result.error500("操作失败");
|
result.error500("操作失败");
|
||||||
@@ -526,6 +530,7 @@ public class SysDictController {
|
|||||||
boolean ok = sysDictService.updateById(sysDict);
|
boolean ok = sysDictService.updateById(sysDict);
|
||||||
if(ok) {
|
if(ok) {
|
||||||
result.success("编辑成功!");
|
result.success("编辑成功!");
|
||||||
|
publishDictChanged("edit", sysDict.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -544,6 +549,7 @@ public class SysDictController {
|
|||||||
boolean ok = sysDictService.removeById(id);
|
boolean ok = sysDictService.removeById(id);
|
||||||
if(ok) {
|
if(ok) {
|
||||||
result.success("删除成功!");
|
result.success("删除成功!");
|
||||||
|
publishDictChanged("delete", id);
|
||||||
}else{
|
}else{
|
||||||
result.error500("删除失败!");
|
result.error500("删除失败!");
|
||||||
}
|
}
|
||||||
@@ -565,6 +571,7 @@ public class SysDictController {
|
|||||||
}else {
|
}else {
|
||||||
sysDictService.removeByIds(Arrays.asList(ids.split(",")));
|
sysDictService.removeByIds(Arrays.asList(ids.split(",")));
|
||||||
result.success("删除成功!");
|
result.success("删除成功!");
|
||||||
|
publishDictChanged("deleteBatch", null);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -870,4 +877,15 @@ public class SysDictController {
|
|||||||
sysDictService.editDictByLowAppId(sysDictVo);
|
sysDictService.editDictByLowAppId(sysDictVo);
|
||||||
return Result.ok("编辑成功");
|
return Result.ok("编辑成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void publishDictChanged(String action, String dictId) {
|
||||||
|
try {
|
||||||
|
String json = "{\"cmd\":\"SYS_DICT_CHANGED\",\"action\":\"" + action + "\""
|
||||||
|
+ (dictId != null ? ",\"dictId\":\"" + dictId + "\"" : "")
|
||||||
|
+ ",\"timestamp\":" + System.currentTimeMillis() + "}";
|
||||||
|
stompTemplate.convertAndSend("/topic/sync/sys-dicts", json);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("广播数据字典变更失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.jeecg.modules.system.entity.SysDictItem;
|
|||||||
import org.jeecg.modules.system.service.ISysDictItemService;
|
import org.jeecg.modules.system.service.ISysDictItemService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
@@ -48,6 +49,8 @@ public class SysDictItemController {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISysDictItemService sysDictItemService;
|
private ISysDictItemService sysDictItemService;
|
||||||
|
@Autowired
|
||||||
|
private SimpMessagingTemplate stompTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @功能:查询字典数据
|
* @功能:查询字典数据
|
||||||
@@ -83,6 +86,7 @@ public class SysDictItemController {
|
|||||||
sysDictItem.setCreateTime(new Date());
|
sysDictItem.setCreateTime(new Date());
|
||||||
sysDictItemService.save(sysDictItem);
|
sysDictItemService.save(sysDictItem);
|
||||||
result.success("保存成功!");
|
result.success("保存成功!");
|
||||||
|
publishDictChanged("add", sysDictItem.getId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(),e);
|
log.error(e.getMessage(),e);
|
||||||
result.error500("操作失败");
|
result.error500("操作失败");
|
||||||
@@ -109,6 +113,7 @@ public class SysDictItemController {
|
|||||||
//TODO 返回false说明什么?
|
//TODO 返回false说明什么?
|
||||||
if(ok) {
|
if(ok) {
|
||||||
result.success("编辑成功!");
|
result.success("编辑成功!");
|
||||||
|
publishDictChanged("edit", sysDictItem.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -131,6 +136,7 @@ public class SysDictItemController {
|
|||||||
boolean ok = sysDictItemService.removeById(id);
|
boolean ok = sysDictItemService.removeById(id);
|
||||||
if(ok) {
|
if(ok) {
|
||||||
result.success("删除成功!");
|
result.success("删除成功!");
|
||||||
|
publishDictChanged("delete", id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -151,6 +157,7 @@ public class SysDictItemController {
|
|||||||
}else {
|
}else {
|
||||||
this.sysDictItemService.removeByIds(Arrays.asList(ids.split(",")));
|
this.sysDictItemService.removeByIds(Arrays.asList(ids.split(",")));
|
||||||
result.success("删除成功!");
|
result.success("删除成功!");
|
||||||
|
publishDictChanged("deleteBatch", null);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -183,4 +190,14 @@ public class SysDictItemController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void publishDictChanged(String action, String itemId) {
|
||||||
|
try {
|
||||||
|
String json = "{\"cmd\":\"SYS_DICT_CHANGED\",\"action\":\"" + action + "\""
|
||||||
|
+ (itemId != null ? ",\"dictId\":\"" + itemId + "\"" : "") + "}";
|
||||||
|
stompTemplate.convertAndSend("/topic/sync/sys-dicts", json);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[数据字典] STOMP推送失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE mes_xsl_weight_record
|
||||||
|
ADD COLUMN mixer_material_ids VARCHAR(2000) NULL COMMENT '密炼物料ID(分号分隔)',
|
||||||
|
ADD COLUMN mixer_material_names VARCHAR(2000) NULL COMMENT '密炼物料名称(分号分隔)';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE mes_xsl_weight_record DROP COLUMN goods_name;
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
-- 磅单新增物料类型字段 + 字典(幂等)
|
||||||
|
|
||||||
|
-- 1) 表结构:物料类型(1自动 2手动)
|
||||||
|
SET @material_type_exists := (
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'mes_xsl_weight_record'
|
||||||
|
AND COLUMN_NAME = 'material_type'
|
||||||
|
);
|
||||||
|
SET @ddl_material_type := IF(
|
||||||
|
@material_type_exists = 0,
|
||||||
|
'ALTER TABLE `mes_xsl_weight_record` ADD COLUMN `material_type` varchar(10) DEFAULT NULL COMMENT ''物料类型(字典xslmes_weight_material_type:1自动 2手动)'' AFTER `mixer_material_names`',
|
||||||
|
'SELECT 1'
|
||||||
|
);
|
||||||
|
PREPARE stmt_material_type FROM @ddl_material_type;
|
||||||
|
EXECUTE stmt_material_type;
|
||||||
|
DEALLOCATE PREPARE stmt_material_type;
|
||||||
|
|
||||||
|
-- 2) 历史数据默认自动
|
||||||
|
UPDATE `mes_xsl_weight_record`
|
||||||
|
SET `material_type` = '1'
|
||||||
|
WHERE `material_type` IS NULL OR `material_type` = '';
|
||||||
|
|
||||||
|
-- 3) 字典主表
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES磅单物料类型', 'xslmes_weight_material_type', '磅单密炼物料来源类型:自动/手动', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_weight_material_type' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
-- 4) 字典项:自动
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '自动', '1', 1, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_weight_material_type'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_dict_item` i
|
||||||
|
WHERE i.`dict_id` = d.`id` AND i.`item_value` = '1'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 5) 字典项:手动
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '手动', '2', 2, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_weight_material_type'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_dict_item` i
|
||||||
|
WHERE i.`dict_id` = d.`id` AND i.`item_value` = '2'
|
||||||
|
);
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
-- 原料入场记录:建表 + 字典 + 菜单权限(幂等)
|
||||||
|
|
||||||
|
-- ===================== 1. 建表 =====================
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_raw_material_entry` (
|
||||||
|
`id` varchar(36) NOT NULL COMMENT '主键',
|
||||||
|
`barcode` varchar(100) DEFAULT NULL COMMENT '条码',
|
||||||
|
`batch_no` varchar(100) DEFAULT NULL COMMENT '批次号',
|
||||||
|
`entry_time` datetime DEFAULT NULL COMMENT '入场时间',
|
||||||
|
`weight_record_id` varchar(36) DEFAULT NULL COMMENT '榜单ID',
|
||||||
|
`bill_no` varchar(100) DEFAULT NULL COMMENT '榜单号',
|
||||||
|
`material_id` varchar(36) DEFAULT NULL COMMENT '物料ID',
|
||||||
|
`material_name` varchar(200) DEFAULT NULL COMMENT '物料名称',
|
||||||
|
`supply_customer` varchar(200) DEFAULT NULL COMMENT '供料客户',
|
||||||
|
`supplier_id` varchar(36) DEFAULT NULL COMMENT '供应商ID',
|
||||||
|
`supplier_name` varchar(200) DEFAULT NULL COMMENT '供应商名称',
|
||||||
|
`manufacturer_material_name` varchar(200) DEFAULT NULL COMMENT '厂家物料名称',
|
||||||
|
`shelf_life` varchar(100) DEFAULT NULL COMMENT '保质期',
|
||||||
|
`total_weight` decimal(10,2) DEFAULT NULL COMMENT '总重(KG)',
|
||||||
|
`total_portions` int DEFAULT NULL COMMENT '总份数',
|
||||||
|
`portion_weight` decimal(10,2) DEFAULT NULL COMMENT '每份总重(KG)',
|
||||||
|
`portion_packages` int DEFAULT NULL COMMENT '每份包数',
|
||||||
|
`test_result` varchar(10) DEFAULT NULL COMMENT '检测结果(字典 xslmes_test_result:0未检 1合格 2不合格)',
|
||||||
|
`test_status` varchar(10) DEFAULT NULL COMMENT '检测状态(字典 xslmes_test_status:0送样 1已批准)',
|
||||||
|
`print_flag` varchar(10) DEFAULT NULL COMMENT '打印标记(字典 xslmes_print_flag:1已打印 0未打印)',
|
||||||
|
`stock_balance` varchar(2) DEFAULT NULL COMMENT '入库结存(字典 yn:1是 0否)',
|
||||||
|
`warehouse_location` varchar(100) DEFAULT NULL COMMENT '库位',
|
||||||
|
`unload_operator` varchar(100) DEFAULT NULL COMMENT '卸货人',
|
||||||
|
`is_special_adoption` varchar(2) DEFAULT NULL COMMENT '是否特采(字典 yn:1是 0否)',
|
||||||
|
`special_adoption_operator` varchar(100) DEFAULT NULL COMMENT '特采操作人',
|
||||||
|
`special_adoption_time` datetime DEFAULT NULL COMMENT '特采时间',
|
||||||
|
`special_adoption_reason` text COMMENT '特采原因',
|
||||||
|
`status` varchar(10) DEFAULT NULL COMMENT '状态(字典 xslmes_entry_status)',
|
||||||
|
`remark` text 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 '更新时间',
|
||||||
|
`tenant_id` int DEFAULT 1002 COMMENT '租户ID',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_rme_barcode` (`barcode`),
|
||||||
|
KEY `idx_rme_batch_no` (`batch_no`),
|
||||||
|
KEY `idx_rme_entry_time` (`entry_time`),
|
||||||
|
KEY `idx_rme_bill_no` (`bill_no`),
|
||||||
|
KEY `idx_rme_material_id` (`material_id`),
|
||||||
|
KEY `idx_rme_supplier_id` (`supplier_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='原料入场记录';
|
||||||
|
|
||||||
|
-- ===================== 2. 检测结果字典 =====================
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES检测结果', 'xslmes_test_result', '原料入场检测结果:未检/合格/不合格', 0, 'admin', NOW(), 0, 1002
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_test_result' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '未检', '0', 1, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_test_result'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '合格', '1', 2, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_test_result'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '不合格', '2', 3, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_test_result'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '2');
|
||||||
|
|
||||||
|
-- ===================== 3. 检测状态字典 =====================
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES检测状态', 'xslmes_test_status', '原料入场检测状态:送样/已批准', 0, 'admin', NOW(), 0, 1002
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_test_status' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '送样', '0', 1, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_test_status'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '已批准', '1', 2, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_test_status'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
|
||||||
|
-- ===================== 4. 打印标记字典 =====================
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES打印标记', 'xslmes_print_flag', '原料入场打印标记:已打印/未打印', 0, 'admin', NOW(), 0, 1002
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_print_flag' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '已打印', '1', 1, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_print_flag'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '未打印', '0', 2, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_print_flag'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
|
||||||
|
-- ===================== 3. 入场记录状态字典 =====================
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES入场记录状态', 'xslmes_entry_status', '原料入场记录状态:待处理/已入库/已拒收', 0, 'admin', NOW(), 0, 1002
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_entry_status' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '待处理', '0', 1, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_entry_status'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '已入库', '1', 2, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_entry_status'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '已拒收', '2', 3, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_entry_status'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '2');
|
||||||
|
|
||||||
|
-- ===================== 4. 菜单权限(父菜单:MES XSL 1900000000000000300)=====================
|
||||||
|
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 '1900000000000000530', '1900000000000000300', '原料入场记录', '/xslmes/mesXslRawMaterialEntry', 'xslmes/mesXslRawMaterialEntry/MesXslRawMaterialEntryList', 1, NULL, NULL, 1, NULL, '0', 11.00, 0, 'ant-design:file-text-outlined', 0, 1, 0, 0, '原料入场记录', 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
|
||||||
|
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000530');
|
||||||
|
|
||||||
|
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 '1900000000000000531', '1900000000000000530', '添加', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mes_xsl_raw_material_entry:add', '1', 1.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
|
||||||
|
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000531');
|
||||||
|
|
||||||
|
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 '1900000000000000532', '1900000000000000530', '编辑', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mes_xsl_raw_material_entry:edit', '1', 2.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
|
||||||
|
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000532');
|
||||||
|
|
||||||
|
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 '1900000000000000533', '1900000000000000530', '删除', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mes_xsl_raw_material_entry:delete', '1', 3.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
|
||||||
|
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000533');
|
||||||
|
|
||||||
|
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 '1900000000000000534', '1900000000000000530', '批量删除', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mes_xsl_raw_material_entry:deleteBatch', '1', 4.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
|
||||||
|
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000534');
|
||||||
|
|
||||||
|
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 '1900000000000000535', '1900000000000000530', '导出', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mes_xsl_raw_material_entry:exportXls', '1', 5.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
|
||||||
|
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000535');
|
||||||
|
|
||||||
|
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 '1900000000000000536', '1900000000000000530', '导入', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mes_xsl_raw_material_entry:importExcel', '1', 6.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
|
||||||
|
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000536');
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
|
||||||
|
const { createConfirm } = useMessage();
|
||||||
|
|
||||||
|
enum Api {
|
||||||
|
list = '/xslmes/mesXslRawMaterialEntry/list',
|
||||||
|
save = '/xslmes/mesXslRawMaterialEntry/add',
|
||||||
|
edit = '/xslmes/mesXslRawMaterialEntry/edit',
|
||||||
|
deleteOne = '/xslmes/mesXslRawMaterialEntry/delete',
|
||||||
|
deleteBatch = '/xslmes/mesXslRawMaterialEntry/deleteBatch',
|
||||||
|
importExcel = '/xslmes/mesXslRawMaterialEntry/importExcel',
|
||||||
|
exportXls = '/xslmes/mesXslRawMaterialEntry/exportXls',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getExportUrl = Api.exportXls;
|
||||||
|
export const getImportUrl = Api.importExcel;
|
||||||
|
|
||||||
|
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||||
|
|
||||||
|
export const deleteOne = (params, handleSuccess) => {
|
||||||
|
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
|
||||||
|
handleSuccess();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const batchDelete = (params, handleSuccess) => {
|
||||||
|
createConfirm({
|
||||||
|
iconType: 'warning',
|
||||||
|
title: '确认删除',
|
||||||
|
content: '是否删除选中数据',
|
||||||
|
okText: '确认',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: () => {
|
||||||
|
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||||
|
handleSuccess();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveOrUpdate = (params, isUpdate) => {
|
||||||
|
let url = isUpdate ? Api.edit : Api.save;
|
||||||
|
return defHttp.post({ url: url, params });
|
||||||
|
};
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||||
|
|
||||||
|
export const columns: BasicColumn[] = [
|
||||||
|
{ title: '条码', align: 'center', dataIndex: 'barcode', width: 160 },
|
||||||
|
{ title: '批次号', align: 'center', dataIndex: 'batchNo', width: 140 },
|
||||||
|
{
|
||||||
|
title: '入场时间',
|
||||||
|
align: 'center',
|
||||||
|
dataIndex: 'entryTime',
|
||||||
|
width: 160,
|
||||||
|
customRender: ({ text }) => (!text ? '' : text.length > 19 ? text.substring(0, 19) : text),
|
||||||
|
},
|
||||||
|
{ title: '榜单号', align: 'center', dataIndex: 'billNo', width: 160 },
|
||||||
|
{ title: '物料名称', align: 'center', dataIndex: 'materialName', width: 140, ellipsis: true },
|
||||||
|
{ title: '供料客户', align: 'center', dataIndex: 'supplyCustomer', width: 140, ellipsis: true },
|
||||||
|
{ title: '供应商名称', align: 'center', dataIndex: 'supplierName', width: 140, ellipsis: true },
|
||||||
|
{ title: '厂家物料名称', align: 'center', dataIndex: 'manufacturerMaterialName', width: 140, ellipsis: true },
|
||||||
|
{ title: '保质期', align: 'center', dataIndex: 'shelfLife', width: 100 },
|
||||||
|
{ title: '总重(KG)', align: 'center', dataIndex: 'totalWeight', width: 100 },
|
||||||
|
{ title: '总份数', align: 'center', dataIndex: 'totalPortions', width: 80 },
|
||||||
|
{ title: '每份总重(KG)', align: 'center', dataIndex: 'portionWeight', width: 110 },
|
||||||
|
{ title: '每份包数', align: 'center', dataIndex: 'portionPackages', width: 80 },
|
||||||
|
{ title: '检测结果', align: 'center', dataIndex: 'testResult_dictText', width: 90 },
|
||||||
|
{ title: '检测状态', align: 'center', dataIndex: 'testStatus_dictText', width: 90 },
|
||||||
|
{ title: '打印标记', align: 'center', dataIndex: 'printFlag_dictText', width: 90 },
|
||||||
|
{ title: '入库结存', align: 'center', dataIndex: 'stockBalance_dictText', width: 90 },
|
||||||
|
{ title: '库位', align: 'center', dataIndex: 'warehouseLocation', width: 100 },
|
||||||
|
{ title: '卸货人', align: 'center', dataIndex: 'unloadOperator', width: 90 },
|
||||||
|
{ title: '是否特采', align: 'center', dataIndex: 'isSpecialAdoption_dictText', width: 80 },
|
||||||
|
{ title: '特采操作人', align: 'center', dataIndex: 'specialAdoptionOperator', width: 100 },
|
||||||
|
{
|
||||||
|
title: '特采时间',
|
||||||
|
align: 'center',
|
||||||
|
dataIndex: 'specialAdoptionTime',
|
||||||
|
width: 160,
|
||||||
|
customRender: ({ text }) => (!text ? '' : text.length > 19 ? text.substring(0, 19) : text),
|
||||||
|
},
|
||||||
|
{ title: '特采原因', align: 'center', dataIndex: 'specialAdoptionReason', width: 160, ellipsis: true },
|
||||||
|
{ title: '状态', align: 'center', dataIndex: 'status_dictText', width: 80 },
|
||||||
|
{ title: '备注', align: 'center', dataIndex: 'remark', width: 160, ellipsis: true },
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
align: 'center',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
width: 160,
|
||||||
|
customRender: ({ text }) => (!text ? '' : text.length > 19 ? text.substring(0, 19) : text),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const searchFormSchema: FormSchema[] = [
|
||||||
|
{ label: '条码', field: 'barcode', component: 'JInput', colProps: { span: 6 } },
|
||||||
|
{ label: '批次号', field: 'batchNo', component: 'JInput', colProps: { span: 6 } },
|
||||||
|
{ label: '榜单号', field: 'billNo', component: 'JInput', colProps: { span: 6 } },
|
||||||
|
{ label: '物料名称', field: 'materialName', component: 'JInput', colProps: { span: 6 } },
|
||||||
|
{ label: '供应商名称', field: 'supplierName', component: 'JInput', colProps: { span: 6 } },
|
||||||
|
{
|
||||||
|
label: '检测结果',
|
||||||
|
field: 'testResult',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'xslmes_test_result' },
|
||||||
|
colProps: { span: 6 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '检测状态',
|
||||||
|
field: 'testStatus',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'xslmes_test_status' },
|
||||||
|
colProps: { span: 6 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否特采',
|
||||||
|
field: 'isSpecialAdoption',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'yn' },
|
||||||
|
colProps: { span: 6 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '状态',
|
||||||
|
field: 'status',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'xslmes_entry_status' },
|
||||||
|
colProps: { span: 6 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '入场时间',
|
||||||
|
field: 'entryTime',
|
||||||
|
component: 'RangePicker',
|
||||||
|
componentProps: { showTime: true, valueFormat: 'YYYY-MM-DD HH:mm:ss' },
|
||||||
|
colProps: { span: 8 },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const formSchema: FormSchema[] = [
|
||||||
|
{ label: '', field: 'id', component: 'Input', show: false },
|
||||||
|
{ label: '', field: 'weightRecordId', component: 'Input', show: false },
|
||||||
|
{ label: '', field: 'materialId', component: 'Input', show: false },
|
||||||
|
{ label: '', field: 'supplierId', component: 'Input', show: false },
|
||||||
|
{
|
||||||
|
label: '条码',
|
||||||
|
field: 'barcode',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入条码' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '批次号',
|
||||||
|
field: 'batchNo',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入批次号' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '入场时间',
|
||||||
|
field: 'entryTime',
|
||||||
|
component: 'DatePicker',
|
||||||
|
componentProps: { showTime: true, valueFormat: 'YYYY-MM-DD HH:mm:ss', placeholder: '请选择入场时间' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '榜单号',
|
||||||
|
field: 'billNo',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入榜单号' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '物料名称',
|
||||||
|
field: 'materialName',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入物料名称' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '供料客户',
|
||||||
|
field: 'supplyCustomer',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入供料客户' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '供应商名称',
|
||||||
|
field: 'supplierName',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入供应商名称' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '厂家物料名称',
|
||||||
|
field: 'manufacturerMaterialName',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入厂家物料名称' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '保质期',
|
||||||
|
field: 'shelfLife',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入保质期' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '总重(KG)',
|
||||||
|
field: 'totalWeight',
|
||||||
|
component: 'InputNumber',
|
||||||
|
componentProps: { min: 0, precision: 2, placeholder: '请输入总重', style: { width: '100%' } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '总份数',
|
||||||
|
field: 'totalPortions',
|
||||||
|
component: 'InputNumber',
|
||||||
|
componentProps: { min: 0, placeholder: '请输入总份数', style: { width: '100%' } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '每份总重(KG)',
|
||||||
|
field: 'portionWeight',
|
||||||
|
component: 'InputNumber',
|
||||||
|
componentProps: { min: 0, precision: 2, placeholder: '请输入每份总重', style: { width: '100%' } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '每份包数',
|
||||||
|
field: 'portionPackages',
|
||||||
|
component: 'InputNumber',
|
||||||
|
componentProps: { min: 0, placeholder: '请输入每份包数', style: { width: '100%' } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '检测结果',
|
||||||
|
field: 'testResult',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'xslmes_test_result', placeholder: '请选择检测结果' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '检测状态',
|
||||||
|
field: 'testStatus',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'xslmes_test_status', placeholder: '请选择检测状态' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '打印标记',
|
||||||
|
field: 'printFlag',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'xslmes_print_flag', placeholder: '请选择' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '入库结存',
|
||||||
|
field: 'stockBalance',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'yn', placeholder: '请选择' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '库位',
|
||||||
|
field: 'warehouseLocation',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入库位' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '卸货人',
|
||||||
|
field: 'unloadOperator',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入卸货人' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否特采',
|
||||||
|
field: 'isSpecialAdoption',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'yn', placeholder: '请选择' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '特采操作人',
|
||||||
|
field: 'specialAdoptionOperator',
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: { placeholder: '请输入特采操作人' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '特采时间',
|
||||||
|
field: 'specialAdoptionTime',
|
||||||
|
component: 'DatePicker',
|
||||||
|
componentProps: { showTime: true, valueFormat: 'YYYY-MM-DD HH:mm:ss', placeholder: '请选择特采时间' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '特采原因',
|
||||||
|
field: 'specialAdoptionReason',
|
||||||
|
component: 'InputTextArea',
|
||||||
|
componentProps: { rows: 3, placeholder: '请输入特采原因' },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '状态',
|
||||||
|
field: 'status',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'xslmes_entry_status', placeholder: '请选择状态' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '备注',
|
||||||
|
field: 'remark',
|
||||||
|
component: 'InputTextArea',
|
||||||
|
componentProps: { rows: 3, placeholder: '请输入备注' },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const superQuerySchema = {
|
||||||
|
barcode: { title: '条码', order: 0, view: 'text' },
|
||||||
|
batchNo: { title: '批次号', order: 1, view: 'text' },
|
||||||
|
entryTime: { title: '入场时间', order: 2, view: 'datetime' },
|
||||||
|
billNo: { title: '榜单号', order: 3, view: 'text' },
|
||||||
|
materialName: { title: '物料名称', order: 4, view: 'text' },
|
||||||
|
supplierName: { title: '供应商名称', order: 5, view: 'text' },
|
||||||
|
totalWeight: { title: '总重(KG)', order: 6, view: 'number' },
|
||||||
|
testStatus: { title: '检测状态', order: 7, view: 'list', dictCode: 'xslmes_test_status' },
|
||||||
|
isSpecialAdoption: { title: '是否特采', order: 8, view: 'list', dictCode: 'yn' },
|
||||||
|
status: { title: '状态', order: 9, view: 'list', dictCode: 'xslmes_entry_status' },
|
||||||
|
};
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||||
|
<template #tableTitle>
|
||||||
|
<a-button type="primary" v-auth="'xslmes:mes_xsl_raw_material_entry:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
|
||||||
|
<a-button type="primary" v-auth="'xslmes:mes_xsl_raw_material_entry:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
||||||
|
<j-upload-button type="primary" v-auth="'xslmes:mes_xsl_raw_material_entry: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_raw_material_entry:deleteBatch'">批量操作
|
||||||
|
<Icon icon="mdi:chevron-down" />
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
|
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
|
||||||
|
</template>
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
<MesXslRawMaterialEntryModal @register="registerModal" @success="handleSuccess" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="xslmes-mesXslRawMaterialEntry" setup>
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||||
|
import { useModal } from '/@/components/Modal';
|
||||||
|
import { useListPage } from '/@/hooks/system/useListPage';
|
||||||
|
import MesXslRawMaterialEntryModal from './components/MesXslRawMaterialEntryModal.vue';
|
||||||
|
import { columns, searchFormSchema, superQuerySchema } from './MesXslRawMaterialEntry.data';
|
||||||
|
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './MesXslRawMaterialEntry.api';
|
||||||
|
|
||||||
|
const queryParam = reactive<any>({});
|
||||||
|
const [registerModal, { openModal }] = useModal();
|
||||||
|
|
||||||
|
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||||
|
tableProps: {
|
||||||
|
title: '原料入场记录',
|
||||||
|
api: list,
|
||||||
|
columns,
|
||||||
|
canResize: true,
|
||||||
|
formConfig: {
|
||||||
|
schemas: searchFormSchema,
|
||||||
|
autoSubmitOnEnter: true,
|
||||||
|
showAdvancedButton: true,
|
||||||
|
fieldMapToTime: [
|
||||||
|
['entryTime', ['entryTime_begin', 'entryTime_end'], 'YYYY-MM-DD HH:mm:ss'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
actionColumn: {
|
||||||
|
width: 120,
|
||||||
|
fixed: 'right',
|
||||||
|
},
|
||||||
|
beforeFetch: (params) => {
|
||||||
|
return 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).map((k) => {
|
||||||
|
queryParam[k] = params[k];
|
||||||
|
});
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
openModal(true, { isUpdate: false, showFooter: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(record: Recordable) {
|
||||||
|
openModal(true, { record, isUpdate: true, showFooter: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDetail(record: Recordable) {
|
||||||
|
openModal(true, { record, isUpdate: true, showFooter: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDelete(record) {
|
||||||
|
await deleteOne({ id: record.id }, handleSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function batchHandleDelete() {
|
||||||
|
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSuccess() {
|
||||||
|
(selectedRowKeys.value = []) && reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTableAction(record) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
auth: 'xslmes:mes_xsl_raw_material_entry:edit',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDropDownAction(record) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '详情',
|
||||||
|
onClick: handleDetail.bind(null, record),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
popConfirm: {
|
||||||
|
title: '是否确认删除',
|
||||||
|
confirm: handleDelete.bind(null, record),
|
||||||
|
placement: 'topLeft',
|
||||||
|
},
|
||||||
|
auth: 'xslmes:mes_xsl_raw_material_entry:delete',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.ant-picker-range) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<template>
|
||||||
|
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="1100" @ok="handleSubmit">
|
||||||
|
<BasicForm @register="registerForm" name="MesXslRawMaterialEntryForm" />
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, unref } from 'vue';
|
||||||
|
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||||
|
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||||
|
import { formSchema } from '../MesXslRawMaterialEntry.data';
|
||||||
|
import { saveOrUpdate } from '../MesXslRawMaterialEntry.api';
|
||||||
|
|
||||||
|
const emit = defineEmits(['register', 'success']);
|
||||||
|
const isUpdate = ref(true);
|
||||||
|
const isDetail = ref(false);
|
||||||
|
|
||||||
|
const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField }] = useForm({
|
||||||
|
labelWidth: 120,
|
||||||
|
schemas: formSchema,
|
||||||
|
showActionButtonGroup: false,
|
||||||
|
baseColProps: { span: 12 },
|
||||||
|
});
|
||||||
|
|
||||||
|
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 (unref(isUpdate)) {
|
||||||
|
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, unref(isUpdate));
|
||||||
|
closeModal();
|
||||||
|
emit('success');
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e?.errorFields) {
|
||||||
|
const firstField = e.errorFields[0];
|
||||||
|
if (firstField) {
|
||||||
|
scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.reject(e);
|
||||||
|
} finally {
|
||||||
|
setModalProps({ confirmLoading: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.ant-input-number) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
:deep(.ant-calendar-picker) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,7 +13,8 @@ export const columns: BasicColumn[] = [
|
|||||||
{ title: '车号', align: 'center', dataIndex: 'plateNumber', width: 120 },
|
{ title: '车号', align: 'center', dataIndex: 'plateNumber', width: 120 },
|
||||||
{ title: '发货单位', align: 'center', dataIndex: 'senderUnit', width: 160, ellipsis: true },
|
{ title: '发货单位', align: 'center', dataIndex: 'senderUnit', width: 160, ellipsis: true },
|
||||||
{ title: '收货单位', align: 'center', dataIndex: 'receiverUnit', width: 160, ellipsis: true },
|
{ title: '收货单位', align: 'center', dataIndex: 'receiverUnit', width: 160, ellipsis: true },
|
||||||
{ title: '货物名称', align: 'center', dataIndex: 'goodsName', width: 140, ellipsis: true },
|
{ title: '密炼物料', align: 'center', dataIndex: 'mixerMaterialNames', width: 160, ellipsis: true },
|
||||||
|
{ title: '类型', align: 'center', dataIndex: 'materialType_dictText', width: 90 },
|
||||||
{ title: '毛重(KG)', align: 'center', dataIndex: 'grossWeight', width: 100 },
|
{ title: '毛重(KG)', align: 'center', dataIndex: 'grossWeight', width: 100 },
|
||||||
{ title: '皮重(KG)', align: 'center', dataIndex: 'tareWeight', width: 100 },
|
{ title: '皮重(KG)', align: 'center', dataIndex: 'tareWeight', width: 100 },
|
||||||
{ title: '净重(KG)', align: 'center', dataIndex: 'netWeight', width: 100 },
|
{ title: '净重(KG)', align: 'center', dataIndex: 'netWeight', width: 100 },
|
||||||
@@ -77,10 +78,17 @@ export const formSchema: FormSchema[] = [
|
|||||||
componentProps: { placeholder: '出厂时自动带出客户简称' },
|
componentProps: { placeholder: '出厂时自动带出客户简称' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '货物名称',
|
label: '密炼物料',
|
||||||
field: 'goodsName',
|
field: 'mixerMaterialNames',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
componentProps: { placeholder: '请输入货物名称' },
|
componentProps: { placeholder: '由桌面端称重操作填入,可手动编辑' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '类型',
|
||||||
|
field: 'materialType',
|
||||||
|
component: 'JDictSelectTag',
|
||||||
|
componentProps: { dictCode: 'xslmes_weight_material_type' },
|
||||||
|
defaultValue: '1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '毛重(KG)',
|
label: '毛重(KG)',
|
||||||
@@ -119,6 +127,7 @@ export const superQuerySchema = {
|
|||||||
inoutDirection: { title: '进出方向', order: 1, view: 'list', dictCode: 'xslmes_inout_direction' },
|
inoutDirection: { title: '进出方向', order: 1, view: 'list', dictCode: 'xslmes_inout_direction' },
|
||||||
plateNumber: { title: '车号', order: 2, view: 'text' },
|
plateNumber: { title: '车号', order: 2, view: 'text' },
|
||||||
weighDate: { title: '称重日期', order: 3, view: 'date' },
|
weighDate: { title: '称重日期', order: 3, view: 'date' },
|
||||||
grossWeight: { title: '毛重(KG)', order: 4, view: 'number' },
|
materialType: { title: '类型', order: 4, view: 'list', dictCode: 'xslmes_weight_material_type' },
|
||||||
netWeight: { title: '净重(KG)', order: 5, view: 'number' },
|
grossWeight: { title: '毛重(KG)', order: 5, view: 'number' },
|
||||||
|
netWeight: { title: '净重(KG)', order: 6, view: 'number' },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,6 +46,8 @@
|
|||||||
api: list,
|
api: list,
|
||||||
columns,
|
columns,
|
||||||
canResize: true,
|
canResize: true,
|
||||||
|
// 更新列配置缓存 key,避免用户历史列顺序覆盖默认顺序
|
||||||
|
tableSetting: { cacheKey: 'mesXslWeightRecordColumns_v20260507' },
|
||||||
formConfig: {
|
formConfig: {
|
||||||
schemas: searchFormSchema,
|
schemas: searchFormSchema,
|
||||||
autoSubmitOnEnter: true,
|
autoSubmitOnEnter: true,
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Prism.Events;
|
||||||
|
|
||||||
|
namespace YY.Admin.Core.Events;
|
||||||
|
|
||||||
|
public class CategoryChangedPayload
|
||||||
|
{
|
||||||
|
public string Action { get; set; } = string.Empty;
|
||||||
|
public string? CategoryId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CategoryChangedEvent : PubSubEvent<CategoryChangedPayload> { }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Prism.Events;
|
||||||
|
|
||||||
|
namespace YY.Admin.Core.Events;
|
||||||
|
|
||||||
|
public class DictChangedPayload
|
||||||
|
{
|
||||||
|
public string Action { get; set; } = string.Empty;
|
||||||
|
public string? DictId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DictChangedEvent : PubSubEvent<DictChangedPayload> { }
|
||||||
@@ -4,6 +4,8 @@ namespace YY.Admin.Core.Services;
|
|||||||
|
|
||||||
public record MixerMaterialPageResult(List<MesMixerMaterial> Records, long Total, int Current, int Size);
|
public record MixerMaterialPageResult(List<MesMixerMaterial> Records, long Total, int Current, int Size);
|
||||||
|
|
||||||
|
public record MaterialCategoryNode(string Id, string? Name, string? Code, List<MaterialCategoryNode> Children);
|
||||||
|
|
||||||
public interface IMixerMaterialService
|
public interface IMixerMaterialService
|
||||||
{
|
{
|
||||||
Task<MixerMaterialPageResult> PageAsync(
|
Task<MixerMaterialPageResult> PageAsync(
|
||||||
@@ -21,7 +23,10 @@ public interface IMixerMaterialService
|
|||||||
Task<bool> EditAsync(MesMixerMaterial material, CancellationToken ct = default);
|
Task<bool> EditAsync(MesMixerMaterial material, CancellationToken ct = default);
|
||||||
Task<bool> DeleteAsync(string id, CancellationToken ct = default);
|
Task<bool> DeleteAsync(string id, CancellationToken ct = default);
|
||||||
Task<bool> DeleteBatchAsync(string ids, CancellationToken ct = default);
|
Task<bool> DeleteBatchAsync(string ids, CancellationToken ct = default);
|
||||||
|
|
||||||
Task<List<KeyValuePair<string, string>>> GetMajorCategoryOptionsAsync(CancellationToken ct = default);
|
Task<List<KeyValuePair<string, string>>> GetMajorCategoryOptionsAsync(CancellationToken ct = default);
|
||||||
Task<List<KeyValuePair<string, string>>> GetMinorCategoryOptionsAsync(string majorCategoryId, CancellationToken ct = default);
|
Task<List<KeyValuePair<string, string>>> GetMinorCategoryOptionsAsync(string majorCategoryId, CancellationToken ct = default);
|
||||||
}
|
|
||||||
|
|
||||||
|
Task<List<MaterialCategoryNode>> GetMaterialCategoryTreeAsync(CancellationToken ct = default);
|
||||||
|
Task SyncFromRemoteAsync(CancellationToken ct = default);
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ public interface IWeightRecordService
|
|||||||
string? filterBillNo = null,
|
string? filterBillNo = null,
|
||||||
string? filterPlateNumber = null,
|
string? filterPlateNumber = null,
|
||||||
string? filterInoutDirection = null,
|
string? filterInoutDirection = null,
|
||||||
string? filterGoodsName = null,
|
|
||||||
string? filterDriverName = null,
|
string? filterDriverName = null,
|
||||||
|
string? filterMixerMaterialName = null,
|
||||||
CancellationToken ct = default);
|
CancellationToken ct = default);
|
||||||
|
|
||||||
Task<MesXslWeightRecord?> GetByIdAsync(string id, CancellationToken ct = default);
|
Task<MesXslWeightRecord?> GetByIdAsync(string id, CancellationToken ct = default);
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ public class MesXslWeightRecord
|
|||||||
/// <summary>收货单位(出厂时为客户简称)</summary>
|
/// <summary>收货单位(出厂时为客户简称)</summary>
|
||||||
public string? ReceiverUnit { get; set; }
|
public string? ReceiverUnit { get; set; }
|
||||||
|
|
||||||
/// <summary>货物名称</summary>
|
|
||||||
public string? GoodsName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>毛重(KG),实际称量</summary>
|
/// <summary>毛重(KG),实际称量</summary>
|
||||||
public double? GrossWeight { get; set; }
|
public double? GrossWeight { get; set; }
|
||||||
|
|
||||||
@@ -46,6 +43,15 @@ public class MesXslWeightRecord
|
|||||||
/// <summary>单据类型:1已称毛重 2称重完成</summary>
|
/// <summary>单据类型:1已称毛重 2称重完成</summary>
|
||||||
public string? BillType { get; set; }
|
public string? BillType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>密炼物料ID(分号分隔)</summary>
|
||||||
|
public string? MixerMaterialIds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>密炼物料名称(分号分隔)</summary>
|
||||||
|
public string? MixerMaterialNames { get; set; }
|
||||||
|
|
||||||
|
/// <summary>物料类型:1自动 2手动</summary>
|
||||||
|
public string? MaterialType { get; set; }
|
||||||
|
|
||||||
public int? TenantId { get; set; }
|
public int? TenantId { get; set; }
|
||||||
public string? CreateBy { get; set; }
|
public string? CreateBy { get; set; }
|
||||||
public DateTime? CreateTime { get; set; }
|
public DateTime? CreateTime { get; set; }
|
||||||
@@ -58,4 +64,7 @@ public class MesXslWeightRecord
|
|||||||
|
|
||||||
/// <summary>单据类型显示文本(由 ViewModel 填充)</summary>
|
/// <summary>单据类型显示文本(由 ViewModel 填充)</summary>
|
||||||
public string BillTypeText { get; set; } = string.Empty;
|
public string BillTypeText { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>物料类型显示文本(由 ViewModel 填充)</summary>
|
||||||
|
public string MaterialTypeText { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using Prism.Events;
|
||||||
|
using System.Text.Json;
|
||||||
|
using YY.Admin.Core;
|
||||||
|
using YY.Admin.Core.Events;
|
||||||
|
using YY.Admin.Core.Services;
|
||||||
|
|
||||||
|
namespace YY.Admin.Services.Service.Category;
|
||||||
|
|
||||||
|
public class CategorySyncCoordinator : ISingletonDependency
|
||||||
|
{
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
private readonly IJeecgCategorySyncService _categorySyncService;
|
||||||
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
|
public CategorySyncCoordinator(
|
||||||
|
IEventAggregator eventAggregator,
|
||||||
|
IJeecgCategorySyncService categorySyncService,
|
||||||
|
ILoggerService logger)
|
||||||
|
{
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
_categorySyncService = categorySyncService;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
_eventAggregator.GetEvent<RemoteCommandReceivedEvent>()
|
||||||
|
.Subscribe(OnRemoteCommand, ThreadOption.BackgroundThread);
|
||||||
|
_eventAggregator.GetEvent<NetworkStatusChangedEvent>()
|
||||||
|
.Subscribe(OnNetworkStatusChanged, ThreadOption.BackgroundThread);
|
||||||
|
|
||||||
|
_logger.Information("[分类字典] CategorySyncCoordinator 已启动");
|
||||||
|
_ = Task.Run(() => SyncAndPublishAsync("startup", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNetworkStatusChanged(NetworkStatusChangedPayload payload)
|
||||||
|
{
|
||||||
|
if (!payload.IsOnline) return;
|
||||||
|
_logger.Information("[分类字典] 网络恢复,触发补偿刷新");
|
||||||
|
_ = Task.Run(() => SyncAndPublishAsync("reconnect", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemoteCommand(RemoteCommandPayload payload)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = payload.CommandJson ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(json)) return;
|
||||||
|
using var doc = JsonDocument.Parse(json);
|
||||||
|
if (!doc.RootElement.TryGetProperty("cmd", out var cmdEl)) return;
|
||||||
|
if (!cmdEl.GetString().Equals("SYS_CATEGORY_CHANGED", StringComparison.OrdinalIgnoreCase)) return;
|
||||||
|
|
||||||
|
doc.RootElement.TryGetProperty("action", out var actionEl);
|
||||||
|
doc.RootElement.TryGetProperty("categoryId", out var idEl);
|
||||||
|
var action = actionEl.GetString() ?? string.Empty;
|
||||||
|
var categoryId = idEl.ValueKind == JsonValueKind.String ? idEl.GetString() : null;
|
||||||
|
|
||||||
|
_logger.Information($"[分类字典] 收到变更信号 action={action}");
|
||||||
|
_ = Task.Run(() => SyncAndPublishAsync(action, categoryId));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[分类字典] 处理STOMP命令失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SyncAndPublishAsync(string action, string? categoryId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var count = await _categorySyncService.SyncFromJeecgAsync().ConfigureAwait(false);
|
||||||
|
if (count > 0)
|
||||||
|
_logger.Information($"[分类字典] 同步完成,共处理 {count} 条");
|
||||||
|
|
||||||
|
_eventAggregator.GetEvent<CategoryChangedEvent>()
|
||||||
|
.Publish(new CategoryChangedPayload { Action = action, CategoryId = categoryId });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[分类字典] 同步失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using Prism.Events;
|
||||||
|
using System.Text.Json;
|
||||||
|
using YY.Admin.Core;
|
||||||
|
using YY.Admin.Core.Events;
|
||||||
|
using YY.Admin.Core.Services;
|
||||||
|
|
||||||
|
namespace YY.Admin.Services.Service.Dict;
|
||||||
|
|
||||||
|
public class DictSyncCoordinator : ISingletonDependency
|
||||||
|
{
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
private readonly IJeecgDictSyncService _dictSyncService;
|
||||||
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
|
public DictSyncCoordinator(
|
||||||
|
IEventAggregator eventAggregator,
|
||||||
|
IJeecgDictSyncService dictSyncService,
|
||||||
|
ILoggerService logger)
|
||||||
|
{
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
_dictSyncService = dictSyncService;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
_eventAggregator.GetEvent<RemoteCommandReceivedEvent>()
|
||||||
|
.Subscribe(OnRemoteCommand, ThreadOption.BackgroundThread);
|
||||||
|
_eventAggregator.GetEvent<NetworkStatusChangedEvent>()
|
||||||
|
.Subscribe(OnNetworkStatusChanged, ThreadOption.BackgroundThread);
|
||||||
|
|
||||||
|
_logger.Information("[数据字典] DictSyncCoordinator 已启动");
|
||||||
|
_ = Task.Run(() => SyncAndPublishAsync("startup", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNetworkStatusChanged(NetworkStatusChangedPayload payload)
|
||||||
|
{
|
||||||
|
if (!payload.IsOnline) return;
|
||||||
|
_logger.Information("[数据字典] 网络恢复,触发补偿刷新");
|
||||||
|
_ = Task.Run(() => SyncAndPublishAsync("reconnect", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemoteCommand(RemoteCommandPayload payload)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = payload.CommandJson ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(json)) return;
|
||||||
|
using var doc = JsonDocument.Parse(json);
|
||||||
|
if (!doc.RootElement.TryGetProperty("cmd", out var cmdEl)) return;
|
||||||
|
if (!cmdEl.GetString().Equals("SYS_DICT_CHANGED", StringComparison.OrdinalIgnoreCase)) return;
|
||||||
|
|
||||||
|
doc.RootElement.TryGetProperty("action", out var actionEl);
|
||||||
|
doc.RootElement.TryGetProperty("dictId", out var idEl);
|
||||||
|
var action = actionEl.GetString() ?? string.Empty;
|
||||||
|
var dictId = idEl.ValueKind == JsonValueKind.String ? idEl.GetString() : null;
|
||||||
|
|
||||||
|
_logger.Information($"[数据字典] 收到变更信号 action={action}");
|
||||||
|
_ = Task.Run(() => SyncAndPublishAsync(action, dictId));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[数据字典] 处理STOMP命令失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SyncAndPublishAsync(string action, string? dictId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var count = await _dictSyncService.SyncFromJeecgAsync().ConfigureAwait(false);
|
||||||
|
if (count > 0)
|
||||||
|
_logger.Information($"[数据字典] 同步完成,共处理 {count} 条");
|
||||||
|
|
||||||
|
_eventAggregator.GetEvent<DictChangedEvent>()
|
||||||
|
.Publish(new DictChangedPayload { Action = action, DictId = dictId });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[数据字典] 同步失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,4 +13,9 @@ public interface IJeecgDictSyncService
|
|||||||
/// 从桌面端本地字典镜像表读取指定字典编码的选项。
|
/// 从桌面端本地字典镜像表读取指定字典编码的选项。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<List<KeyValuePair<string, string>>> GetDictOptionsAsync(string dictCode, bool includeAll = false);
|
Task<List<KeyValuePair<string, string>>> GetDictOptionsAsync(string dictCode, bool includeAll = false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有字典分组(字典编码 + 字典名称),用于左侧树形导航。
|
||||||
|
/// </summary>
|
||||||
|
Task<List<KeyValuePair<string, string>>> GetDictGroupsAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class JeecgDictSyncService : IJeecgDictSyncService, ISingletonDependency
|
|||||||
{
|
{
|
||||||
var statusFilter = input.Status.HasValue ? (int?)input.Status.Value : null;
|
var statusFilter = input.Status.HasValue ? (int?)input.Status.Value : null;
|
||||||
var query = _dbContext.Queryable<JeecgSysDictItem>().ClearFilter()
|
var query = _dbContext.Queryable<JeecgSysDictItem>().ClearFilter()
|
||||||
.WhereIF(!string.IsNullOrWhiteSpace(input.DictCode), x => x.DictCode != null && x.DictCode.Contains(input.DictCode))
|
.WhereIF(!string.IsNullOrWhiteSpace(input.DictCode), x => x.DictCode != null && x.DictCode == input.DictCode)
|
||||||
.WhereIF(!string.IsNullOrWhiteSpace(input.DictName), x => x.DictName != null && x.DictName.Contains(input.DictName))
|
.WhereIF(!string.IsNullOrWhiteSpace(input.DictName), x => x.DictName != null && x.DictName.Contains(input.DictName))
|
||||||
.WhereIF(!string.IsNullOrWhiteSpace(input.ItemText), x => x.ItemText != null && x.ItemText.Contains(input.ItemText))
|
.WhereIF(!string.IsNullOrWhiteSpace(input.ItemText), x => x.ItemText != null && x.ItemText.Contains(input.ItemText))
|
||||||
.WhereIF(!string.IsNullOrWhiteSpace(input.ItemValue), x => x.ItemValue != null && x.ItemValue.Contains(input.ItemValue));
|
.WhereIF(!string.IsNullOrWhiteSpace(input.ItemValue), x => x.ItemValue != null && x.ItemValue.Contains(input.ItemValue));
|
||||||
@@ -164,6 +164,22 @@ public class JeecgDictSyncService : IJeecgDictSyncService, ISingletonDependency
|
|||||||
return synced;
|
return synced;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<KeyValuePair<string, string>>> GetDictGroupsAsync()
|
||||||
|
{
|
||||||
|
var rows = await _dbContext.Queryable<JeecgSysDictItem>()
|
||||||
|
.ClearFilter()
|
||||||
|
.Where(x => x.DictCode != null)
|
||||||
|
.OrderBy(x => x.DictCode)
|
||||||
|
.Select(x => new JeecgSysDictItem { DictCode = x.DictCode, DictName = x.DictName })
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return rows
|
||||||
|
.Where(x => !string.IsNullOrWhiteSpace(x.DictCode))
|
||||||
|
.DistinctBy(x => x.DictCode)
|
||||||
|
.Select(x => new KeyValuePair<string, string>(x.DictCode!, x.DictName ?? x.DictCode!))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<KeyValuePair<string, string>>> GetDictOptionsAsync(string dictCode, bool includeAll = false)
|
public async Task<List<KeyValuePair<string, string>>> GetDictOptionsAsync(string dictCode, bool includeAll = false)
|
||||||
{
|
{
|
||||||
var result = new List<KeyValuePair<string, string>>();
|
var result = new List<KeyValuePair<string, string>>();
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using SqlSugar;
|
||||||
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@@ -14,7 +16,12 @@ public class MixerMaterialService : IMixerMaterialService, ISingletonDependency
|
|||||||
{
|
{
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly ISqlSugarClient _dbContext;
|
||||||
private readonly ILoggerService _logger;
|
private readonly ILoggerService _logger;
|
||||||
|
private readonly SemaphoreSlim _syncLock = new(1, 1);
|
||||||
|
private readonly object _cacheLock = new();
|
||||||
|
private readonly string _cacheFilePath;
|
||||||
|
private List<MesMixerMaterial> _localCache = [];
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions _jsonOpts = new()
|
private static readonly JsonSerializerOptions _jsonOpts = new()
|
||||||
{
|
{
|
||||||
@@ -26,36 +33,158 @@ public class MixerMaterialService : IMixerMaterialService, ISingletonDependency
|
|||||||
public MixerMaterialService(
|
public MixerMaterialService(
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
|
ISqlSugarClient dbContext,
|
||||||
ILoggerService logger)
|
ILoggerService logger)
|
||||||
{
|
{
|
||||||
_httpClientFactory = httpClientFactory;
|
_httpClientFactory = httpClientFactory;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
_dbContext = dbContext;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
|
var appDataDir = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
|
"YY.Admin", "sync-cache");
|
||||||
|
Directory.CreateDirectory(appDataDir);
|
||||||
|
_cacheFilePath = Path.Combine(appDataDir, "mixer-material-cache.json");
|
||||||
|
|
||||||
|
_ = LoadCacheAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string BaseUrl => (_configuration.GetValue<string>("JeecgIntegration:BaseUrl") ?? "http://localhost:8080/jeecg-boot").TrimEnd('/');
|
private string BaseUrl => (_configuration.GetValue<string>("JeecgIntegration:BaseUrl") ?? "http://localhost:8080/jeecg-boot").TrimEnd('/');
|
||||||
private HttpClient CreateClient() => _httpClientFactory.CreateClient("JeecgApi");
|
private HttpClient CreateClient() => _httpClientFactory.CreateClient("JeecgApi");
|
||||||
|
|
||||||
public async Task<MixerMaterialPageResult> PageAsync(
|
private async Task LoadCacheAsync()
|
||||||
int pageNo,
|
{
|
||||||
int pageSize,
|
try
|
||||||
string? materialCode = null,
|
{
|
||||||
string? materialName = null,
|
if (!File.Exists(_cacheFilePath)) return;
|
||||||
string? erpCode = null,
|
var json = await File.ReadAllTextAsync(_cacheFilePath).ConfigureAwait(false);
|
||||||
string? majorCategoryId = null,
|
var list = JsonSerializer.Deserialize<List<MesMixerMaterial>>(json, _jsonOpts);
|
||||||
string? minorCategoryId = null,
|
if (list != null)
|
||||||
|
lock (_cacheLock) { _localCache = list; }
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼物料] 加载本地缓存失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveCacheAsync(List<MesMixerMaterial> list)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(list, _jsonOpts);
|
||||||
|
await File.WriteAllTextAsync(_cacheFilePath, json).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼物料] 保存本地缓存失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SyncFromRemoteAsync(CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
if (!await _syncLock.WaitAsync(0, ct).ConfigureAwait(false)) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var all = new List<MesMixerMaterial>();
|
||||||
|
var pageNo = 1;
|
||||||
|
const int pageSize = 500;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var qs = HttpUtility.ParseQueryString(string.Empty);
|
||||||
|
qs["pageNo"] = pageNo.ToString();
|
||||||
|
qs["pageSize"] = pageSize.ToString();
|
||||||
|
var url = $"{BaseUrl}/mes/material/mixerMaterial/anon/list?{qs}";
|
||||||
|
using var client = CreateClient();
|
||||||
|
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
|
||||||
|
if (!resp.IsSuccessStatusCode) break;
|
||||||
|
|
||||||
|
var json = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
||||||
|
using var doc = JsonDocument.Parse(json);
|
||||||
|
if (!doc.RootElement.TryGetProperty("result", out var resultEl)) break;
|
||||||
|
var records = resultEl.TryGetProperty("records", out var recEl)
|
||||||
|
? recEl.Deserialize<List<MesMixerMaterial>>(_jsonOpts) ?? []
|
||||||
|
: [];
|
||||||
|
|
||||||
|
all.AddRange(records);
|
||||||
|
if (records.Count < pageSize) break;
|
||||||
|
pageNo++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all.Count > 0)
|
||||||
|
{
|
||||||
|
lock (_cacheLock) { _localCache = all; }
|
||||||
|
await SaveCacheAsync(all).ConfigureAwait(false);
|
||||||
|
_logger.Information($"[密炼物料] 同步完成,共 {all.Count} 条");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼物料] 远程同步失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_syncLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<MixerMaterialPageResult> PageAsync(
|
||||||
|
int pageNo, int pageSize,
|
||||||
|
string? materialCode = null, string? materialName = null, string? erpCode = null,
|
||||||
|
string? majorCategoryId = null, string? minorCategoryId = null,
|
||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var query = HttpUtility.ParseQueryString(string.Empty);
|
List<MesMixerMaterial> cache;
|
||||||
query["pageNo"] = pageNo.ToString();
|
lock (_cacheLock) { cache = _localCache; }
|
||||||
query["pageSize"] = pageSize.ToString();
|
|
||||||
if (!string.IsNullOrWhiteSpace(materialCode)) query["materialCode"] = materialCode;
|
|
||||||
if (!string.IsNullOrWhiteSpace(materialName)) query["materialName"] = materialName;
|
|
||||||
if (!string.IsNullOrWhiteSpace(erpCode)) query["erpCode"] = erpCode;
|
|
||||||
if (!string.IsNullOrWhiteSpace(majorCategoryId)) query["majorCategoryId"] = majorCategoryId;
|
|
||||||
if (!string.IsNullOrWhiteSpace(minorCategoryId)) query["minorCategoryId"] = minorCategoryId;
|
|
||||||
|
|
||||||
var url = $"{BaseUrl}/mes/material/mixerMaterial/anon/list?{query}";
|
if (cache.Count > 0)
|
||||||
|
return Task.FromResult(PageFromCache(cache, pageNo, pageSize,
|
||||||
|
materialCode, materialName, erpCode, majorCategoryId, minorCategoryId));
|
||||||
|
|
||||||
|
return PageFromRemoteAsync(pageNo, pageSize,
|
||||||
|
materialCode, materialName, erpCode, majorCategoryId, minorCategoryId, ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MixerMaterialPageResult PageFromCache(
|
||||||
|
List<MesMixerMaterial> cache, int pageNo, int pageSize,
|
||||||
|
string? materialCode, string? materialName, string? erpCode,
|
||||||
|
string? majorCategoryId, string? minorCategoryId)
|
||||||
|
{
|
||||||
|
var q = cache.AsEnumerable();
|
||||||
|
if (!string.IsNullOrWhiteSpace(materialCode))
|
||||||
|
q = q.Where(x => x.MaterialCode?.Contains(materialCode, StringComparison.OrdinalIgnoreCase) == true);
|
||||||
|
if (!string.IsNullOrWhiteSpace(materialName))
|
||||||
|
q = q.Where(x => x.MaterialName?.Contains(materialName, StringComparison.OrdinalIgnoreCase) == true);
|
||||||
|
if (!string.IsNullOrWhiteSpace(erpCode))
|
||||||
|
q = q.Where(x => x.ErpCode?.Contains(erpCode, StringComparison.OrdinalIgnoreCase) == true);
|
||||||
|
if (!string.IsNullOrWhiteSpace(majorCategoryId))
|
||||||
|
q = q.Where(x => x.MajorCategoryId == majorCategoryId);
|
||||||
|
if (!string.IsNullOrWhiteSpace(minorCategoryId))
|
||||||
|
q = q.Where(x => x.MinorCategoryId == minorCategoryId);
|
||||||
|
|
||||||
|
var filtered = q.ToList();
|
||||||
|
var records = filtered.Skip((pageNo - 1) * pageSize).Take(pageSize).ToList();
|
||||||
|
return new MixerMaterialPageResult(records, filtered.Count, pageNo, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<MixerMaterialPageResult> PageFromRemoteAsync(
|
||||||
|
int pageNo, int pageSize,
|
||||||
|
string? materialCode, string? materialName, string? erpCode,
|
||||||
|
string? majorCategoryId, string? minorCategoryId,
|
||||||
|
CancellationToken ct)
|
||||||
|
{
|
||||||
|
var qs = HttpUtility.ParseQueryString(string.Empty);
|
||||||
|
qs["pageNo"] = pageNo.ToString();
|
||||||
|
qs["pageSize"] = pageSize.ToString();
|
||||||
|
if (!string.IsNullOrWhiteSpace(materialCode)) qs["materialCode"] = materialCode;
|
||||||
|
if (!string.IsNullOrWhiteSpace(materialName)) qs["materialName"] = materialName;
|
||||||
|
if (!string.IsNullOrWhiteSpace(erpCode)) qs["erpCode"] = erpCode;
|
||||||
|
if (!string.IsNullOrWhiteSpace(majorCategoryId)) qs["majorCategoryId"] = majorCategoryId;
|
||||||
|
if (!string.IsNullOrWhiteSpace(minorCategoryId)) qs["minorCategoryId"] = minorCategoryId;
|
||||||
|
|
||||||
|
var url = $"{BaseUrl}/mes/material/mixerMaterial/anon/list?{qs}";
|
||||||
using var client = CreateClient();
|
using var client = CreateClient();
|
||||||
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
|
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
|
||||||
resp.EnsureSuccessStatusCode();
|
resp.EnsureSuccessStatusCode();
|
||||||
@@ -63,13 +192,18 @@ public class MixerMaterialService : IMixerMaterialService, ISingletonDependency
|
|||||||
var json = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
var json = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
||||||
using var doc = JsonDocument.Parse(json);
|
using var doc = JsonDocument.Parse(json);
|
||||||
var result = doc.RootElement.GetProperty("result");
|
var result = doc.RootElement.GetProperty("result");
|
||||||
var records = result.GetProperty("records").Deserialize<List<MesMixerMaterial>>(_jsonOpts) ?? new();
|
var records = result.GetProperty("records").Deserialize<List<MesMixerMaterial>>(_jsonOpts) ?? [];
|
||||||
var total = result.TryGetProperty("total", out var totalEl) ? totalEl.GetInt64() : records.Count;
|
var total = result.TryGetProperty("total", out var totalEl) ? totalEl.GetInt64() : records.Count;
|
||||||
return new MixerMaterialPageResult(records, total, pageNo, pageSize);
|
return new MixerMaterialPageResult(records, total, pageNo, pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MesMixerMaterial?> GetByIdAsync(string id, CancellationToken ct = default)
|
public async Task<MesMixerMaterial?> GetByIdAsync(string id, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
|
List<MesMixerMaterial> cache;
|
||||||
|
lock (_cacheLock) { cache = _localCache; }
|
||||||
|
var local = cache.FirstOrDefault(x => x.Id == id);
|
||||||
|
if (local != null) return local;
|
||||||
|
|
||||||
var url = $"{BaseUrl}/mes/material/mixerMaterial/anon/queryById?id={Uri.EscapeDataString(id)}";
|
var url = $"{BaseUrl}/mes/material/mixerMaterial/anon/queryById?id={Uri.EscapeDataString(id)}";
|
||||||
using var client = CreateClient();
|
using var client = CreateClient();
|
||||||
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
|
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
|
||||||
@@ -105,43 +239,75 @@ public class MixerMaterialService : IMixerMaterialService, ISingletonDependency
|
|||||||
|
|
||||||
public async Task<List<KeyValuePair<string, string>>> GetMajorCategoryOptionsAsync(CancellationToken ct = default)
|
public async Task<List<KeyValuePair<string, string>>> GetMajorCategoryOptionsAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var url = $"{BaseUrl}/sys/category/anon/loadTreeData?pcode=XSLMES_MATERIAL";
|
try
|
||||||
using var client = CreateClient();
|
{
|
||||||
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
|
var root = await _dbContext.Queryable<JeecgSysCategoryItem>()
|
||||||
if (!resp.IsSuccessStatusCode) return [];
|
.ClearFilter().Where(x => x.Code == "XSLMES_MATERIAL").FirstAsync();
|
||||||
var json = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
if (root == null) return [];
|
||||||
using var doc = JsonDocument.Parse(json);
|
|
||||||
var arr = doc.RootElement.TryGetProperty("result", out var resultEl) ? resultEl : doc.RootElement;
|
var majors = await _dbContext.Queryable<JeecgSysCategoryItem>()
|
||||||
return ParseTreeOptions(arr);
|
.ClearFilter().Where(x => x.Pid == root.Id).OrderBy(x => x.Code).ToListAsync();
|
||||||
|
|
||||||
|
return majors.Where(x => !string.IsNullOrWhiteSpace(x.Id))
|
||||||
|
.Select(x => new KeyValuePair<string, string>(x.Name ?? x.Code ?? x.Id, x.Id))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼物料] 加载大类选项失败:{ex.Message}");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<KeyValuePair<string, string>>> GetMinorCategoryOptionsAsync(string majorCategoryId, CancellationToken ct = default)
|
public async Task<List<KeyValuePair<string, string>>> GetMinorCategoryOptionsAsync(string majorCategoryId, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(majorCategoryId)) return [];
|
if (string.IsNullOrWhiteSpace(majorCategoryId)) return [];
|
||||||
var url = $"{BaseUrl}/sys/category/anon/loadTreeData?pid={Uri.EscapeDataString(majorCategoryId)}";
|
try
|
||||||
using var client = CreateClient();
|
{
|
||||||
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
|
var minors = await _dbContext.Queryable<JeecgSysCategoryItem>()
|
||||||
if (!resp.IsSuccessStatusCode) return [];
|
.ClearFilter().Where(x => x.Pid == majorCategoryId).OrderBy(x => x.Code).ToListAsync();
|
||||||
var json = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
|
||||||
using var doc = JsonDocument.Parse(json);
|
return minors.Where(x => !string.IsNullOrWhiteSpace(x.Id))
|
||||||
var arr = doc.RootElement.TryGetProperty("result", out var resultEl) ? resultEl : doc.RootElement;
|
.Select(x => new KeyValuePair<string, string>(x.Name ?? x.Code ?? x.Id, x.Id))
|
||||||
return ParseTreeOptions(arr);
|
.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼物料] 加载小类选项失败:{ex.Message}");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<KeyValuePair<string, string>> ParseTreeOptions(JsonElement root)
|
public async Task<List<MaterialCategoryNode>> GetMaterialCategoryTreeAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var list = new List<KeyValuePair<string, string>>();
|
try
|
||||||
if (root.ValueKind != JsonValueKind.Array) return list;
|
|
||||||
foreach (var item in root.EnumerateArray())
|
|
||||||
{
|
{
|
||||||
var text = item.TryGetProperty("title", out var titleEl) ? titleEl.GetString() : null;
|
var root = await _dbContext.Queryable<JeecgSysCategoryItem>()
|
||||||
var key = item.TryGetProperty("key", out var keyEl) ? keyEl.GetString() : null;
|
.ClearFilter().Where(x => x.Code == "XSLMES_MATERIAL").FirstAsync();
|
||||||
if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(key))
|
if (root == null) return [];
|
||||||
|
|
||||||
|
var majors = await _dbContext.Queryable<JeecgSysCategoryItem>()
|
||||||
|
.ClearFilter().Where(x => x.Pid == root.Id).OrderBy(x => x.Code).ToListAsync();
|
||||||
|
if (majors.Count == 0) return [];
|
||||||
|
|
||||||
|
var majorIds = majors.Select(x => x.Id).ToList();
|
||||||
|
var allMinors = await _dbContext.Queryable<JeecgSysCategoryItem>()
|
||||||
|
.ClearFilter().Where(x => majorIds.Contains(x.Pid!)).OrderBy(x => x.Code).ToListAsync();
|
||||||
|
|
||||||
|
return majors.Select(major =>
|
||||||
{
|
{
|
||||||
list.Add(new KeyValuePair<string, string>(text!, key!));
|
var minorNodes = allMinors
|
||||||
|
.Where(m => m.Pid == major.Id)
|
||||||
|
.Select(m => new MaterialCategoryNode(m.Id, m.Name, m.Code, []))
|
||||||
|
.ToList();
|
||||||
|
return new MaterialCategoryNode(major.Id, major.Name, major.Code, minorNodes);
|
||||||
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼物料] 加载分类树失败:{ex.Message}");
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> PostJsonAsync(string url, object body, CancellationToken ct)
|
private async Task<bool> PostJsonAsync(string url, object body, CancellationToken ct)
|
||||||
@@ -158,32 +324,20 @@ public class MixerMaterialService : IMixerMaterialService, ISingletonDependency
|
|||||||
{
|
{
|
||||||
var json = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
var json = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
||||||
using var doc = JsonDocument.Parse(json);
|
using var doc = JsonDocument.Parse(json);
|
||||||
if (doc.RootElement.TryGetProperty("code", out var code))
|
if (doc.RootElement.TryGetProperty("code", out var code)) return code.GetInt32() == 200;
|
||||||
{
|
if (doc.RootElement.TryGetProperty("success", out var success)) return success.GetBoolean();
|
||||||
return code.GetInt32() == 200;
|
|
||||||
}
|
|
||||||
if (doc.RootElement.TryGetProperty("success", out var success))
|
|
||||||
{
|
|
||||||
return success.GetBoolean();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
catch { return true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class NullableDateTimeJsonConverter : JsonConverter<DateTime?>
|
private sealed class NullableDateTimeJsonConverter : JsonConverter<DateTime?>
|
||||||
{
|
{
|
||||||
private static readonly string[] SupportedFormats =
|
private static readonly string[] Formats =
|
||||||
[
|
[
|
||||||
"yyyy-MM-dd HH:mm:ss",
|
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss.fff",
|
||||||
"yyyy-MM-dd HH:mm:ss.fff",
|
"yyyy-MM-ddTHH:mm:ss", "yyyy-MM-ddTHH:mm:ss.fff",
|
||||||
"yyyy-MM-ddTHH:mm:ss",
|
"yyyy-MM-ddTHH:mm:ssZ", "yyyy-MM-ddTHH:mm:ss.fffZ"
|
||||||
"yyyy-MM-ddTHH:mm:ss.fff",
|
|
||||||
"yyyy-MM-ddTHH:mm:ssZ",
|
|
||||||
"yyyy-MM-ddTHH:mm:ss.fffZ"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
@@ -193,14 +347,9 @@ public class MixerMaterialService : IMixerMaterialService, ISingletonDependency
|
|||||||
{
|
{
|
||||||
var raw = reader.GetString();
|
var raw = reader.GetString();
|
||||||
if (string.IsNullOrWhiteSpace(raw)) return null;
|
if (string.IsNullOrWhiteSpace(raw)) return null;
|
||||||
if (DateTime.TryParseExact(raw, SupportedFormats, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeLocal, out var exact))
|
if (DateTime.TryParseExact(raw, Formats, System.Globalization.CultureInfo.InvariantCulture,
|
||||||
{
|
System.Globalization.DateTimeStyles.AssumeLocal, out var exact)) return exact;
|
||||||
return exact;
|
if (DateTime.TryParse(raw, out var fallback)) return fallback;
|
||||||
}
|
|
||||||
if (DateTime.TryParse(raw, out var fallback))
|
|
||||||
{
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw new JsonException($"无法将 JSON 值转换为 DateTime?,token={reader.TokenType}");
|
throw new JsonException($"无法将 JSON 值转换为 DateTime?,token={reader.TokenType}");
|
||||||
}
|
}
|
||||||
@@ -212,4 +361,3 @@ public class MixerMaterialService : IMixerMaterialService, ISingletonDependency
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,24 +9,36 @@ namespace YY.Admin.Services.Service.MixerMaterial;
|
|||||||
public class MixerMaterialSyncCoordinator : ISingletonDependency
|
public class MixerMaterialSyncCoordinator : ISingletonDependency
|
||||||
{
|
{
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
private readonly IMixerMaterialService _mixerMaterialService;
|
||||||
private readonly ILoggerService _logger;
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
public MixerMaterialSyncCoordinator(IEventAggregator eventAggregator, ILoggerService logger)
|
public MixerMaterialSyncCoordinator(
|
||||||
|
IEventAggregator eventAggregator,
|
||||||
|
IMixerMaterialService mixerMaterialService,
|
||||||
|
ILoggerService logger)
|
||||||
{
|
{
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
|
_mixerMaterialService = mixerMaterialService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
_eventAggregator.GetEvent<RemoteCommandReceivedEvent>()
|
_eventAggregator.GetEvent<RemoteCommandReceivedEvent>()
|
||||||
.Subscribe(OnRemoteCommand, ThreadOption.BackgroundThread);
|
.Subscribe(OnRemoteCommand, ThreadOption.BackgroundThread);
|
||||||
_eventAggregator.GetEvent<NetworkStatusChangedEvent>()
|
_eventAggregator.GetEvent<NetworkStatusChangedEvent>()
|
||||||
.Subscribe(OnNetworkStatusChanged, ThreadOption.BackgroundThread);
|
.Subscribe(OnNetworkStatusChanged, ThreadOption.BackgroundThread);
|
||||||
|
|
||||||
_logger.Information("[密炼物料推送] MixerMaterialSyncCoordinator 已启动");
|
_logger.Information("[密炼物料推送] MixerMaterialSyncCoordinator 已启动");
|
||||||
|
_ = _mixerMaterialService.SyncFromRemoteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNetworkStatusChanged(NetworkStatusChangedPayload payload)
|
private void OnNetworkStatusChanged(NetworkStatusChangedPayload payload)
|
||||||
{
|
{
|
||||||
if (!payload.IsOnline) return;
|
if (!payload.IsOnline) return;
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await _mixerMaterialService.SyncFromRemoteAsync().ConfigureAwait(false);
|
||||||
_eventAggregator.GetEvent<MixerMaterialChangedEvent>()
|
_eventAggregator.GetEvent<MixerMaterialChangedEvent>()
|
||||||
.Publish(new MixerMaterialChangedPayload { Action = "reconnect" });
|
.Publish(new MixerMaterialChangedPayload { Action = "reconnect" });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRemoteCommand(RemoteCommandPayload payload)
|
private void OnRemoteCommand(RemoteCommandPayload payload)
|
||||||
@@ -43,16 +55,19 @@ public class MixerMaterialSyncCoordinator : ISingletonDependency
|
|||||||
doc.RootElement.TryGetProperty("action", out var actionEl);
|
doc.RootElement.TryGetProperty("action", out var actionEl);
|
||||||
doc.RootElement.TryGetProperty("mixerMaterialId", out var idEl);
|
doc.RootElement.TryGetProperty("mixerMaterialId", out var idEl);
|
||||||
if (idEl.ValueKind != JsonValueKind.String && doc.RootElement.TryGetProperty("id", out var id2El))
|
if (idEl.ValueKind != JsonValueKind.String && doc.RootElement.TryGetProperty("id", out var id2El))
|
||||||
{
|
|
||||||
idEl = id2El;
|
idEl = id2El;
|
||||||
}
|
|
||||||
|
|
||||||
var changed = new MixerMaterialChangedPayload
|
var changed = new MixerMaterialChangedPayload
|
||||||
{
|
{
|
||||||
Action = actionEl.GetString() ?? string.Empty,
|
Action = actionEl.GetString() ?? string.Empty,
|
||||||
MixerMaterialId = idEl.ValueKind == JsonValueKind.String ? idEl.GetString() : null
|
MixerMaterialId = idEl.ValueKind == JsonValueKind.String ? idEl.GetString() : null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await _mixerMaterialService.SyncFromRemoteAsync().ConfigureAwait(false);
|
||||||
_eventAggregator.GetEvent<MixerMaterialChangedEvent>().Publish(changed);
|
_eventAggregator.GetEvent<MixerMaterialChangedEvent>().Publish(changed);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -60,4 +75,3 @@ public class MixerMaterialSyncCoordinator : ISingletonDependency
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ public class WeightRecordService : IWeightRecordService, ISingletonDependency
|
|||||||
string? filterBillNo = null,
|
string? filterBillNo = null,
|
||||||
string? filterPlateNumber = null,
|
string? filterPlateNumber = null,
|
||||||
string? filterInoutDirection = null,
|
string? filterInoutDirection = null,
|
||||||
string? filterGoodsName = null,
|
|
||||||
string? filterDriverName = null,
|
string? filterDriverName = null,
|
||||||
|
string? filterMixerMaterialName = null,
|
||||||
CancellationToken ct = default)
|
CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
List<MesXslWeightRecord>? source = null;
|
List<MesXslWeightRecord>? source = null;
|
||||||
@@ -104,7 +104,7 @@ public class WeightRecordService : IWeightRecordService, ISingletonDependency
|
|||||||
source = ApplyPendingOpsSnapshotUnsafe(source);
|
source = ApplyPendingOpsSnapshotUnsafe(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
var filtered = ApplyFilters(source, filterBillNo, filterPlateNumber, filterInoutDirection, filterGoodsName, filterDriverName);
|
var filtered = ApplyFilters(source, filterBillNo, filterPlateNumber, filterInoutDirection, filterDriverName, filterMixerMaterialName);
|
||||||
var total = filtered.Count;
|
var total = filtered.Count;
|
||||||
var records = filtered.Skip(Math.Max(0, (pageNo - 1) * pageSize)).Take(pageSize).ToList();
|
var records = filtered.Skip(Math.Max(0, (pageNo - 1) * pageSize)).Take(pageSize).ToList();
|
||||||
return new WeightRecordPageResult(records, total, pageNo, pageSize);
|
return new WeightRecordPageResult(records, total, pageNo, pageSize);
|
||||||
@@ -535,7 +535,8 @@ public class WeightRecordService : IWeightRecordService, ISingletonDependency
|
|||||||
|
|
||||||
private static List<MesXslWeightRecord> ApplyFilters(
|
private static List<MesXslWeightRecord> ApplyFilters(
|
||||||
List<MesXslWeightRecord> source,
|
List<MesXslWeightRecord> source,
|
||||||
string? billNo, string? plateNumber, string? inoutDirection, string? goodsName, string? driverName)
|
string? billNo, string? plateNumber, string? inoutDirection, string? driverName,
|
||||||
|
string? mixerMaterialName = null)
|
||||||
{
|
{
|
||||||
IEnumerable<MesXslWeightRecord> q = source;
|
IEnumerable<MesXslWeightRecord> q = source;
|
||||||
if (!string.IsNullOrWhiteSpace(billNo))
|
if (!string.IsNullOrWhiteSpace(billNo))
|
||||||
@@ -544,10 +545,10 @@ public class WeightRecordService : IWeightRecordService, ISingletonDependency
|
|||||||
q = q.Where(v => (v.PlateNumber ?? "").Contains(plateNumber, StringComparison.OrdinalIgnoreCase));
|
q = q.Where(v => (v.PlateNumber ?? "").Contains(plateNumber, StringComparison.OrdinalIgnoreCase));
|
||||||
if (!string.IsNullOrWhiteSpace(inoutDirection))
|
if (!string.IsNullOrWhiteSpace(inoutDirection))
|
||||||
q = q.Where(v => string.Equals(v.InoutDirection, inoutDirection, StringComparison.OrdinalIgnoreCase));
|
q = q.Where(v => string.Equals(v.InoutDirection, inoutDirection, StringComparison.OrdinalIgnoreCase));
|
||||||
if (!string.IsNullOrWhiteSpace(goodsName))
|
|
||||||
q = q.Where(v => (v.GoodsName ?? "").Contains(goodsName, StringComparison.OrdinalIgnoreCase));
|
|
||||||
if (!string.IsNullOrWhiteSpace(driverName))
|
if (!string.IsNullOrWhiteSpace(driverName))
|
||||||
q = q.Where(v => (v.DriverName ?? "").Contains(driverName, StringComparison.OrdinalIgnoreCase));
|
q = q.Where(v => (v.DriverName ?? "").Contains(driverName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (!string.IsNullOrWhiteSpace(mixerMaterialName))
|
||||||
|
q = q.Where(v => (v.MixerMaterialNames ?? "").Contains(mixerMaterialName, StringComparison.OrdinalIgnoreCase));
|
||||||
return q.OrderByDescending(v => v.CreateTime ?? DateTime.MinValue).ToList();
|
return q.OrderByDescending(v => v.CreateTime ?? DateTime.MinValue).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,7 +642,9 @@ public class WeightRecordService : IWeightRecordService, ISingletonDependency
|
|||||||
PlateNumber = input.PlateNumber,
|
PlateNumber = input.PlateNumber,
|
||||||
SenderUnit = input.SenderUnit,
|
SenderUnit = input.SenderUnit,
|
||||||
ReceiverUnit = input.ReceiverUnit,
|
ReceiverUnit = input.ReceiverUnit,
|
||||||
GoodsName = input.GoodsName,
|
MixerMaterialIds = input.MixerMaterialIds,
|
||||||
|
MixerMaterialNames = input.MixerMaterialNames,
|
||||||
|
MaterialType = input.MaterialType,
|
||||||
GrossWeight = input.GrossWeight,
|
GrossWeight = input.GrossWeight,
|
||||||
TareWeight = input.TareWeight,
|
TareWeight = input.TareWeight,
|
||||||
NetWeight = input.NetWeight,
|
NetWeight = input.NetWeight,
|
||||||
@@ -655,7 +658,8 @@ public class WeightRecordService : IWeightRecordService, ISingletonDependency
|
|||||||
UpdateTime = input.UpdateTime,
|
UpdateTime = input.UpdateTime,
|
||||||
SysOrgCode = input.SysOrgCode,
|
SysOrgCode = input.SysOrgCode,
|
||||||
InoutDirectionText = input.InoutDirectionText,
|
InoutDirectionText = input.InoutDirectionText,
|
||||||
BillTypeText = input.BillTypeText
|
BillTypeText = input.BillTypeText,
|
||||||
|
MaterialTypeText = input.MaterialTypeText
|
||||||
};
|
};
|
||||||
|
|
||||||
private static bool IsLocalTempId(string? id) =>
|
private static bool IsLocalTempId(string? id) =>
|
||||||
|
|||||||
@@ -142,6 +142,14 @@ public class StompWebSocketService : ISignalRService
|
|||||||
await SendFrameAsync(
|
await SendFrameAsync(
|
||||||
BuildSubscribeFrame("sub-mes-mixer-material", "/topic/sync/mes-mixer-materials"),
|
BuildSubscribeFrame("sub-mes-mixer-material", "/topic/sync/mes-mixer-materials"),
|
||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
// 分类字典变更:订阅 /topic/sync/sys-categories
|
||||||
|
await SendFrameAsync(
|
||||||
|
BuildSubscribeFrame("sub-sys-categories", "/topic/sync/sys-categories"),
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
// 数据字典变更:订阅 /topic/sync/sys-dicts
|
||||||
|
await SendFrameAsync(
|
||||||
|
BuildSubscribeFrame("sub-sys-dicts", "/topic/sync/sys-dicts"),
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// 订阅服务端 PONG 回复(应用层假在线检测)
|
// 订阅服务端 PONG 回复(应用层假在线检测)
|
||||||
await SendFrameAsync(
|
await SendFrameAsync(
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ using YY.Admin.Infrastructure.Hubs;
|
|||||||
using YY.Admin.Infrastructure.Network;
|
using YY.Admin.Infrastructure.Network;
|
||||||
using YY.Admin.Infrastructure.Storage;
|
using YY.Admin.Infrastructure.Storage;
|
||||||
using YY.Admin.Infrastructure.Sync;
|
using YY.Admin.Infrastructure.Sync;
|
||||||
|
using YY.Admin.Services.Service.Category;
|
||||||
using YY.Admin.Services.Service.Customer;
|
using YY.Admin.Services.Service.Customer;
|
||||||
using YY.Admin.Services.Service.Supplier;
|
using YY.Admin.Services.Service.Dict;
|
||||||
using YY.Admin.Services.Service.MixerMaterial;
|
using YY.Admin.Services.Service.MixerMaterial;
|
||||||
|
using YY.Admin.Services.Service.Supplier;
|
||||||
using YY.Admin.Services.Service.Vehicle;
|
using YY.Admin.Services.Service.Vehicle;
|
||||||
using YY.Admin.Services.Service.WeightRecord;
|
using YY.Admin.Services.Service.WeightRecord;
|
||||||
|
|
||||||
@@ -50,6 +52,10 @@ public class SyncModule : IModule
|
|||||||
// 密炼物料信息:API直连 + STOMP实时通知
|
// 密炼物料信息:API直连 + STOMP实时通知
|
||||||
containerRegistry.RegisterSingleton<IMixerMaterialService, MixerMaterialService>();
|
containerRegistry.RegisterSingleton<IMixerMaterialService, MixerMaterialService>();
|
||||||
containerRegistry.RegisterSingleton<MixerMaterialSyncCoordinator>();
|
containerRegistry.RegisterSingleton<MixerMaterialSyncCoordinator>();
|
||||||
|
// 分类字典:启动同步 + 断线重连补刷
|
||||||
|
containerRegistry.RegisterSingleton<CategorySyncCoordinator>();
|
||||||
|
// 数据字典:启动同步 + 断线重连补刷
|
||||||
|
containerRegistry.RegisterSingleton<DictSyncCoordinator>();
|
||||||
|
|
||||||
var serviceCollection = new ServiceCollection();
|
var serviceCollection = new ServiceCollection();
|
||||||
serviceCollection.AddTransient<DisconnectGuardHandler>();
|
serviceCollection.AddTransient<DisconnectGuardHandler>();
|
||||||
@@ -104,6 +110,10 @@ public class SyncModule : IModule
|
|||||||
_ = containerProvider.Resolve<WeightRecordSyncCoordinator>();
|
_ = containerProvider.Resolve<WeightRecordSyncCoordinator>();
|
||||||
// 强制实例化密炼物料同步协调器
|
// 强制实例化密炼物料同步协调器
|
||||||
_ = containerProvider.Resolve<MixerMaterialSyncCoordinator>();
|
_ = containerProvider.Resolve<MixerMaterialSyncCoordinator>();
|
||||||
|
// 强制实例化分类字典同步协调器
|
||||||
|
_ = containerProvider.Resolve<CategorySyncCoordinator>();
|
||||||
|
// 强制实例化数据字典同步协调器
|
||||||
|
_ = containerProvider.Resolve<DictSyncCoordinator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
|
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
|
||||||
|
|||||||
@@ -64,6 +64,42 @@
|
|||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<!-- Button Text(透明背景链接风格按钮) -->
|
||||||
|
<Style x:Key="ButtonText" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource PrimaryBrush}"/>
|
||||||
|
<Setter Property="Padding" Value="4 2"/>
|
||||||
|
<Setter Property="MinHeight" Value="0"/>
|
||||||
|
<Setter Property="MinWidth" Value="0"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Opacity" Value="0.75"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter Property="Opacity" Value="0.5"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsEnabled" Value="False">
|
||||||
|
<Setter Property="Opacity" Value="0.4"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
<!-- TreeView -->
|
<!-- TreeView -->
|
||||||
<Style x:Key="CusTreeViewItemBaseStyle" BasedOn="{StaticResource TreeViewItemBaseStyle}" TargetType="TreeViewItem">
|
<Style x:Key="CusTreeViewItemBaseStyle" BasedOn="{StaticResource TreeViewItemBaseStyle}" TargetType="TreeViewItem">
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
|
|||||||
@@ -62,6 +62,28 @@ public class MixerMaterialListViewModel : BaseViewModel
|
|||||||
public ObservableCollection<KeyValuePair<string, string>> MajorCategoryOptions { get; } = new();
|
public ObservableCollection<KeyValuePair<string, string>> MajorCategoryOptions { get; } = new();
|
||||||
public ObservableCollection<KeyValuePair<string, string>> MinorCategoryOptions { get; } = new();
|
public ObservableCollection<KeyValuePair<string, string>> MinorCategoryOptions { get; } = new();
|
||||||
|
|
||||||
|
public ObservableCollection<CategoryFilterNode> TreeNodes { get; } = new();
|
||||||
|
|
||||||
|
private CategoryFilterNode? _selectedTreeNode;
|
||||||
|
public CategoryFilterNode? SelectedTreeNode
|
||||||
|
{
|
||||||
|
get => _selectedTreeNode;
|
||||||
|
set => SetProperty(ref _selectedTreeNode, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isTreeAllExpanded = true;
|
||||||
|
public bool IsTreeAllExpanded
|
||||||
|
{
|
||||||
|
get => _isTreeAllExpanded;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isTreeAllExpanded, value))
|
||||||
|
RaisePropertyChanged(nameof(ToggleExpandButtonText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToggleExpandButtonText => _isTreeAllExpanded ? "折叠全部" : "展开全部";
|
||||||
|
|
||||||
public DelegateCommand SearchCommand { get; }
|
public DelegateCommand SearchCommand { get; }
|
||||||
public DelegateCommand ResetCommand { get; }
|
public DelegateCommand ResetCommand { get; }
|
||||||
public DelegateCommand AddCommand { get; }
|
public DelegateCommand AddCommand { get; }
|
||||||
@@ -69,6 +91,7 @@ public class MixerMaterialListViewModel : BaseViewModel
|
|||||||
public DelegateCommand<MesMixerMaterial> DeleteCommand { get; }
|
public DelegateCommand<MesMixerMaterial> DeleteCommand { get; }
|
||||||
public DelegateCommand PrevPageCommand { get; }
|
public DelegateCommand PrevPageCommand { get; }
|
||||||
public DelegateCommand NextPageCommand { get; }
|
public DelegateCommand NextPageCommand { get; }
|
||||||
|
public DelegateCommand ToggleExpandCommand { get; }
|
||||||
|
|
||||||
public MixerMaterialListViewModel(
|
public MixerMaterialListViewModel(
|
||||||
IMixerMaterialService mixerMaterialService,
|
IMixerMaterialService mixerMaterialService,
|
||||||
@@ -83,10 +106,12 @@ public class MixerMaterialListViewModel : BaseViewModel
|
|||||||
FilterMaterialCode = null;
|
FilterMaterialCode = null;
|
||||||
FilterMaterialName = null;
|
FilterMaterialName = null;
|
||||||
FilterErpCode = null;
|
FilterErpCode = null;
|
||||||
FilterMajorCategoryId = null;
|
_filterMajorCategoryId = null;
|
||||||
|
RaisePropertyChanged(nameof(FilterMajorCategoryId));
|
||||||
FilterMinorCategoryId = null;
|
FilterMinorCategoryId = null;
|
||||||
PageNo = 1;
|
SelectedTreeNode = null;
|
||||||
await LoadMinorCategoryOptionsAsync();
|
await LoadMinorCategoryOptionsAsync();
|
||||||
|
PageNo = 1;
|
||||||
await LoadAsync();
|
await LoadAsync();
|
||||||
});
|
});
|
||||||
AddCommand = new DelegateCommand(async () => await ShowAddDialogAsync());
|
AddCommand = new DelegateCommand(async () => await ShowAddDialogAsync());
|
||||||
@@ -94,6 +119,7 @@ public class MixerMaterialListViewModel : BaseViewModel
|
|||||||
DeleteCommand = new DelegateCommand<MesMixerMaterial>(async m => await DeleteAsync(m));
|
DeleteCommand = new DelegateCommand<MesMixerMaterial>(async m => await DeleteAsync(m));
|
||||||
PrevPageCommand = new DelegateCommand(async () => { if (PageNo > 1) { PageNo--; await LoadAsync(); } });
|
PrevPageCommand = new DelegateCommand(async () => { if (PageNo > 1) { PageNo--; await LoadAsync(); } });
|
||||||
NextPageCommand = new DelegateCommand(async () => { if ((long)PageNo * PageSize < Total) { PageNo++; await LoadAsync(); } });
|
NextPageCommand = new DelegateCommand(async () => { if ((long)PageNo * PageSize < Total) { PageNo++; await LoadAsync(); } });
|
||||||
|
ToggleExpandCommand = new DelegateCommand(ToggleExpand);
|
||||||
|
|
||||||
_materialChangedToken = _eventAggregator
|
_materialChangedToken = _eventAggregator
|
||||||
.GetEvent<MixerMaterialChangedEvent>()
|
.GetEvent<MixerMaterialChangedEvent>()
|
||||||
@@ -106,6 +132,7 @@ public class MixerMaterialListViewModel : BaseViewModel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
await LoadCategoryTreeAsync();
|
||||||
await LoadMajorCategoryOptionsAsync();
|
await LoadMajorCategoryOptionsAsync();
|
||||||
await LoadMinorCategoryOptionsAsync();
|
await LoadMinorCategoryOptionsAsync();
|
||||||
await UIHelper.WaitForRenderAsync();
|
await UIHelper.WaitForRenderAsync();
|
||||||
@@ -117,6 +144,70 @@ public class MixerMaterialListViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LoadCategoryTreeAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tree = await _mixerMaterialService.GetMaterialCategoryTreeAsync();
|
||||||
|
TreeNodes.Clear();
|
||||||
|
var root = new CategoryFilterNode("", "全部", null, false, null, []) { IsExpanded = true };
|
||||||
|
foreach (var major in tree)
|
||||||
|
{
|
||||||
|
var majorNode = new CategoryFilterNode(major.Id, major.Name ?? major.Code ?? major.Id, major.Code, true, null, []);
|
||||||
|
foreach (var minor in major.Children)
|
||||||
|
{
|
||||||
|
majorNode.Children.Add(new CategoryFilterNode(minor.Id, minor.Name ?? minor.Code ?? minor.Id, minor.Code, false, major.Id, []));
|
||||||
|
}
|
||||||
|
root.Children.Add(majorNode);
|
||||||
|
}
|
||||||
|
TreeNodes.Add(root);
|
||||||
|
IsTreeAllExpanded = false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"加载物料分类树失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleExpand()
|
||||||
|
{
|
||||||
|
var expand = !_isTreeAllExpanded;
|
||||||
|
foreach (var root in TreeNodes)
|
||||||
|
{
|
||||||
|
root.IsExpanded = true; // 根节点始终展开
|
||||||
|
foreach (var major in root.Children)
|
||||||
|
major.IsExpanded = expand;
|
||||||
|
}
|
||||||
|
IsTreeAllExpanded = expand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnTreeNodeSelectedAsync(CategoryFilterNode? node)
|
||||||
|
{
|
||||||
|
SelectedTreeNode = node;
|
||||||
|
if (node == null || node.Id == "")
|
||||||
|
{
|
||||||
|
_filterMajorCategoryId = null;
|
||||||
|
RaisePropertyChanged(nameof(FilterMajorCategoryId));
|
||||||
|
FilterMinorCategoryId = null;
|
||||||
|
}
|
||||||
|
else if (node.IsMajor)
|
||||||
|
{
|
||||||
|
_filterMajorCategoryId = node.Id;
|
||||||
|
RaisePropertyChanged(nameof(FilterMajorCategoryId));
|
||||||
|
FilterMinorCategoryId = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_filterMajorCategoryId = node.ParentId;
|
||||||
|
RaisePropertyChanged(nameof(FilterMajorCategoryId));
|
||||||
|
FilterMinorCategoryId = node.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
await LoadMinorCategoryOptionsAsync();
|
||||||
|
PageNo = 1;
|
||||||
|
await LoadAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task LoadMajorCategoryOptionsAsync()
|
private async Task LoadMajorCategoryOptionsAsync()
|
||||||
{
|
{
|
||||||
MajorCategoryOptions.Clear();
|
MajorCategoryOptions.Clear();
|
||||||
@@ -132,11 +223,11 @@ public class MixerMaterialListViewModel : BaseViewModel
|
|||||||
{
|
{
|
||||||
MinorCategoryOptions.Clear();
|
MinorCategoryOptions.Clear();
|
||||||
MinorCategoryOptions.Add(new KeyValuePair<string, string>("全部", ""));
|
MinorCategoryOptions.Add(new KeyValuePair<string, string>("全部", ""));
|
||||||
if (string.IsNullOrWhiteSpace(FilterMajorCategoryId))
|
if (string.IsNullOrWhiteSpace(_filterMajorCategoryId))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var options = await _mixerMaterialService.GetMinorCategoryOptionsAsync(FilterMajorCategoryId!);
|
var options = await _mixerMaterialService.GetMinorCategoryOptionsAsync(_filterMajorCategoryId!);
|
||||||
foreach (var item in options)
|
foreach (var item in options)
|
||||||
{
|
{
|
||||||
MinorCategoryOptions.Add(item);
|
MinorCategoryOptions.Add(item);
|
||||||
@@ -155,7 +246,7 @@ public class MixerMaterialListViewModel : BaseViewModel
|
|||||||
{
|
{
|
||||||
IsLoading = true;
|
IsLoading = true;
|
||||||
var result = await _mixerMaterialService.PageAsync(
|
var result = await _mixerMaterialService.PageAsync(
|
||||||
PageNo, PageSize, FilterMaterialCode, FilterMaterialName, FilterErpCode, FilterMajorCategoryId, FilterMinorCategoryId);
|
PageNo, PageSize, FilterMaterialCode, FilterMaterialName, FilterErpCode, _filterMajorCategoryId, FilterMinorCategoryId);
|
||||||
Materials = new ObservableCollection<MesMixerMaterial>(result.Records);
|
Materials = new ObservableCollection<MesMixerMaterial>(result.Records);
|
||||||
Total = result.Total;
|
Total = result.Total;
|
||||||
}
|
}
|
||||||
@@ -228,5 +319,40 @@ public class MixerMaterialListViewModel : BaseViewModel
|
|||||||
_materialChangedToken = null;
|
_materialChangedToken = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CategoryFilterNode : System.ComponentModel.INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
public string Id { get; }
|
||||||
|
public string DisplayName { get; }
|
||||||
|
public string? Code { get; }
|
||||||
|
public bool IsMajor { get; }
|
||||||
|
public string? ParentId { get; }
|
||||||
|
public ObservableCollection<CategoryFilterNode> Children { get; }
|
||||||
|
|
||||||
|
private bool _isExpanded;
|
||||||
|
public bool IsExpanded
|
||||||
|
{
|
||||||
|
get => _isExpanded;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isExpanded != value)
|
||||||
|
{
|
||||||
|
_isExpanded = value;
|
||||||
|
PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(nameof(IsExpanded)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CategoryFilterNode(string id, string? displayName, string? code, bool isMajor, string? parentId, List<CategoryFilterNode> children)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
DisplayName = displayName ?? id;
|
||||||
|
Code = code;
|
||||||
|
IsMajor = isMajor;
|
||||||
|
ParentId = parentId;
|
||||||
|
Children = new ObservableCollection<CategoryFilterNode>(children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using HandyControl.Controls;
|
using HandyControl.Controls;
|
||||||
|
using Prism.Events;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using YY.Admin.Core;
|
using YY.Admin.Core;
|
||||||
|
using YY.Admin.Core.Events;
|
||||||
using YY.Admin.Core.Helper;
|
using YY.Admin.Core.Helper;
|
||||||
using YY.Admin.Services.Service.Category;
|
using YY.Admin.Services.Service.Category;
|
||||||
using YY.Admin.Services.Service.Category.Dto;
|
using YY.Admin.Services.Service.Category.Dto;
|
||||||
@@ -11,6 +13,7 @@ namespace YY.Admin.ViewModels.SysManage;
|
|||||||
public class CategoryDictionaryManagementViewModel : BaseViewModel
|
public class CategoryDictionaryManagementViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private readonly IJeecgCategorySyncService _categorySyncService;
|
private readonly IJeecgCategorySyncService _categorySyncService;
|
||||||
|
private SubscriptionToken? _categoryChangedToken;
|
||||||
|
|
||||||
private PaginationDataGridViewModel<JeecgCategoryItemOutput> _paginationDataGridViewModel;
|
private PaginationDataGridViewModel<JeecgCategoryItemOutput> _paginationDataGridViewModel;
|
||||||
public PaginationDataGridViewModel<JeecgCategoryItemOutput> PaginationDataGridViewModel
|
public PaginationDataGridViewModel<JeecgCategoryItemOutput> PaginationDataGridViewModel
|
||||||
@@ -28,7 +31,6 @@ public class CategoryDictionaryManagementViewModel : BaseViewModel
|
|||||||
|
|
||||||
public DelegateCommand SearchCommand { get; }
|
public DelegateCommand SearchCommand { get; }
|
||||||
public DelegateCommand ResetCommand { get; }
|
public DelegateCommand ResetCommand { get; }
|
||||||
public DelegateCommand SyncCommand { get; }
|
|
||||||
public ObservableCollection<CategoryTreeNode> TreeNodes { get; } = [];
|
public ObservableCollection<CategoryTreeNode> TreeNodes { get; } = [];
|
||||||
|
|
||||||
private CategoryTreeNode? _selectedTreeNode;
|
private CategoryTreeNode? _selectedTreeNode;
|
||||||
@@ -49,7 +51,10 @@ public class CategoryDictionaryManagementViewModel : BaseViewModel
|
|||||||
|
|
||||||
SearchCommand = new DelegateCommand(async () => await SearchAsync());
|
SearchCommand = new DelegateCommand(async () => await SearchAsync());
|
||||||
ResetCommand = new DelegateCommand(async () => await ResetAsync());
|
ResetCommand = new DelegateCommand(async () => await ResetAsync());
|
||||||
SyncCommand = new DelegateCommand(async () => await SyncAsync());
|
|
||||||
|
_categoryChangedToken = _eventAggregator
|
||||||
|
.GetEvent<CategoryChangedEvent>()
|
||||||
|
.Subscribe(async _ => await OnCategoryChangedAsync(), ThreadOption.UIThread);
|
||||||
|
|
||||||
_ = InitializeAsync();
|
_ = InitializeAsync();
|
||||||
}
|
}
|
||||||
@@ -84,21 +89,6 @@ public class CategoryDictionaryManagementViewModel : BaseViewModel
|
|||||||
await SearchAsync();
|
await SearchAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SyncAsync()
|
|
||||||
{
|
|
||||||
var count = await _categorySyncService.SyncFromJeecgAsync();
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
Growl.Success($"同步完成,共处理 {count} 条分类字典数据");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Growl.Warning("未同步到分类字典,请确认后端可访问");
|
|
||||||
}
|
|
||||||
await LoadTreeAsync();
|
|
||||||
await SearchAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OnTreeSelectedAsync(CategoryTreeNode? node)
|
public async Task OnTreeSelectedAsync(CategoryTreeNode? node)
|
||||||
{
|
{
|
||||||
SelectedTreeNode = node;
|
SelectedTreeNode = node;
|
||||||
@@ -131,6 +121,22 @@ public class CategoryDictionaryManagementViewModel : BaseViewModel
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OnCategoryChangedAsync()
|
||||||
|
{
|
||||||
|
await LoadTreeAsync();
|
||||||
|
await PaginationDataGridViewModel.LoadDataAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CleanUp()
|
||||||
|
{
|
||||||
|
base.CleanUp();
|
||||||
|
if (_categoryChangedToken != null)
|
||||||
|
{
|
||||||
|
_eventAggregator.GetEvent<CategoryChangedEvent>().Unsubscribe(_categoryChangedToken);
|
||||||
|
_categoryChangedToken = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class CategoryTreeNode
|
public class CategoryTreeNode
|
||||||
{
|
{
|
||||||
public string Id { get; set; } = string.Empty;
|
public string Id { get; set; } = string.Empty;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using HandyControl.Controls;
|
using Prism.Events;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using YY.Admin.Core;
|
using YY.Admin.Core;
|
||||||
|
using YY.Admin.Core.Events;
|
||||||
using YY.Admin.Core.Extension;
|
using YY.Admin.Core.Extension;
|
||||||
using YY.Admin.Core.Helper;
|
using YY.Admin.Core.Helper;
|
||||||
using YY.Admin.Services.Service;
|
using YY.Admin.Services.Service;
|
||||||
@@ -10,6 +12,16 @@ namespace YY.Admin.ViewModels.SysManage;
|
|||||||
public class DataDictionaryManagementViewModel : BaseViewModel
|
public class DataDictionaryManagementViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private readonly IJeecgDictSyncService _dictSyncService;
|
private readonly IJeecgDictSyncService _dictSyncService;
|
||||||
|
private SubscriptionToken? _dictChangedToken;
|
||||||
|
|
||||||
|
public ObservableCollection<DictTreeNode> TreeNodes { get; } = [];
|
||||||
|
|
||||||
|
private DictTreeNode? _selectedTreeNode;
|
||||||
|
public DictTreeNode? SelectedTreeNode
|
||||||
|
{
|
||||||
|
get => _selectedTreeNode;
|
||||||
|
set => SetProperty(ref _selectedTreeNode, value);
|
||||||
|
}
|
||||||
|
|
||||||
private PaginationDataGridViewModel<JeecgDictItemOutput> _paginationDataGridViewModel;
|
private PaginationDataGridViewModel<JeecgDictItemOutput> _paginationDataGridViewModel;
|
||||||
public PaginationDataGridViewModel<JeecgDictItemOutput> PaginationDataGridViewModel
|
public PaginationDataGridViewModel<JeecgDictItemOutput> PaginationDataGridViewModel
|
||||||
@@ -33,7 +45,6 @@ public class DataDictionaryManagementViewModel : BaseViewModel
|
|||||||
|
|
||||||
public DelegateCommand SearchCommand { get; }
|
public DelegateCommand SearchCommand { get; }
|
||||||
public DelegateCommand ResetCommand { get; }
|
public DelegateCommand ResetCommand { get; }
|
||||||
public DelegateCommand SyncCommand { get; }
|
|
||||||
|
|
||||||
public DataDictionaryManagementViewModel(
|
public DataDictionaryManagementViewModel(
|
||||||
IJeecgDictSyncService dictSyncService,
|
IJeecgDictSyncService dictSyncService,
|
||||||
@@ -46,13 +57,17 @@ public class DataDictionaryManagementViewModel : BaseViewModel
|
|||||||
|
|
||||||
SearchCommand = new DelegateCommand(async () => await SearchAsync());
|
SearchCommand = new DelegateCommand(async () => await SearchAsync());
|
||||||
ResetCommand = new DelegateCommand(async () => await ResetAsync());
|
ResetCommand = new DelegateCommand(async () => await ResetAsync());
|
||||||
SyncCommand = new DelegateCommand(async () => await SyncAsync());
|
|
||||||
|
_dictChangedToken = _eventAggregator
|
||||||
|
.GetEvent<DictChangedEvent>()
|
||||||
|
.Subscribe(async _ => await OnDictChangedAsync(), ThreadOption.UIThread);
|
||||||
|
|
||||||
_ = InitializeAsync();
|
_ = InitializeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InitializeAsync()
|
private async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
|
await LoadDictTreeAsync();
|
||||||
await UIHelper.WaitForRenderAsync();
|
await UIHelper.WaitForRenderAsync();
|
||||||
await PaginationDataGridViewModel.LoadDataAsync();
|
await PaginationDataGridViewModel.LoadDataAsync();
|
||||||
}
|
}
|
||||||
@@ -74,21 +89,54 @@ public class DataDictionaryManagementViewModel : BaseViewModel
|
|||||||
private async Task ResetAsync()
|
private async Task ResetAsync()
|
||||||
{
|
{
|
||||||
Input = new PageJeecgDictItemInput();
|
Input = new PageJeecgDictItemInput();
|
||||||
|
SelectedTreeNode = null;
|
||||||
await UIHelper.WaitForRenderAsync();
|
await UIHelper.WaitForRenderAsync();
|
||||||
await SearchAsync();
|
await SearchAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SyncAsync()
|
public async Task OnDictTreeSelectedAsync(DictTreeNode? node)
|
||||||
{
|
{
|
||||||
var count = await _dictSyncService.SyncFromJeecgAsync();
|
SelectedTreeNode = node;
|
||||||
if (count > 0)
|
Input.DictCode = node?.DictCode;
|
||||||
{
|
|
||||||
Growl.Success($"同步完成,共处理 {count} 条数据字典项");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Growl.Warning("未同步到数据字典,请确认后端可访问");
|
|
||||||
}
|
|
||||||
await SearchAsync();
|
await SearchAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LoadDictTreeAsync()
|
||||||
|
{
|
||||||
|
var groups = await _dictSyncService.GetDictGroupsAsync();
|
||||||
|
TreeNodes.Clear();
|
||||||
|
foreach (var group in groups)
|
||||||
|
{
|
||||||
|
TreeNodes.Add(new DictTreeNode(group.Key, group.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnDictChangedAsync()
|
||||||
|
{
|
||||||
|
await LoadDictTreeAsync();
|
||||||
|
await PaginationDataGridViewModel.LoadDataAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CleanUp()
|
||||||
|
{
|
||||||
|
base.CleanUp();
|
||||||
|
if (_dictChangedToken != null)
|
||||||
|
{
|
||||||
|
_eventAggregator.GetEvent<DictChangedEvent>().Unsubscribe(_dictChangedToken);
|
||||||
|
_dictChangedToken = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DictTreeNode
|
||||||
|
{
|
||||||
|
public string DictCode { get; }
|
||||||
|
public string DictName { get; }
|
||||||
|
public string DisplayName => $"[{DictCode}] {DictName}";
|
||||||
|
|
||||||
|
public DictTreeNode(string dictCode, string dictName)
|
||||||
|
{
|
||||||
|
DictCode = dictCode;
|
||||||
|
DictName = dictName;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
using HandyControl.Tools.Extension;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using YY.Admin.Core;
|
||||||
|
using YY.Admin.Core.Entity;
|
||||||
|
using YY.Admin.Core.Services;
|
||||||
|
|
||||||
|
namespace YY.Admin.ViewModels.WeightRecord;
|
||||||
|
|
||||||
|
public class MixerMaterialPickerDialogViewModel : BaseViewModel, IDialogResultable<bool>
|
||||||
|
{
|
||||||
|
private readonly IMixerMaterialService _mixerMaterialService;
|
||||||
|
|
||||||
|
private string? _searchCode;
|
||||||
|
public string? SearchCode { get => _searchCode; set => SetProperty(ref _searchCode, value); }
|
||||||
|
|
||||||
|
private string? _searchName;
|
||||||
|
public string? SearchName { get => _searchName; set => SetProperty(ref _searchName, value); }
|
||||||
|
|
||||||
|
public ObservableCollection<SelectableMixerMaterial> Materials { get; } = new();
|
||||||
|
|
||||||
|
public IReadOnlyList<SelectableMixerMaterial> SelectedMaterials =>
|
||||||
|
Materials.Where(m => m.IsSelected).ToList();
|
||||||
|
|
||||||
|
public int SelectedCount => Materials.Count(m => m.IsSelected);
|
||||||
|
|
||||||
|
public string SelectedDisplayText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var names = Materials.Where(m => m.IsSelected).Select(m => m.Source.MaterialName).ToList();
|
||||||
|
return names.Count == 0 ? "请在上方列表中勾选物料,可多选" : string.Join(";", names);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasSelectedMaterials => Materials.Any(m => m.IsSelected);
|
||||||
|
|
||||||
|
private bool _result;
|
||||||
|
public bool Result { get => _result; set => SetProperty(ref _result, value); }
|
||||||
|
public Action? CloseAction { get; set; }
|
||||||
|
|
||||||
|
public DelegateCommand SearchCommand { get; }
|
||||||
|
public DelegateCommand ConfirmCommand { get; }
|
||||||
|
public DelegateCommand CancelCommand { get; }
|
||||||
|
public DelegateCommand ClearSelectionCommand { get; }
|
||||||
|
|
||||||
|
public MixerMaterialPickerDialogViewModel(
|
||||||
|
IMixerMaterialService mixerMaterialService,
|
||||||
|
IContainerExtension container,
|
||||||
|
IRegionManager regionManager) : base(container, regionManager)
|
||||||
|
{
|
||||||
|
_mixerMaterialService = mixerMaterialService;
|
||||||
|
SearchCommand = new DelegateCommand(async () => await LoadAsync());
|
||||||
|
ConfirmCommand = new DelegateCommand(Confirm, () => HasSelectedMaterials);
|
||||||
|
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
|
||||||
|
ClearSelectionCommand = new DelegateCommand(ClearSelection);
|
||||||
|
_ = LoadAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsLoading = true;
|
||||||
|
var selectedIds = Materials.Where(m => m.IsSelected).Select(m => m.Source.Id).ToHashSet();
|
||||||
|
var result = await _mixerMaterialService.PageAsync(1, 500,
|
||||||
|
materialCode: SearchCode?.Trim(),
|
||||||
|
materialName: SearchName?.Trim());
|
||||||
|
Materials.Clear();
|
||||||
|
foreach (var mat in result.Records)
|
||||||
|
{
|
||||||
|
var item = new SelectableMixerMaterial(mat);
|
||||||
|
if (selectedIds.Contains(mat.Id)) item.IsSelected = true;
|
||||||
|
item.SelectionChanged += NotifySelectionChanged;
|
||||||
|
Materials.Add(item);
|
||||||
|
}
|
||||||
|
NotifySelectionChanged();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Materials.Clear();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotifySelectionChanged()
|
||||||
|
{
|
||||||
|
RaisePropertyChanged(nameof(SelectedCount));
|
||||||
|
RaisePropertyChanged(nameof(SelectedDisplayText));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedMaterials));
|
||||||
|
ConfirmCommand.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearSelection()
|
||||||
|
{
|
||||||
|
foreach (var m in Materials) m.IsSelected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Confirm()
|
||||||
|
{
|
||||||
|
if (!HasSelectedMaterials) return;
|
||||||
|
Result = true;
|
||||||
|
CloseAction?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SelectableMixerMaterial : System.ComponentModel.INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
public event Action? SelectionChanged;
|
||||||
|
|
||||||
|
private bool _isSelected;
|
||||||
|
public bool IsSelected
|
||||||
|
{
|
||||||
|
get => _isSelected;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_isSelected == value) return;
|
||||||
|
_isSelected = value;
|
||||||
|
PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(nameof(IsSelected)));
|
||||||
|
SelectionChanged?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MesMixerMaterial Source { get; }
|
||||||
|
public SelectableMixerMaterial(MesMixerMaterial source) => Source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ using YY.Admin.Core;
|
|||||||
using YY.Admin.Core.Entity;
|
using YY.Admin.Core.Entity;
|
||||||
using YY.Admin.Core.Services;
|
using YY.Admin.Core.Services;
|
||||||
using YY.Admin.Services.Service;
|
using YY.Admin.Services.Service;
|
||||||
|
using YY.Admin.Views.WeightRecord;
|
||||||
|
|
||||||
namespace YY.Admin.ViewModels.WeightRecord;
|
namespace YY.Admin.ViewModels.WeightRecord;
|
||||||
|
|
||||||
@@ -12,6 +13,16 @@ public class WeightRecordEditDialogViewModel : BaseViewModel, IDialogResultable<
|
|||||||
{
|
{
|
||||||
private readonly IWeightRecordService _weightRecordService;
|
private readonly IWeightRecordService _weightRecordService;
|
||||||
private readonly IJeecgDictSyncService _dictSyncService;
|
private readonly IJeecgDictSyncService _dictSyncService;
|
||||||
|
private readonly IMixerMaterialService _mixerMaterialService;
|
||||||
|
|
||||||
|
private string? _mixerMaterialIds;
|
||||||
|
private string? _mixerMaterialNames;
|
||||||
|
|
||||||
|
public string MixerMaterialDisplay => !string.IsNullOrWhiteSpace(_mixerMaterialNames)
|
||||||
|
? _mixerMaterialNames
|
||||||
|
: "点击右侧「选择」选取密炼物料(可多选)";
|
||||||
|
|
||||||
|
public bool HasSelectedMixerMaterials => !string.IsNullOrWhiteSpace(_mixerMaterialNames);
|
||||||
|
|
||||||
private MesXslWeightRecord? _record;
|
private MesXslWeightRecord? _record;
|
||||||
public MesXslWeightRecord? Record
|
public MesXslWeightRecord? Record
|
||||||
@@ -31,20 +42,56 @@ public class WeightRecordEditDialogViewModel : BaseViewModel, IDialogResultable<
|
|||||||
|
|
||||||
public DelegateCommand SaveCommand { get; }
|
public DelegateCommand SaveCommand { get; }
|
||||||
public DelegateCommand CancelCommand { get; }
|
public DelegateCommand CancelCommand { get; }
|
||||||
|
public DelegateCommand OpenMixerMaterialPickerCommand { get; }
|
||||||
|
public DelegateCommand ClearMixerMaterialCommand { get; }
|
||||||
|
|
||||||
public WeightRecordEditDialogViewModel(
|
public WeightRecordEditDialogViewModel(
|
||||||
IWeightRecordService weightRecordService,
|
IWeightRecordService weightRecordService,
|
||||||
IJeecgDictSyncService dictSyncService,
|
IJeecgDictSyncService dictSyncService,
|
||||||
|
IMixerMaterialService mixerMaterialService,
|
||||||
IContainerExtension container,
|
IContainerExtension container,
|
||||||
IRegionManager regionManager) : base(container, regionManager)
|
IRegionManager regionManager) : base(container, regionManager)
|
||||||
{
|
{
|
||||||
_weightRecordService = weightRecordService;
|
_weightRecordService = weightRecordService;
|
||||||
_dictSyncService = dictSyncService;
|
_dictSyncService = dictSyncService;
|
||||||
|
_mixerMaterialService = mixerMaterialService;
|
||||||
SaveCommand = new DelegateCommand(async () => await SaveAsync());
|
SaveCommand = new DelegateCommand(async () => await SaveAsync());
|
||||||
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
|
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
|
||||||
|
OpenMixerMaterialPickerCommand = new DelegateCommand(async () => await OpenMixerMaterialPickerAsync());
|
||||||
|
ClearMixerMaterialCommand = new DelegateCommand(ClearMixerMaterialSelection);
|
||||||
_ = LoadDictOptionsAsync();
|
_ = LoadDictOptionsAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OpenMixerMaterialPickerAsync()
|
||||||
|
{
|
||||||
|
MixerMaterialPickerDialogViewModel? pickerVm = null;
|
||||||
|
bool confirmed;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
confirmed = await HandyControl.Controls.Dialog.Show<MixerMaterialPickerDialogView>()
|
||||||
|
.Initialize<MixerMaterialPickerDialogViewModel>(vm => { pickerVm = vm; })
|
||||||
|
.GetResultAsync<bool>();
|
||||||
|
}
|
||||||
|
catch { return; }
|
||||||
|
|
||||||
|
if (!confirmed || pickerVm == null) return;
|
||||||
|
var selected = pickerVm.SelectedMaterials;
|
||||||
|
if (selected.Count == 0) return;
|
||||||
|
|
||||||
|
_mixerMaterialIds = string.Join(";", selected.Select(m => m.Source.Id ?? ""));
|
||||||
|
_mixerMaterialNames = string.Join(";", selected.Select(m => m.Source.MaterialName ?? ""));
|
||||||
|
RaisePropertyChanged(nameof(MixerMaterialDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedMixerMaterials));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearMixerMaterialSelection()
|
||||||
|
{
|
||||||
|
_mixerMaterialIds = null;
|
||||||
|
_mixerMaterialNames = null;
|
||||||
|
RaisePropertyChanged(nameof(MixerMaterialDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedMixerMaterials));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task LoadDictOptionsAsync()
|
private async Task LoadDictOptionsAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -69,7 +116,8 @@ public class WeightRecordEditDialogViewModel : BaseViewModel, IDialogResultable<
|
|||||||
Record = new MesXslWeightRecord
|
Record = new MesXslWeightRecord
|
||||||
{
|
{
|
||||||
WeighDate = DateTime.Today,
|
WeighDate = DateTime.Today,
|
||||||
InoutDirection = "1"
|
InoutDirection = "1",
|
||||||
|
MaterialType = "1"
|
||||||
};
|
};
|
||||||
RaisePropertyChanged(nameof(IsAddMode));
|
RaisePropertyChanged(nameof(IsAddMode));
|
||||||
RaisePropertyChanged(nameof(DialogTitle));
|
RaisePropertyChanged(nameof(DialogTitle));
|
||||||
@@ -87,16 +135,20 @@ public class WeightRecordEditDialogViewModel : BaseViewModel, IDialogResultable<
|
|||||||
PlateNumber = record.PlateNumber,
|
PlateNumber = record.PlateNumber,
|
||||||
SenderUnit = record.SenderUnit,
|
SenderUnit = record.SenderUnit,
|
||||||
ReceiverUnit = record.ReceiverUnit,
|
ReceiverUnit = record.ReceiverUnit,
|
||||||
GoodsName = record.GoodsName,
|
|
||||||
GrossWeight = record.GrossWeight,
|
GrossWeight = record.GrossWeight,
|
||||||
TareWeight = record.TareWeight,
|
TareWeight = record.TareWeight,
|
||||||
NetWeight = record.NetWeight,
|
NetWeight = record.NetWeight,
|
||||||
DriverName = record.DriverName,
|
DriverName = record.DriverName,
|
||||||
DriverPhone = record.DriverPhone,
|
DriverPhone = record.DriverPhone,
|
||||||
BillType = record.BillType,
|
BillType = record.BillType,
|
||||||
|
MaterialType = record.MaterialType,
|
||||||
TenantId = record.TenantId,
|
TenantId = record.TenantId,
|
||||||
UpdateTime = record.UpdateTime
|
UpdateTime = record.UpdateTime
|
||||||
};
|
};
|
||||||
|
_mixerMaterialIds = record.MixerMaterialIds;
|
||||||
|
_mixerMaterialNames = record.MixerMaterialNames;
|
||||||
|
RaisePropertyChanged(nameof(MixerMaterialDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedMixerMaterials));
|
||||||
RaisePropertyChanged(nameof(IsAddMode));
|
RaisePropertyChanged(nameof(IsAddMode));
|
||||||
RaisePropertyChanged(nameof(DialogTitle));
|
RaisePropertyChanged(nameof(DialogTitle));
|
||||||
}
|
}
|
||||||
@@ -120,6 +172,12 @@ public class WeightRecordEditDialogViewModel : BaseViewModel, IDialogResultable<
|
|||||||
if (Record.GrossWeight.HasValue && Record.TareWeight.HasValue)
|
if (Record.GrossWeight.HasValue && Record.TareWeight.HasValue)
|
||||||
Record.NetWeight = Math.Round(Record.GrossWeight.Value - Record.TareWeight.Value, 2);
|
Record.NetWeight = Math.Round(Record.GrossWeight.Value - Record.TareWeight.Value, 2);
|
||||||
|
|
||||||
|
// 写入密炼物料
|
||||||
|
Record.MixerMaterialIds = _mixerMaterialIds;
|
||||||
|
Record.MixerMaterialNames = _mixerMaterialNames;
|
||||||
|
if (string.IsNullOrWhiteSpace(Record.MaterialType))
|
||||||
|
Record.MaterialType = "1";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
|
|||||||
@@ -47,14 +47,15 @@ public class WeightRecordListViewModel : BaseViewModel
|
|||||||
private string? _filterInoutDirection;
|
private string? _filterInoutDirection;
|
||||||
public string? FilterInoutDirection { get => _filterInoutDirection; set => SetProperty(ref _filterInoutDirection, value); }
|
public string? FilterInoutDirection { get => _filterInoutDirection; set => SetProperty(ref _filterInoutDirection, value); }
|
||||||
|
|
||||||
private string? _filterGoodsName;
|
|
||||||
public string? FilterGoodsName { get => _filterGoodsName; set => SetProperty(ref _filterGoodsName, value); }
|
|
||||||
|
|
||||||
private string? _filterDriverName;
|
private string? _filterDriverName;
|
||||||
public string? FilterDriverName { get => _filterDriverName; set => SetProperty(ref _filterDriverName, value); }
|
public string? FilterDriverName { get => _filterDriverName; set => SetProperty(ref _filterDriverName, value); }
|
||||||
|
|
||||||
|
private string? _filterMixerMaterialName;
|
||||||
|
public string? FilterMixerMaterialName { get => _filterMixerMaterialName; set => SetProperty(ref _filterMixerMaterialName, value); }
|
||||||
|
|
||||||
public ObservableCollection<KeyValuePair<string, string>> InoutDirectionOptions { get; } = new();
|
public ObservableCollection<KeyValuePair<string, string>> InoutDirectionOptions { get; } = new();
|
||||||
public ObservableCollection<KeyValuePair<string, string>> BillTypeOptions { get; } = new();
|
public ObservableCollection<KeyValuePair<string, string>> BillTypeOptions { get; } = new();
|
||||||
|
public ObservableCollection<KeyValuePair<string, string>> MaterialTypeOptions { get; } = new();
|
||||||
|
|
||||||
public DelegateCommand SearchCommand { get; }
|
public DelegateCommand SearchCommand { get; }
|
||||||
public DelegateCommand ResetCommand { get; }
|
public DelegateCommand ResetCommand { get; }
|
||||||
@@ -79,7 +80,7 @@ public class WeightRecordListViewModel : BaseViewModel
|
|||||||
ResetCommand = new DelegateCommand(async () =>
|
ResetCommand = new DelegateCommand(async () =>
|
||||||
{
|
{
|
||||||
FilterBillNo = null; FilterPlateNumber = null; FilterInoutDirection = null;
|
FilterBillNo = null; FilterPlateNumber = null; FilterInoutDirection = null;
|
||||||
FilterGoodsName = null; FilterDriverName = null;
|
FilterDriverName = null; FilterMixerMaterialName = null;
|
||||||
PageNo = 1;
|
PageNo = 1;
|
||||||
await LoadAsync();
|
await LoadAsync();
|
||||||
});
|
});
|
||||||
@@ -133,6 +134,11 @@ public class WeightRecordListViewModel : BaseViewModel
|
|||||||
BillTypeOptions.Clear();
|
BillTypeOptions.Clear();
|
||||||
foreach (var item in billTypeOptions) BillTypeOptions.Add(item);
|
foreach (var item in billTypeOptions) BillTypeOptions.Add(item);
|
||||||
if (BillTypeOptions.Count == 0) AddDefaultBillTypeOptions();
|
if (BillTypeOptions.Count == 0) AddDefaultBillTypeOptions();
|
||||||
|
|
||||||
|
var materialTypeOptions = await _dictSyncService.GetDictOptionsAsync("xslmes_weight_material_type", includeAll: true);
|
||||||
|
MaterialTypeOptions.Clear();
|
||||||
|
foreach (var item in materialTypeOptions) MaterialTypeOptions.Add(item);
|
||||||
|
if (MaterialTypeOptions.Count == 0) AddDefaultMaterialTypeOptions();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -141,6 +147,7 @@ public class WeightRecordListViewModel : BaseViewModel
|
|||||||
InoutDirectionOptions.Add(new KeyValuePair<string, string>("进厂", "1"));
|
InoutDirectionOptions.Add(new KeyValuePair<string, string>("进厂", "1"));
|
||||||
InoutDirectionOptions.Add(new KeyValuePair<string, string>("出厂", "2"));
|
InoutDirectionOptions.Add(new KeyValuePair<string, string>("出厂", "2"));
|
||||||
AddDefaultBillTypeOptions();
|
AddDefaultBillTypeOptions();
|
||||||
|
AddDefaultMaterialTypeOptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,21 +160,31 @@ public class WeightRecordListViewModel : BaseViewModel
|
|||||||
BillTypeOptions.Add(new KeyValuePair<string, string>("已称皮重", "3"));
|
BillTypeOptions.Add(new KeyValuePair<string, string>("已称皮重", "3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddDefaultMaterialTypeOptions()
|
||||||
|
{
|
||||||
|
MaterialTypeOptions.Clear();
|
||||||
|
MaterialTypeOptions.Add(new KeyValuePair<string, string>("全部", ""));
|
||||||
|
MaterialTypeOptions.Add(new KeyValuePair<string, string>("自动", "1"));
|
||||||
|
MaterialTypeOptions.Add(new KeyValuePair<string, string>("手动", "2"));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task LoadAsync()
|
public async Task LoadAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IsLoading = true;
|
IsLoading = true;
|
||||||
var result = await _weightRecordService.PageAsync(
|
var result = await _weightRecordService.PageAsync(
|
||||||
PageNo, PageSize, FilterBillNo, FilterPlateNumber, FilterInoutDirection, FilterGoodsName, FilterDriverName);
|
PageNo, PageSize, FilterBillNo, FilterPlateNumber, FilterInoutDirection, filterDriverName: FilterDriverName, filterMixerMaterialName: FilterMixerMaterialName);
|
||||||
|
|
||||||
// 填充字典显示文本
|
// 填充字典显示文本
|
||||||
var dictMap = InoutDirectionOptions.ToDictionary(x => x.Value, x => x.Key);
|
var dictMap = InoutDirectionOptions.ToDictionary(x => x.Value, x => x.Key);
|
||||||
var billTypeMap = BillTypeOptions.ToDictionary(x => x.Value, x => x.Key);
|
var billTypeMap = BillTypeOptions.ToDictionary(x => x.Value, x => x.Key);
|
||||||
|
var materialTypeMap = MaterialTypeOptions.ToDictionary(x => x.Value, x => x.Key);
|
||||||
foreach (var r in result.Records)
|
foreach (var r in result.Records)
|
||||||
{
|
{
|
||||||
r.InoutDirectionText = dictMap.TryGetValue(r.InoutDirection ?? "", out var txt) ? txt : r.InoutDirection ?? "";
|
r.InoutDirectionText = dictMap.TryGetValue(r.InoutDirection ?? "", out var txt) ? txt : r.InoutDirection ?? "";
|
||||||
r.BillTypeText = billTypeMap.TryGetValue(r.BillType ?? "", out var billTypeTxt) ? billTypeTxt : r.BillType ?? "";
|
r.BillTypeText = billTypeMap.TryGetValue(r.BillType ?? "", out var billTypeTxt) ? billTypeTxt : r.BillType ?? "";
|
||||||
|
r.MaterialTypeText = materialTypeMap.TryGetValue(r.MaterialType ?? "", out var materialTypeTxt) ? materialTypeTxt : r.MaterialType ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Records = new ObservableCollection<MesXslWeightRecord>(result.Records);
|
Records = new ObservableCollection<MesXslWeightRecord>(result.Records);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using HandyControl.Controls;
|
using HandyControl.Controls;
|
||||||
using HandyControl.Tools.Extension;
|
using HandyControl.Tools.Extension;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using YY.Admin.Core;
|
using YY.Admin.Core;
|
||||||
using YY.Admin.Core.Entity;
|
using YY.Admin.Core.Entity;
|
||||||
@@ -19,6 +21,8 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
private readonly IWeightRecordService _weightRecordService;
|
private readonly IWeightRecordService _weightRecordService;
|
||||||
private readonly IJeecgDictSyncService _dictSyncService;
|
private readonly IJeecgDictSyncService _dictSyncService;
|
||||||
private readonly IVehicleService _vehicleService;
|
private readonly IVehicleService _vehicleService;
|
||||||
|
private readonly IMixerMaterialService _mixerMaterialService;
|
||||||
|
private readonly string _unitCacheFilePath;
|
||||||
|
|
||||||
// ─── 车辆档案匹配 ───
|
// ─── 车辆档案匹配 ───
|
||||||
private MesXslVehicle? _matchedVehicle;
|
private MesXslVehicle? _matchedVehicle;
|
||||||
@@ -115,6 +119,7 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
{
|
{
|
||||||
ClearFormByDirectionSwitch();
|
ClearFormByDirectionSwitch();
|
||||||
}
|
}
|
||||||
|
TryApplyCachedUnitByDirection(value);
|
||||||
_ = LoadRecentWeightRecordsAsync(PlateNumber);
|
_ = LoadRecentWeightRecordsAsync(PlateNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,6 +147,8 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
|
|
||||||
private MesXslSupplier? _selectedSupplier;
|
private MesXslSupplier? _selectedSupplier;
|
||||||
private MesXslCustomer? _selectedCustomer;
|
private MesXslCustomer? _selectedCustomer;
|
||||||
|
private string? _cachedInboundReceiverUnit;
|
||||||
|
private string? _cachedOutboundSenderUnit;
|
||||||
|
|
||||||
public bool HasSelectedSupplier => _selectedSupplier != null;
|
public bool HasSelectedSupplier => _selectedSupplier != null;
|
||||||
public bool HasSelectedCustomer => _selectedCustomer != null;
|
public bool HasSelectedCustomer => _selectedCustomer != null;
|
||||||
@@ -176,8 +183,52 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? _goodsName;
|
// ─── 密炼物料选择 ───
|
||||||
public string? GoodsName { get => _goodsName; set => SetProperty(ref _goodsName, value); }
|
|
||||||
|
private string? _mixerMaterialIds;
|
||||||
|
private string? _mixerMaterialNames;
|
||||||
|
private bool _isMixerMaterialManual;
|
||||||
|
|
||||||
|
public bool IsMixerMaterialManual
|
||||||
|
{
|
||||||
|
get => _isMixerMaterialManual;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!SetProperty(ref _isMixerMaterialManual, value)) return;
|
||||||
|
// 切换为手动填写时,清空 ID,避免保存时出现“名称手填 + ID遗留”不一致
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
_mixerMaterialIds = null;
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedMixerMaterials));
|
||||||
|
}
|
||||||
|
RaisePropertyChanged(nameof(IsMixerMaterialReadOnly));
|
||||||
|
OpenMixerMaterialPickerCommand.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMixerMaterialReadOnly => !IsMixerMaterialManual;
|
||||||
|
|
||||||
|
public string? MixerMaterialNames
|
||||||
|
{
|
||||||
|
get => _mixerMaterialNames;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!SetProperty(ref _mixerMaterialNames, value)) return;
|
||||||
|
// 手动填写时,名称由人工输入,ID 不再有效
|
||||||
|
if (IsMixerMaterialManual)
|
||||||
|
{
|
||||||
|
_mixerMaterialIds = null;
|
||||||
|
}
|
||||||
|
RaisePropertyChanged(nameof(MixerMaterialDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedMixerMaterials));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string MixerMaterialDisplay => !string.IsNullOrWhiteSpace(_mixerMaterialNames)
|
||||||
|
? _mixerMaterialNames
|
||||||
|
: "点击右侧「选择」选取密炼物料(可多选)";
|
||||||
|
|
||||||
|
public bool HasSelectedMixerMaterials => !string.IsNullOrWhiteSpace(_mixerMaterialNames);
|
||||||
|
|
||||||
private double? _grossWeight;
|
private double? _grossWeight;
|
||||||
public double? GrossWeight
|
public double? GrossWeight
|
||||||
@@ -234,19 +285,30 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
public DelegateCommand UseDetectedPlateCommand { get; }
|
public DelegateCommand UseDetectedPlateCommand { get; }
|
||||||
public DelegateCommand OpenSupplierPickerCommand { get; }
|
public DelegateCommand OpenSupplierPickerCommand { get; }
|
||||||
public DelegateCommand OpenCustomerPickerCommand { get; }
|
public DelegateCommand OpenCustomerPickerCommand { get; }
|
||||||
|
public DelegateCommand OpenMixerMaterialPickerCommand { get; }
|
||||||
public DelegateCommand ClearSupplierCommand { get; }
|
public DelegateCommand ClearSupplierCommand { get; }
|
||||||
public DelegateCommand ClearCustomerCommand { get; }
|
public DelegateCommand ClearCustomerCommand { get; }
|
||||||
|
public DelegateCommand ClearMixerMaterialCommand { get; }
|
||||||
|
|
||||||
public WeightRecordOperationViewModel(
|
public WeightRecordOperationViewModel(
|
||||||
IWeightRecordService weightRecordService,
|
IWeightRecordService weightRecordService,
|
||||||
IJeecgDictSyncService dictSyncService,
|
IJeecgDictSyncService dictSyncService,
|
||||||
IVehicleService vehicleService,
|
IVehicleService vehicleService,
|
||||||
|
IMixerMaterialService mixerMaterialService,
|
||||||
IContainerExtension container,
|
IContainerExtension container,
|
||||||
IRegionManager regionManager) : base(container, regionManager)
|
IRegionManager regionManager) : base(container, regionManager)
|
||||||
{
|
{
|
||||||
_weightRecordService = weightRecordService;
|
_weightRecordService = weightRecordService;
|
||||||
_dictSyncService = dictSyncService;
|
_dictSyncService = dictSyncService;
|
||||||
_vehicleService = vehicleService;
|
_vehicleService = vehicleService;
|
||||||
|
_mixerMaterialService = mixerMaterialService;
|
||||||
|
var cacheDir = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
|
"YY.Admin",
|
||||||
|
"cache");
|
||||||
|
Directory.CreateDirectory(cacheDir);
|
||||||
|
_unitCacheFilePath = Path.Combine(cacheDir, "weight-record-unit-cache.json");
|
||||||
|
LoadUnitCache();
|
||||||
|
|
||||||
CaptureGrossWeightCommand = new DelegateCommand(CaptureGrossWeight, CanCaptureGrossWeight)
|
CaptureGrossWeightCommand = new DelegateCommand(CaptureGrossWeight, CanCaptureGrossWeight)
|
||||||
.ObservesProperty(() => IsWeightStable)
|
.ObservesProperty(() => IsWeightStable)
|
||||||
@@ -262,11 +324,15 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
.ObservesProperty(() => HasPlate);
|
.ObservesProperty(() => HasPlate);
|
||||||
OpenSupplierPickerCommand = new DelegateCommand(async () => await OpenSupplierPickerAsync());
|
OpenSupplierPickerCommand = new DelegateCommand(async () => await OpenSupplierPickerAsync());
|
||||||
OpenCustomerPickerCommand = new DelegateCommand(async () => await OpenCustomerPickerAsync());
|
OpenCustomerPickerCommand = new DelegateCommand(async () => await OpenCustomerPickerAsync());
|
||||||
|
OpenMixerMaterialPickerCommand = new DelegateCommand(async () => await OpenMixerMaterialPickerAsync(), () => !IsMixerMaterialManual)
|
||||||
|
.ObservesProperty(() => IsMixerMaterialManual);
|
||||||
ClearSupplierCommand = new DelegateCommand(ClearSupplierSelection);
|
ClearSupplierCommand = new DelegateCommand(ClearSupplierSelection);
|
||||||
ClearCustomerCommand = new DelegateCommand(ClearCustomerSelection);
|
ClearCustomerCommand = new DelegateCommand(ClearCustomerSelection);
|
||||||
|
ClearMixerMaterialCommand = new DelegateCommand(ClearMixerMaterialSelection);
|
||||||
|
|
||||||
_ = LoadDictOptionsAsync();
|
_ = LoadDictOptionsAsync();
|
||||||
_ = LoadRecentWeightRecordsAsync(PlateNumber);
|
_ = LoadRecentWeightRecordsAsync(PlateNumber);
|
||||||
|
TryApplyCachedUnitByDirection(InoutDirection);
|
||||||
|
|
||||||
// 启动串口模拟定时器(200ms 刷新一次,约 5Hz)
|
// 启动串口模拟定时器(200ms 刷新一次,约 5Hz)
|
||||||
_serialTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };
|
_serialTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };
|
||||||
@@ -447,12 +513,15 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
PlateNumber = PlateNumber,
|
PlateNumber = PlateNumber,
|
||||||
SenderUnit = SenderUnit,
|
SenderUnit = SenderUnit,
|
||||||
ReceiverUnit = ReceiverUnit,
|
ReceiverUnit = ReceiverUnit,
|
||||||
GoodsName = GoodsName,
|
|
||||||
GrossWeight = GrossWeight,
|
GrossWeight = GrossWeight,
|
||||||
TareWeight = TareWeight,
|
TareWeight = TareWeight,
|
||||||
NetWeight = NetWeight,
|
NetWeight = NetWeight,
|
||||||
DriverName = DriverName,
|
DriverName = DriverName,
|
||||||
DriverPhone = DriverPhone
|
DriverPhone = DriverPhone,
|
||||||
|
MixerMaterialIds = _mixerMaterialIds,
|
||||||
|
MixerMaterialNames = _mixerMaterialNames,
|
||||||
|
// 勾选“手动”=2;未勾选=1(自动)
|
||||||
|
MaterialType = IsMixerMaterialManual ? "2" : "1"
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -462,6 +531,7 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
: await _weightRecordService.AddAsync(entity);
|
: await _weightRecordService.AddAsync(entity);
|
||||||
if (ok)
|
if (ok)
|
||||||
{
|
{
|
||||||
|
CacheUnitByDirection();
|
||||||
if (isCompleteSelectedRecord)
|
if (isCompleteSelectedRecord)
|
||||||
{
|
{
|
||||||
Growl.Success("皮重回填成功,单据已更新为称重完成!");
|
Growl.Success("皮重回填成功,单据已更新为称重完成!");
|
||||||
@@ -482,11 +552,14 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
SelectedRecentWeightRecord = null;
|
SelectedRecentWeightRecord = null;
|
||||||
IsPlateNumberLocked = false;
|
IsPlateNumberLocked = false;
|
||||||
PlateNumber = null; DetectedPlate = string.Empty; HasPlate = false;
|
PlateNumber = null; DetectedPlate = string.Empty; HasPlate = false;
|
||||||
SenderUnit = null; ReceiverUnit = null; GoodsName = null;
|
SenderUnit = null; ReceiverUnit = null;
|
||||||
DriverName = null; DriverPhone = null;
|
DriverName = null; DriverPhone = null;
|
||||||
ClearSupplierSelection();
|
ClearSupplierSelection();
|
||||||
ClearCustomerSelection();
|
ClearCustomerSelection();
|
||||||
|
ClearMixerMaterialSelection();
|
||||||
ClearVehicleMatch();
|
ClearVehicleMatch();
|
||||||
|
// 保存后立即按当前方向回填缓存,避免必须重进页面才显示
|
||||||
|
TryApplyCachedUnitByDirection(InoutDirection);
|
||||||
RaisePropertyChanged(nameof(NetWeightDisplay));
|
RaisePropertyChanged(nameof(NetWeightDisplay));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -511,7 +584,7 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
WeighDate = DateTime.Today;
|
WeighDate = DateTime.Today;
|
||||||
InoutDirection = "1";
|
InoutDirection = "1";
|
||||||
PlateNumber = null; SenderUnit = null; ReceiverUnit = null;
|
PlateNumber = null; SenderUnit = null; ReceiverUnit = null;
|
||||||
GoodsName = null; GrossWeight = null; TareWeight = null; NetWeight = null;
|
GrossWeight = null; TareWeight = null; NetWeight = null;
|
||||||
DriverName = null; DriverPhone = null;
|
DriverName = null; DriverPhone = null;
|
||||||
GrossWeightCaptured = false; TareWeightCaptured = false;
|
GrossWeightCaptured = false; TareWeightCaptured = false;
|
||||||
DetectedPlate = string.Empty; HasPlate = false;
|
DetectedPlate = string.Empty; HasPlate = false;
|
||||||
@@ -519,11 +592,123 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
IsPlateNumberLocked = false;
|
IsPlateNumberLocked = false;
|
||||||
ClearSupplierSelection();
|
ClearSupplierSelection();
|
||||||
ClearCustomerSelection();
|
ClearCustomerSelection();
|
||||||
|
ClearMixerMaterialSelection();
|
||||||
ClearVehicleMatch();
|
ClearVehicleMatch();
|
||||||
RaisePropertyChanged(nameof(NetWeightDisplay));
|
RaisePropertyChanged(nameof(NetWeightDisplay));
|
||||||
AddLog("表单已清空");
|
AddLog("表单已清空");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CacheUnitByDirection()
|
||||||
|
{
|
||||||
|
var changed = false;
|
||||||
|
// 按需求:进厂缓存收货单位;出厂缓存发货单位
|
||||||
|
if (string.Equals(InoutDirection, "1", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(ReceiverUnit) && !string.Equals(_cachedInboundReceiverUnit, ReceiverUnit, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_cachedInboundReceiverUnit = ReceiverUnit;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (string.Equals(InoutDirection, "2", StringComparison.Ordinal)
|
||||||
|
&& !string.IsNullOrWhiteSpace(SenderUnit)
|
||||||
|
&& !string.Equals(_cachedOutboundSenderUnit, SenderUnit, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_cachedOutboundSenderUnit = SenderUnit;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
SaveUnitCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryApplyCachedUnitByDirection(string? inoutDirection)
|
||||||
|
{
|
||||||
|
// 切换方向时动态切换缓存展示:
|
||||||
|
// 进厂只展示“收货单位缓存”,出厂只展示“发货单位缓存”
|
||||||
|
if (string.Equals(inoutDirection, "1", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
// 进厂:清空发货单位展示
|
||||||
|
_selectedSupplier = null;
|
||||||
|
SenderUnit = null;
|
||||||
|
RaisePropertyChanged(nameof(SenderUnitDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedSupplier));
|
||||||
|
|
||||||
|
// 进厂:带出收货单位缓存(无缓存则清空)
|
||||||
|
if (!string.IsNullOrWhiteSpace(_cachedInboundReceiverUnit))
|
||||||
|
{
|
||||||
|
ReceiverUnit = _cachedInboundReceiverUnit;
|
||||||
|
AddLog($"已带出进厂收货单位缓存:{_cachedInboundReceiverUnit}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_selectedCustomer = null;
|
||||||
|
ReceiverUnit = null;
|
||||||
|
RaisePropertyChanged(nameof(ReceiverUnitDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedCustomer));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (string.Equals(inoutDirection, "2", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
// 出厂:清空收货单位展示
|
||||||
|
_selectedCustomer = null;
|
||||||
|
ReceiverUnit = null;
|
||||||
|
RaisePropertyChanged(nameof(ReceiverUnitDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedCustomer));
|
||||||
|
|
||||||
|
// 出厂:带出发货单位缓存(无缓存则清空)
|
||||||
|
if (!string.IsNullOrWhiteSpace(_cachedOutboundSenderUnit))
|
||||||
|
{
|
||||||
|
SenderUnit = _cachedOutboundSenderUnit;
|
||||||
|
AddLog($"已带出出厂发货单位缓存:{_cachedOutboundSenderUnit}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_selectedSupplier = null;
|
||||||
|
SenderUnit = null;
|
||||||
|
RaisePropertyChanged(nameof(SenderUnitDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedSupplier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadUnitCache()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(_unitCacheFilePath)) return;
|
||||||
|
var json = File.ReadAllText(_unitCacheFilePath);
|
||||||
|
var cache = JsonSerializer.Deserialize<UnitDirectionCache>(json);
|
||||||
|
if (cache == null) return;
|
||||||
|
_cachedInboundReceiverUnit = string.IsNullOrWhiteSpace(cache.InboundReceiverUnit) ? null : cache.InboundReceiverUnit;
|
||||||
|
_cachedOutboundSenderUnit = string.IsNullOrWhiteSpace(cache.OutboundSenderUnit) ? null : cache.OutboundSenderUnit;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 缓存读取失败不影响主流程
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveUnitCache()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cache = new UnitDirectionCache
|
||||||
|
{
|
||||||
|
InboundReceiverUnit = _cachedInboundReceiverUnit,
|
||||||
|
OutboundSenderUnit = _cachedOutboundSenderUnit,
|
||||||
|
};
|
||||||
|
var json = JsonSerializer.Serialize(cache);
|
||||||
|
File.WriteAllText(_unitCacheFilePath, json);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 缓存写入失败不影响主流程
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task OpenSupplierPickerAsync()
|
private async Task OpenSupplierPickerAsync()
|
||||||
{
|
{
|
||||||
SupplierPickerDialogViewModel? pickerVm = null;
|
SupplierPickerDialogViewModel? pickerVm = null;
|
||||||
@@ -582,6 +767,42 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
RaisePropertyChanged(nameof(HasSelectedCustomer));
|
RaisePropertyChanged(nameof(HasSelectedCustomer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OpenMixerMaterialPickerAsync()
|
||||||
|
{
|
||||||
|
MixerMaterialPickerDialogViewModel? pickerVm = null;
|
||||||
|
bool confirmed;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
confirmed = await HandyControl.Controls.Dialog.Show<MixerMaterialPickerDialogView>()
|
||||||
|
.Initialize<MixerMaterialPickerDialogViewModel>(vm => { pickerVm = vm; })
|
||||||
|
.GetResultAsync<bool>();
|
||||||
|
}
|
||||||
|
catch { return; }
|
||||||
|
|
||||||
|
if (!confirmed || pickerVm == null) return;
|
||||||
|
var selected = pickerVm.SelectedMaterials;
|
||||||
|
if (selected.Count == 0) return;
|
||||||
|
|
||||||
|
_mixerMaterialIds = string.Join(";", selected.Select(m => m.Source.Id ?? ""));
|
||||||
|
MixerMaterialNames = string.Join(";", selected.Select(m => m.Source.MaterialName ?? ""));
|
||||||
|
if (IsMixerMaterialManual)
|
||||||
|
{
|
||||||
|
IsMixerMaterialManual = false;
|
||||||
|
}
|
||||||
|
RaisePropertyChanged(nameof(MixerMaterialDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedMixerMaterials));
|
||||||
|
AddLog($"已选密炼物料:{_mixerMaterialNames}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearMixerMaterialSelection()
|
||||||
|
{
|
||||||
|
_mixerMaterialIds = null;
|
||||||
|
MixerMaterialNames = null;
|
||||||
|
IsMixerMaterialManual = false;
|
||||||
|
RaisePropertyChanged(nameof(MixerMaterialDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedMixerMaterials));
|
||||||
|
}
|
||||||
|
|
||||||
private void AddLog(string message)
|
private void AddLog(string message)
|
||||||
{
|
{
|
||||||
var entry = $"{DateTime.Now:HH:mm:ss} {message}";
|
var entry = $"{DateTime.Now:HH:mm:ss} {message}";
|
||||||
@@ -681,9 +902,13 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
RaisePropertyChanged(nameof(ReceiverUnitDisplay));
|
RaisePropertyChanged(nameof(ReceiverUnitDisplay));
|
||||||
RaisePropertyChanged(nameof(HasSelectedSupplier));
|
RaisePropertyChanged(nameof(HasSelectedSupplier));
|
||||||
RaisePropertyChanged(nameof(HasSelectedCustomer));
|
RaisePropertyChanged(nameof(HasSelectedCustomer));
|
||||||
GoodsName = selected.Source.GoodsName;
|
|
||||||
DriverName = selected.Source.DriverName;
|
DriverName = selected.Source.DriverName;
|
||||||
DriverPhone = selected.Source.DriverPhone;
|
DriverPhone = selected.Source.DriverPhone;
|
||||||
|
_mixerMaterialIds = selected.Source.MixerMaterialIds;
|
||||||
|
MixerMaterialNames = selected.Source.MixerMaterialNames;
|
||||||
|
IsMixerMaterialManual = string.Equals(selected.Source.MaterialType, "2", StringComparison.Ordinal);
|
||||||
|
RaisePropertyChanged(nameof(MixerMaterialDisplay));
|
||||||
|
RaisePropertyChanged(nameof(HasSelectedMixerMaterials));
|
||||||
|
|
||||||
var isOutbound = string.Equals(InoutDirection, "2", StringComparison.Ordinal);
|
var isOutbound = string.Equals(InoutDirection, "2", StringComparison.Ordinal);
|
||||||
if (isOutbound)
|
if (isOutbound)
|
||||||
@@ -715,7 +940,6 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
PlateNumber = null;
|
PlateNumber = null;
|
||||||
SenderUnit = null;
|
SenderUnit = null;
|
||||||
ReceiverUnit = null;
|
ReceiverUnit = null;
|
||||||
GoodsName = null;
|
|
||||||
DriverName = null;
|
DriverName = null;
|
||||||
DriverPhone = null;
|
DriverPhone = null;
|
||||||
GrossWeight = null;
|
GrossWeight = null;
|
||||||
@@ -727,6 +951,7 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
IsPlateNumberLocked = false;
|
IsPlateNumberLocked = false;
|
||||||
ClearSupplierSelection();
|
ClearSupplierSelection();
|
||||||
ClearCustomerSelection();
|
ClearCustomerSelection();
|
||||||
|
ClearMixerMaterialSelection();
|
||||||
ClearVehicleMatch();
|
ClearVehicleMatch();
|
||||||
RaisePropertyChanged(nameof(NetWeightDisplay));
|
RaisePropertyChanged(nameof(NetWeightDisplay));
|
||||||
AddLog("已切换进出方向,当前表单数据已清空");
|
AddLog("已切换进出方向,当前表单数据已清空");
|
||||||
@@ -889,6 +1114,12 @@ public class WeightRecordOperationViewModel : BaseViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class UnitDirectionCache
|
||||||
|
{
|
||||||
|
public string? InboundReceiverUnit { get; set; }
|
||||||
|
public string? OutboundSenderUnit { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class WeightRecordSimpleItem
|
public class WeightRecordSimpleItem
|
||||||
{
|
{
|
||||||
public MesXslWeightRecord? Source { get; set; }
|
public MesXslWeightRecord? Source { get; set; }
|
||||||
|
|||||||
@@ -96,7 +96,62 @@
|
|||||||
</hc:UniformSpacingPanel>
|
</hc:UniformSpacingPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<DataGrid Grid.Row="2"
|
<Grid Grid.Row="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="220"/>
|
||||||
|
<ColumnDefinition Width="10"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Border Grid.Column="0" BorderBrush="#FFEDEDED" BorderThickness="1" CornerRadius="4">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Border Grid.Row="0"
|
||||||
|
Background="#FFF5F5F5"
|
||||||
|
BorderBrush="#FFEDEDED"
|
||||||
|
BorderThickness="0 0 0 1"
|
||||||
|
Padding="10 7"
|
||||||
|
CornerRadius="4 4 0 0">
|
||||||
|
<Grid>
|
||||||
|
<TextBlock Text="物料分类"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
FontSize="12"
|
||||||
|
Foreground="#FF333333"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
<Button HorizontalAlignment="Right"
|
||||||
|
Command="{Binding ToggleExpandCommand}"
|
||||||
|
Style="{StaticResource ButtonText}"
|
||||||
|
Padding="4 2">
|
||||||
|
<TextBlock Text="{Binding ToggleExpandButtonText}" FontSize="11"/>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<TreeView Grid.Row="1"
|
||||||
|
x:Name="CategoryTreeView"
|
||||||
|
Padding="4"
|
||||||
|
BorderThickness="0"
|
||||||
|
ItemsSource="{Binding TreeNodes}"
|
||||||
|
SelectedItemChanged="CategoryTreeView_SelectedItemChanged">
|
||||||
|
<TreeView.ItemContainerStyle>
|
||||||
|
<Style TargetType="TreeViewItem" BasedOn="{StaticResource TreeViewItemBaseStyle}">
|
||||||
|
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
|
||||||
|
</Style>
|
||||||
|
</TreeView.ItemContainerStyle>
|
||||||
|
<TreeView.ItemTemplate>
|
||||||
|
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
|
||||||
|
<TextBlock Text="{Binding DisplayName}" Padding="2"/>
|
||||||
|
</HierarchicalDataTemplate>
|
||||||
|
</TreeView.ItemTemplate>
|
||||||
|
</TreeView>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<DataGrid Grid.Column="2"
|
||||||
ItemsSource="{Binding Materials}"
|
ItemsSource="{Binding Materials}"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
@@ -160,6 +215,7 @@
|
|||||||
</DataGridTemplateColumn>
|
</DataGridTemplateColumn>
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
|
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
|
||||||
<TextBlock Text="{Binding Total, StringFormat=共 {0} 条}" VerticalAlignment="Center" Margin="0,0,16,0"
|
<TextBlock Text="{Binding Total, StringFormat=共 {0} 条}" VerticalAlignment="Center" Margin="0,0,16,0"
|
||||||
@@ -171,4 +227,3 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using YY.Admin.ViewModels.MixerMaterial;
|
||||||
|
|
||||||
namespace YY.Admin.Views.MixerMaterial;
|
namespace YY.Admin.Views.MixerMaterial;
|
||||||
|
|
||||||
@@ -8,5 +10,10 @@ public partial class MixerMaterialListView : UserControl
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
private void CategoryTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||||
|
{
|
||||||
|
if (DataContext is MixerMaterialListViewModel vm && e.NewValue is MixerMaterialListViewModel.CategoryFilterNode node)
|
||||||
|
_ = vm.OnTreeNodeSelectedAsync(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,12 +65,6 @@
|
|||||||
<TextBlock Text="重置" Style="{StaticResource IconButtonStyle}"/>
|
<TextBlock Text="重置" Style="{StaticResource IconButtonStyle}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
<Button Style="{StaticResource ButtonSuccess}" Command="{Binding SyncCommand}">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<md:PackIcon Kind="CloudSyncOutline"/>
|
|
||||||
<TextBlock Text="同步分类字典" Style="{StaticResource IconButtonStyle}"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</hc:UniformSpacingPanel>
|
</hc:UniformSpacingPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
@@ -11,34 +11,84 @@
|
|||||||
|
|
||||||
<Grid Style="{StaticResource BaseViewStyle}">
|
<Grid Style="{StaticResource BaseViewStyle}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Grid.Row="0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="260"/>
|
||||||
|
<ColumnDefinition Width="10"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- 左侧字典列表 -->
|
||||||
|
<Border Grid.Column="0"
|
||||||
|
BorderBrush="#FFEDEDED"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="4">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Border Grid.Row="0"
|
||||||
|
Background="#FFF5F5F5"
|
||||||
|
BorderBrush="#FFEDEDED"
|
||||||
|
BorderThickness="0 0 0 1"
|
||||||
|
Padding="12 8"
|
||||||
|
CornerRadius="4 4 0 0">
|
||||||
|
<TextBlock Text="字典列表"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
FontSize="13"
|
||||||
|
Foreground="#FF333333"/>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<ListBox Grid.Row="1"
|
||||||
|
x:Name="DictListBox"
|
||||||
|
ItemsSource="{Binding TreeNodes}"
|
||||||
|
SelectedItem="{Binding SelectedTreeNode, Mode=TwoWay}"
|
||||||
|
SelectionChanged="DictListBox_SelectionChanged"
|
||||||
|
BorderThickness="0"
|
||||||
|
Background="Transparent"
|
||||||
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||||
|
<ListBox.ItemContainerStyle>
|
||||||
|
<Style TargetType="ListBoxItem">
|
||||||
|
<Setter Property="Padding" Value="12 8"/>
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
</Style>
|
||||||
|
</ListBox.ItemContainerStyle>
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="{Binding DictCode}"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
FontSize="12"
|
||||||
|
TextTrimming="CharacterEllipsis"/>
|
||||||
|
<TextBlock Text="{Binding DictName}"
|
||||||
|
FontSize="11"
|
||||||
|
Foreground="#FF888888"
|
||||||
|
Margin="0 2 0 0"
|
||||||
|
TextTrimming="CharacterEllipsis"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- 右侧字典详情 -->
|
||||||
|
<Grid Grid.Column="2">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Border Grid.Row="0" CornerRadius="4" Margin="0 0 -10 0">
|
<Border Grid.Row="0" CornerRadius="4" Margin="0 0 -10 0">
|
||||||
<hc:Row>
|
<hc:Row>
|
||||||
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
|
||||||
<hc:TextBox
|
|
||||||
Text="{Binding Input.DictCode, UpdateSourceTrigger=PropertyChanged}"
|
|
||||||
hc:InfoElement.Placeholder="请输入字典编码"
|
|
||||||
hc:InfoElement.Title="字典编码"
|
|
||||||
hc:InfoElement.TitleWidth="70"
|
|
||||||
hc:InfoElement.TitlePlacement="Left"
|
|
||||||
hc:InfoElement.ShowClearButton="True"
|
|
||||||
Margin="0 0 10 10"/>
|
|
||||||
</hc:Col>
|
|
||||||
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
|
||||||
<hc:TextBox
|
|
||||||
Text="{Binding Input.DictName, UpdateSourceTrigger=PropertyChanged}"
|
|
||||||
hc:InfoElement.Placeholder="请输入字典名称"
|
|
||||||
hc:InfoElement.Title="字典名称"
|
|
||||||
hc:InfoElement.TitleWidth="70"
|
|
||||||
hc:InfoElement.TitlePlacement="Left"
|
|
||||||
hc:InfoElement.ShowClearButton="True"
|
|
||||||
Margin="0 0 10 10"/>
|
|
||||||
</hc:Col>
|
|
||||||
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
||||||
<hc:TextBox
|
<hc:TextBox
|
||||||
Text="{Binding Input.ItemText, UpdateSourceTrigger=PropertyChanged}"
|
Text="{Binding Input.ItemText, UpdateSourceTrigger=PropertyChanged}"
|
||||||
@@ -89,12 +139,6 @@
|
|||||||
<TextBlock Text="重置" Style="{StaticResource IconButtonStyle}"/>
|
<TextBlock Text="重置" Style="{StaticResource IconButtonStyle}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
<Button Style="{StaticResource ButtonSuccess}" Command="{Binding SyncCommand}">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<md:PackIcon Kind="CloudSyncOutline"/>
|
|
||||||
<TextBlock Text="同步数据字典" Style="{StaticResource IconButtonStyle}"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
</hc:UniformSpacingPanel>
|
</hc:UniformSpacingPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
@@ -106,10 +150,8 @@
|
|||||||
ColumnHeaderStyle="{StaticResource CusDataGridColumnHeaderStyle}"
|
ColumnHeaderStyle="{StaticResource CusDataGridColumnHeaderStyle}"
|
||||||
Style="{StaticResource CusDataGridStyle}">
|
Style="{StaticResource CusDataGridStyle}">
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn IsReadOnly="True" Binding="{Binding DictCode}" Header="字典编码" Width="150" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
<DataGridTextColumn IsReadOnly="True" Binding="{Binding ItemText}" Header="字典项文本" Width="200" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
||||||
<DataGridTextColumn IsReadOnly="True" Binding="{Binding DictName}" Header="字典名称" Width="170" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
<DataGridTextColumn IsReadOnly="True" Binding="{Binding ItemValue}" Header="字典项值" Width="160" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
||||||
<DataGridTextColumn IsReadOnly="True" Binding="{Binding ItemText}" Header="字典项文本" Width="170" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
|
||||||
<DataGridTextColumn IsReadOnly="True" Binding="{Binding ItemValue}" Header="字典项值" Width="140" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
|
||||||
<DataGridTextColumn IsReadOnly="True" Binding="{Binding ItemDescription}" Header="描述" Width="220" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
<DataGridTextColumn IsReadOnly="True" Binding="{Binding ItemDescription}" Header="描述" Width="220" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
||||||
<DataGridTextColumn IsReadOnly="True" Binding="{Binding SortOrder}" Header="排序" Width="80" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
<DataGridTextColumn IsReadOnly="True" Binding="{Binding SortOrder}" Header="排序" Width="80" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
||||||
<DataGridTextColumn IsReadOnly="True" Binding="{Binding Status, Converter={StaticResource EnumDescriptionConverter}}" Header="状态" Width="80" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
<DataGridTextColumn IsReadOnly="True" Binding="{Binding Status, Converter={StaticResource EnumDescriptionConverter}}" Header="状态" Width="80" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
||||||
@@ -117,7 +159,9 @@
|
|||||||
<DataGridTextColumn IsReadOnly="True" Binding="{Binding UpdateTime, StringFormat='yyyy-MM-dd HH:mm:ss'}" Header="更新时间" Width="170" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
<DataGridTextColumn IsReadOnly="True" Binding="{Binding UpdateTime, StringFormat='yyyy-MM-dd HH:mm:ss'}" Header="更新时间" Width="170" CellStyle="{StaticResource CusDataGridCellStyle}"/>
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<components:PaginationDataGridControl Grid.Row="3" DataContext="{Binding PaginationDataGridViewModel}"/>
|
<components:PaginationDataGridControl Grid.Row="1" DataContext="{Binding PaginationDataGridViewModel}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using YY.Admin.ViewModels.SysManage;
|
||||||
|
|
||||||
namespace YY.Admin.Views.SysManage
|
namespace YY.Admin.Views.SysManage
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// DataDictionaryManagementView.xaml 的交互逻辑
|
|
||||||
/// </summary>
|
|
||||||
public partial class DataDictionaryManagementView : UserControl
|
public partial class DataDictionaryManagementView : UserControl
|
||||||
{
|
{
|
||||||
public DataDictionaryManagementView()
|
public DataDictionaryManagementView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void DictListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is DataDictionaryManagementViewModel vm)
|
||||||
|
{
|
||||||
|
var node = (sender as ListBox)?.SelectedItem as DataDictionaryManagementViewModel.DictTreeNode;
|
||||||
|
await vm.OnDictTreeSelectedAsync(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
<UserControl x:Class="YY.Admin.Views.WeightRecord.MixerMaterialPickerDialogView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||||
|
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Width="720">
|
||||||
|
|
||||||
|
<Grid Background="{DynamicResource ThirdlyRegionBrush}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="360"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<hc:SimplePanel Margin="20,16,20,12">
|
||||||
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
|
<Border Width="32" Height="32" CornerRadius="6" Background="{DynamicResource PrimaryBrush}" Margin="0,0,10,0">
|
||||||
|
<md:PackIcon Kind="Flask" Width="18" Height="18" Foreground="White"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock Text="选择密炼物料(可多选)" FontSize="16" FontWeight="SemiBold"
|
||||||
|
Foreground="{DynamicResource PrimaryTextBrush}" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
<Button Width="22" Height="22" Command="hc:ControlCommands.Close"
|
||||||
|
Style="{StaticResource ButtonIcon}"
|
||||||
|
Foreground="{DynamicResource PrimaryBrush}"
|
||||||
|
hc:IconElement.Geometry="{StaticResource ErrorGeometry}"
|
||||||
|
Padding="0" HorizontalAlignment="Right" VerticalAlignment="Center"/>
|
||||||
|
</hc:SimplePanel>
|
||||||
|
|
||||||
|
<!-- 搜索栏 -->
|
||||||
|
<Border Grid.Row="1" Background="{DynamicResource RegionBrush}" Padding="16,10">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="80"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<hc:TextBox Grid.Column="0"
|
||||||
|
Text="{Binding SearchCode, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
hc:InfoElement.Placeholder="物料编码..."
|
||||||
|
hc:InfoElement.ShowClearButton="True"
|
||||||
|
Margin="0,0,8,0">
|
||||||
|
<hc:TextBox.InputBindings>
|
||||||
|
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
|
||||||
|
</hc:TextBox.InputBindings>
|
||||||
|
</hc:TextBox>
|
||||||
|
<hc:TextBox Grid.Column="1"
|
||||||
|
Text="{Binding SearchName, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
hc:InfoElement.Placeholder="物料名称..."
|
||||||
|
hc:InfoElement.ShowClearButton="True"
|
||||||
|
Margin="0,0,8,0">
|
||||||
|
<hc:TextBox.InputBindings>
|
||||||
|
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
|
||||||
|
</hc:TextBox.InputBindings>
|
||||||
|
</hc:TextBox>
|
||||||
|
<Button Grid.Column="2" Command="{Binding SearchCommand}"
|
||||||
|
IsEnabled="{Binding IsLoading, Converter={StaticResource Boolean2BooleanReConverter}}"
|
||||||
|
Style="{StaticResource ButtonPrimary}" Height="32">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<md:PackIcon Kind="Magnify" Width="14" Height="14" VerticalAlignment="Center" Margin="0,0,4,0"/>
|
||||||
|
<TextBlock Text="搜索" FontSize="13" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- 物料列表(多选) -->
|
||||||
|
<DataGrid x:Name="MaterialsGrid"
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="16,8,16,0"
|
||||||
|
ItemsSource="{Binding Materials}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
CanUserDeleteRows="False"
|
||||||
|
CanUserResizeRows="False"
|
||||||
|
HeadersVisibility="Column"
|
||||||
|
SelectionMode="Single"
|
||||||
|
SelectionUnit="FullRow"
|
||||||
|
IsReadOnly="False"
|
||||||
|
GridLinesVisibility="Horizontal"
|
||||||
|
HorizontalGridLinesBrush="#FFEDEFF2"
|
||||||
|
VerticalGridLinesBrush="Transparent"
|
||||||
|
Background="White"
|
||||||
|
RowBackground="White"
|
||||||
|
AlternatingRowBackground="#FAFCFF"
|
||||||
|
RowHeight="32"
|
||||||
|
ColumnHeaderHeight="34"
|
||||||
|
RowHeaderWidth="0">
|
||||||
|
<DataGrid.Resources>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#EAF3FF"/>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="#1F1F1F"/>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="#EAF3FF"/>
|
||||||
|
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="#1F1F1F"/>
|
||||||
|
</DataGrid.Resources>
|
||||||
|
<DataGrid.ColumnHeaderStyle>
|
||||||
|
<Style TargetType="DataGridColumnHeader">
|
||||||
|
<Setter Property="Background" Value="#F5F7FA"/>
|
||||||
|
<Setter Property="Foreground" Value="#606266"/>
|
||||||
|
<Setter Property="FontSize" Value="13"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="Padding" Value="8,0"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#EBEEF5"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0,0,0,1"/>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.ColumnHeaderStyle>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<Setter Property="Background" Value="White"/>
|
||||||
|
<Setter Property="Foreground" Value="#262626"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#FFEDEFF2"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0,0,0,1"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsSelected}" Value="True">
|
||||||
|
<Setter Property="Background" Value="#EAF3FF"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="#F5F9FF"/>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.CellStyle>
|
||||||
|
<Style TargetType="DataGridCell">
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Padding" Value="6,0"/>
|
||||||
|
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.CellStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTemplateColumn Header="选择" Width="50" CanUserSort="False">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTextColumn Header="物料编码" Binding="{Binding Source.MaterialCode}" Width="120" IsReadOnly="True"/>
|
||||||
|
<DataGridTextColumn Header="物料名称" Binding="{Binding Source.MaterialName}" Width="*" IsReadOnly="True"/>
|
||||||
|
<DataGridTextColumn Header="大类" Binding="{Binding Source.MajorCategoryText}" Width="110" IsReadOnly="True"/>
|
||||||
|
<DataGridTextColumn Header="小类" Binding="{Binding Source.MinorCategoryText}" Width="110" IsReadOnly="True"/>
|
||||||
|
<DataGridTextColumn Header="状态" Binding="{Binding Source.UseStatusText}" Width="70" IsReadOnly="True"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<!-- 底部操作栏 -->
|
||||||
|
<Border Grid.Row="3" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,1,0,0"
|
||||||
|
Padding="16,12">
|
||||||
|
<Grid>
|
||||||
|
<!-- 已选提示 + 清除 -->
|
||||||
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left" MaxWidth="380">
|
||||||
|
<Border Background="{DynamicResource PrimaryBrush}" CornerRadius="10" Padding="8,2" Margin="0,0,8,0"
|
||||||
|
Visibility="{Binding HasSelectedMaterials, Converter={StaticResource Boolean2VisibilityConverter}}">
|
||||||
|
<TextBlock Foreground="White" FontSize="12" FontWeight="SemiBold"
|
||||||
|
Text="{Binding SelectedCount, StringFormat=已选 {0} 项}"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock VerticalAlignment="Center" FontSize="13" TextTrimming="CharacterEllipsis"
|
||||||
|
Text="{Binding SelectedDisplayText}">
|
||||||
|
<TextBlock.Style>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource SecondaryTextBrush}"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding HasSelectedMaterials}" Value="True">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource PrimaryBrush}"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</TextBlock.Style>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
<!-- 按钮 -->
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Content="清除选择" Command="{Binding ClearSelectionCommand}"
|
||||||
|
Style="{StaticResource ButtonDefault}" Width="88" Margin="0,0,8,0" Height="34"
|
||||||
|
Visibility="{Binding HasSelectedMaterials, Converter={StaticResource Boolean2VisibilityConverter}}"/>
|
||||||
|
<Button Content="取 消" Command="{Binding CancelCommand}"
|
||||||
|
Style="{StaticResource ButtonDefault}" Width="88" Margin="0,0,12,0" Height="34"/>
|
||||||
|
<Button Content="确认选择" Command="{Binding ConfirmCommand}"
|
||||||
|
Style="{StaticResource ButtonPrimary}" Width="100" Height="34"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace YY.Admin.Views.WeightRecord;
|
||||||
|
|
||||||
|
public partial class MixerMaterialPickerDialogView : UserControl
|
||||||
|
{
|
||||||
|
public MixerMaterialPickerDialogView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,15 +101,45 @@
|
|||||||
Margin="0,0,0,16"/>
|
Margin="0,0,0,16"/>
|
||||||
</hc:Col>
|
</hc:Col>
|
||||||
|
|
||||||
<!-- 货物名称 -->
|
<!-- 密炼物料 -->
|
||||||
<hc:Col Span="12">
|
<hc:Col Span="24">
|
||||||
<hc:TextBox Text="{Binding Record.GoodsName, UpdateSourceTrigger=PropertyChanged}"
|
<Grid Margin="0,0,0,16">
|
||||||
hc:InfoElement.Title="货物名称"
|
<Grid.ColumnDefinitions>
|
||||||
hc:InfoElement.TitleWidth="80"
|
<ColumnDefinition Width="80"/>
|
||||||
hc:InfoElement.TitlePlacement="Left"
|
<ColumnDefinition Width="*"/>
|
||||||
hc:InfoElement.Placeholder="请输入货物名称"
|
<ColumnDefinition Width="60"/>
|
||||||
hc:InfoElement.ShowClearButton="True"
|
<ColumnDefinition Width="32"/>
|
||||||
Margin="0,0,0,16"/>
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="密炼物料" VerticalAlignment="Center"
|
||||||
|
FontSize="14" Foreground="{DynamicResource PrimaryTextBrush}"/>
|
||||||
|
<Border Grid.Column="1" CornerRadius="4" Height="32"
|
||||||
|
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"
|
||||||
|
Background="{DynamicResource RegionBrush}">
|
||||||
|
<TextBlock Text="{Binding MixerMaterialDisplay}" VerticalAlignment="Center"
|
||||||
|
Margin="8,0" FontSize="13" TextTrimming="CharacterEllipsis">
|
||||||
|
<TextBlock.Style>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource SecondaryTextBrush}"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding HasSelectedMixerMaterials}" Value="True">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</TextBlock.Style>
|
||||||
|
</TextBlock>
|
||||||
|
</Border>
|
||||||
|
<Button Grid.Column="2" Content="选 择"
|
||||||
|
Command="{Binding OpenMixerMaterialPickerCommand}"
|
||||||
|
Style="{StaticResource ButtonDefault}"
|
||||||
|
Height="32" Margin="6,0,0,0" FontSize="12"/>
|
||||||
|
<Button Grid.Column="3" Command="{Binding ClearMixerMaterialCommand}"
|
||||||
|
Style="{StaticResource ButtonIcon}"
|
||||||
|
Height="32" Width="28" Margin="4,0,0,0" Padding="0"
|
||||||
|
ToolTip="清除选择"
|
||||||
|
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||||
|
hc:IconElement.Geometry="{StaticResource ErrorGeometry}"/>
|
||||||
|
</Grid>
|
||||||
</hc:Col>
|
</hc:Col>
|
||||||
|
|
||||||
<!-- 毛重 -->
|
<!-- 毛重 -->
|
||||||
|
|||||||
@@ -59,12 +59,12 @@
|
|||||||
hc:InfoElement.ShowClearButton="True"/>
|
hc:InfoElement.ShowClearButton="True"/>
|
||||||
</hc:Col>
|
</hc:Col>
|
||||||
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=4, Xl=4}">
|
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=4, Xl=4}">
|
||||||
<hc:TextBox Text="{Binding FilterGoodsName, UpdateSourceTrigger=PropertyChanged}"
|
<hc:TextBox Text="{Binding FilterDriverName, UpdateSourceTrigger=PropertyChanged}"
|
||||||
Margin="0 0 10 10"
|
Margin="0 0 10 10"
|
||||||
hc:InfoElement.Title="货物名称"
|
hc:InfoElement.Title="司机"
|
||||||
hc:InfoElement.TitlePlacement="Left"
|
hc:InfoElement.TitlePlacement="Left"
|
||||||
hc:InfoElement.TitleWidth="65"
|
hc:InfoElement.TitleWidth="65"
|
||||||
hc:InfoElement.Placeholder="请输入货物名称"
|
hc:InfoElement.Placeholder="请输入司机姓名"
|
||||||
hc:InfoElement.ShowClearButton="True">
|
hc:InfoElement.ShowClearButton="True">
|
||||||
<hc:TextBox.InputBindings>
|
<hc:TextBox.InputBindings>
|
||||||
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
|
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
|
||||||
@@ -72,12 +72,12 @@
|
|||||||
</hc:TextBox>
|
</hc:TextBox>
|
||||||
</hc:Col>
|
</hc:Col>
|
||||||
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=4, Xl=4}">
|
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=4, Xl=4}">
|
||||||
<hc:TextBox Text="{Binding FilterDriverName, UpdateSourceTrigger=PropertyChanged}"
|
<hc:TextBox Text="{Binding FilterMixerMaterialName, UpdateSourceTrigger=PropertyChanged}"
|
||||||
Margin="0 0 10 10"
|
Margin="0 0 10 10"
|
||||||
hc:InfoElement.Title="司机"
|
hc:InfoElement.Title="密炼物料"
|
||||||
hc:InfoElement.TitlePlacement="Left"
|
hc:InfoElement.TitlePlacement="Left"
|
||||||
hc:InfoElement.TitleWidth="65"
|
hc:InfoElement.TitleWidth="65"
|
||||||
hc:InfoElement.Placeholder="请输入司机姓名"
|
hc:InfoElement.Placeholder="请输入物料名称"
|
||||||
hc:InfoElement.ShowClearButton="True">
|
hc:InfoElement.ShowClearButton="True">
|
||||||
<hc:TextBox.InputBindings>
|
<hc:TextBox.InputBindings>
|
||||||
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
|
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
|
||||||
@@ -142,7 +142,8 @@
|
|||||||
<DataGridTextColumn Header="车牌号" Binding="{Binding PlateNumber}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="110"/>
|
<DataGridTextColumn Header="车牌号" Binding="{Binding PlateNumber}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="110"/>
|
||||||
<DataGridTextColumn Header="发货单位" Binding="{Binding SenderUnit}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
|
<DataGridTextColumn Header="发货单位" Binding="{Binding SenderUnit}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
|
||||||
<DataGridTextColumn Header="收货单位" Binding="{Binding ReceiverUnit}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
|
<DataGridTextColumn Header="收货单位" Binding="{Binding ReceiverUnit}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
|
||||||
<DataGridTextColumn Header="货物名称" Binding="{Binding GoodsName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="120"/>
|
<DataGridTextColumn Header="密炼物料" Binding="{Binding MixerMaterialNames}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="160"/>
|
||||||
|
<DataGridTextColumn Header="类型" Binding="{Binding MaterialTypeText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="80"/>
|
||||||
<DataGridTextColumn Header="毛重(KG)" Binding="{Binding GrossWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
|
<DataGridTextColumn Header="毛重(KG)" Binding="{Binding GrossWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
|
||||||
<DataGridTextColumn Header="皮重(KG)" Binding="{Binding TareWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
|
<DataGridTextColumn Header="皮重(KG)" Binding="{Binding TareWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
|
||||||
<DataGridTextColumn Header="净重(KG)" Binding="{Binding NetWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
|
<DataGridTextColumn Header="净重(KG)" Binding="{Binding NetWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
|
||||||
|
|||||||
@@ -389,16 +389,6 @@
|
|||||||
</hc:Col>
|
</hc:Col>
|
||||||
<!-- 发货单位(供应商弹窗选择) -->
|
<!-- 发货单位(供应商弹窗选择) -->
|
||||||
<hc:Col Span="24">
|
<hc:Col Span="24">
|
||||||
<hc:Col.Style>
|
|
||||||
<Style TargetType="hc:Col">
|
|
||||||
<Setter Property="Visibility" Value="Visible"/>
|
|
||||||
<Style.Triggers>
|
|
||||||
<DataTrigger Binding="{Binding InoutDirection}" Value="2">
|
|
||||||
<Setter Property="Visibility" Value="Collapsed"/>
|
|
||||||
</DataTrigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</hc:Col.Style>
|
|
||||||
<Grid Margin="0,0,0,8">
|
<Grid Margin="0,0,0,8">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="75"/>
|
<ColumnDefinition Width="75"/>
|
||||||
@@ -439,16 +429,6 @@
|
|||||||
</hc:Col>
|
</hc:Col>
|
||||||
<!-- 收货单位(客户弹窗选择) -->
|
<!-- 收货单位(客户弹窗选择) -->
|
||||||
<hc:Col Span="24">
|
<hc:Col Span="24">
|
||||||
<hc:Col.Style>
|
|
||||||
<Style TargetType="hc:Col">
|
|
||||||
<Setter Property="Visibility" Value="Collapsed"/>
|
|
||||||
<Style.Triggers>
|
|
||||||
<DataTrigger Binding="{Binding InoutDirection}" Value="2">
|
|
||||||
<Setter Property="Visibility" Value="Visible"/>
|
|
||||||
</DataTrigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</hc:Col.Style>
|
|
||||||
<Grid Margin="0,0,0,8">
|
<Grid Margin="0,0,0,8">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="75"/>
|
<ColumnDefinition Width="75"/>
|
||||||
@@ -487,6 +467,46 @@
|
|||||||
hc:IconElement.Geometry="{StaticResource ErrorGeometry}"/>
|
hc:IconElement.Geometry="{StaticResource ErrorGeometry}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</hc:Col>
|
</hc:Col>
|
||||||
|
<!-- 密炼物料选择 -->
|
||||||
|
<hc:Col Span="24">
|
||||||
|
<Grid Margin="0,0,0,8">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="75"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="56"/>
|
||||||
|
<ColumnDefinition Width="60"/>
|
||||||
|
<ColumnDefinition Width="32"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0" Text="密炼物料" VerticalAlignment="Center"
|
||||||
|
FontSize="14" Foreground="{DynamicResource PrimaryTextBrush}"/>
|
||||||
|
<Border Grid.Column="1" CornerRadius="4" Height="32"
|
||||||
|
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"
|
||||||
|
Background="{DynamicResource ThirdlyRegionBrush}">
|
||||||
|
<TextBox Text="{Binding MixerMaterialNames, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
IsReadOnly="{Binding IsMixerMaterialReadOnly}"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
Margin="8,0"
|
||||||
|
BorderThickness="0"
|
||||||
|
Background="Transparent"
|
||||||
|
FontSize="13"
|
||||||
|
ToolTip="勾选“手动”可直接输入密炼物料名称"/>
|
||||||
|
</Border>
|
||||||
|
<CheckBox Grid.Column="2" Content="手动"
|
||||||
|
IsChecked="{Binding IsMixerMaterialManual}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
<Button Grid.Column="3" Content="选 择"
|
||||||
|
Command="{Binding OpenMixerMaterialPickerCommand}"
|
||||||
|
Style="{StaticResource ButtonDefault}"
|
||||||
|
Height="32" Margin="6,0,0,0" FontSize="12"/>
|
||||||
|
<Button Grid.Column="4" Command="{Binding ClearMixerMaterialCommand}"
|
||||||
|
Style="{StaticResource ButtonIcon}"
|
||||||
|
Height="32" Width="28" Margin="4,0,0,0" Padding="0"
|
||||||
|
ToolTip="清除选择"
|
||||||
|
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||||
|
hc:IconElement.Geometry="{StaticResource ErrorGeometry}"/>
|
||||||
|
</Grid>
|
||||||
|
</hc:Col>
|
||||||
<!-- 司机 -->
|
<!-- 司机 -->
|
||||||
<hc:Col Span="12">
|
<hc:Col Span="12">
|
||||||
<hc:TextBox Text="{Binding DriverName, UpdateSourceTrigger=PropertyChanged}"
|
<hc:TextBox Text="{Binding DriverName, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
|||||||
Reference in New Issue
Block a user