桌面端新增密炼计划获取
This commit is contained in:
@@ -13,6 +13,7 @@ import org.jeecg.common.system.base.controller.JeecgController;
|
|||||||
import org.jeecg.common.system.query.QueryGenerator;
|
import org.jeecg.common.system.query.QueryGenerator;
|
||||||
import org.jeecg.modules.xslmes.entity.MesXslMixingProductionPlan;
|
import org.jeecg.modules.xslmes.entity.MesXslMixingProductionPlan;
|
||||||
import org.jeecg.modules.xslmes.service.IMesXslMixingProductionPlanService;
|
import org.jeecg.modules.xslmes.service.IMesXslMixingProductionPlanService;
|
||||||
|
import org.jeecg.modules.xslmes.service.MesXslStompNotifyService;
|
||||||
import org.jeecg.modules.xslmes.vo.MesXslMixingProductionPlanOrderOptionVO;
|
import org.jeecg.modules.xslmes.vo.MesXslMixingProductionPlanOrderOptionVO;
|
||||||
import org.jeecg.modules.xslmes.vo.MesXslMixingProductionPlanSaveAllVO;
|
import org.jeecg.modules.xslmes.vo.MesXslMixingProductionPlanSaveAllVO;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -29,10 +30,13 @@ public class MesXslMixingProductionPlanController
|
|||||||
extends JeecgController<MesXslMixingProductionPlan, IMesXslMixingProductionPlanService> {
|
extends JeecgController<MesXslMixingProductionPlan, IMesXslMixingProductionPlanService> {
|
||||||
|
|
||||||
private final IMesXslMixingProductionPlanService mixingProductionPlanService;
|
private final IMesXslMixingProductionPlanService mixingProductionPlanService;
|
||||||
|
private final MesXslStompNotifyService stompNotify;
|
||||||
|
|
||||||
public MesXslMixingProductionPlanController(
|
public MesXslMixingProductionPlanController(
|
||||||
IMesXslMixingProductionPlanService mixingProductionPlanService) {
|
IMesXslMixingProductionPlanService mixingProductionPlanService,
|
||||||
|
MesXslStompNotifyService stompNotify) {
|
||||||
this.mixingProductionPlanService = mixingProductionPlanService;
|
this.mixingProductionPlanService = mixingProductionPlanService;
|
||||||
|
this.stompNotify = stompNotify;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "密炼生产计划维护-分页列表查询")
|
@Operation(summary = "密炼生产计划维护-分页列表查询")
|
||||||
@@ -56,6 +60,9 @@ public class MesXslMixingProductionPlanController
|
|||||||
@PostMapping("/saveAll")
|
@PostMapping("/saveAll")
|
||||||
public Result<String> saveAll(@RequestBody MesXslMixingProductionPlanSaveAllVO req) {
|
public Result<String> saveAll(@RequestBody MesXslMixingProductionPlanSaveAllVO req) {
|
||||||
mixingProductionPlanService.saveAllRows(req == null ? null : req.getRows());
|
mixingProductionPlanService.saveAllRows(req == null ? null : req.getRows());
|
||||||
|
//update-begin---author:jiangxh ---date:20260617 for:【密炼计划】整表保存后广播桌面端同步-----------
|
||||||
|
stompNotify.publishMixingProductionPlanChanged("saveAll", null);
|
||||||
|
//update-end---author:jiangxh ---date:20260617 for:【密炼计划】整表保存后广播桌面端同步-----------
|
||||||
return Result.OK("保存成功");
|
return Result.OK("保存成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,14 @@ public class MesXslStompNotifyService {
|
|||||||
}
|
}
|
||||||
//update-end---author:jiangxh ---date:20260617 for:【快检实验标准】桌面端只读同步 STOMP-----------
|
//update-end---author:jiangxh ---date:20260617 for:【快检实验标准】桌面端只读同步 STOMP-----------
|
||||||
|
|
||||||
|
//update-begin---author:jiangxh ---date:20260617 for:【密炼计划】桌面端只读同步 STOMP-----------
|
||||||
|
/** 广播密炼生产计划变更事件到 /topic/sync/mes-mixing-production-plans */
|
||||||
|
public void publishMixingProductionPlanChanged(String action, String mixingProductionPlanId) {
|
||||||
|
publish("/topic/sync/mes-mixing-production-plans", "MIXING_PRODUCTION_PLAN_CHANGED",
|
||||||
|
"mixingProductionPlanId", mixingProductionPlanId, action);
|
||||||
|
}
|
||||||
|
//update-end---author:jiangxh ---date:20260617 for:【密炼计划】桌面端只读同步 STOMP-----------
|
||||||
|
|
||||||
// ─────────────────────────── 私有辅助 ────────────────────────────
|
// ─────────────────────────── 私有辅助 ────────────────────────────
|
||||||
|
|
||||||
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) {
|
||||||
|
|||||||
374
jeecg-boot/scan_mixing_plan.json
Normal file
374
jeecg-boot/scan_mixing_plan.json
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
{
|
||||||
|
"scanKeyword": "MesXslMixingProductionPlan",
|
||||||
|
"entityClass": "MesXslMixingProductionPlan",
|
||||||
|
"tableName": "mes_xsl_mixing_production_plan",
|
||||||
|
"javaEntityFile": "jeecg-boot\\jeecg-boot-module\\jeecg-module-xslmes\\src\\main\\java\\org\\jeecg\\modules\\xslmes\\entity\\MesXslMixingProductionPlan.java",
|
||||||
|
"hasIzEnable": false,
|
||||||
|
"hasCodeUniqueness": false,
|
||||||
|
"uniquenessFields": [],
|
||||||
|
"backendArch": {
|
||||||
|
"unifiedAnonCtrl": "jeecg-boot\\jeecg-boot-module\\jeecg-module-xslmes\\src\\main\\java\\org\\jeecg\\modules\\xslmes\\controller\\MesXslDesktopAnonController.java",
|
||||||
|
"registeredInAnonCtrl": true,
|
||||||
|
"anonEndpoints": [
|
||||||
|
"list"
|
||||||
|
],
|
||||||
|
"stompNotifySvc": "jeecg-boot\\jeecg-boot-module\\jeecg-module-xslmes\\src\\main\\java\\org\\jeecg\\modules\\xslmes\\service\\MesXslStompNotifyService.java",
|
||||||
|
"registeredInStompSvc": false,
|
||||||
|
"bizCtrlFile": "jeecg-boot\\jeecg-boot-module\\jeecg-module-xslmes\\src\\main\\java\\org\\jeecg\\modules\\xslmes\\controller\\MesXslMixingProductionPlanController.java",
|
||||||
|
"bizCtrlUsesSharedNotify": false,
|
||||||
|
"bizCtrlHasPrivatePublish": false
|
||||||
|
},
|
||||||
|
"wpfRegistrationStatus": {
|
||||||
|
"syncModuleService": false,
|
||||||
|
"syncModuleCoordinator": false,
|
||||||
|
"navigationView": false,
|
||||||
|
"stompSubscribe": false,
|
||||||
|
"menuRegistered": false,
|
||||||
|
"tenantMenuRegistered": false,
|
||||||
|
"syncModuleFilePath": "yy-admin-master\\YY.Admin\\Module\\SyncModule.cs",
|
||||||
|
"navExtFilePath": "yy-admin-master\\YY.Admin\\Module\\NavigationExtensions.cs",
|
||||||
|
"stompWsFilePath": "yy-admin-master\\YY.Admin\\Infrastructure\\Hubs\\StompWebSocketService.cs",
|
||||||
|
"menuSeedFilePath": "yy-admin-master\\YY.Admin.Core\\SeedData\\SysMenuSeedData.cs",
|
||||||
|
"summary": "✗ 待完成: SyncModule服务注册, SyncModule协调器注册, NavigationExtensions视图注册, STOMP订阅, 菜单注册"
|
||||||
|
},
|
||||||
|
"menuSuggestion": {
|
||||||
|
"parentMenuId": 1300150000101,
|
||||||
|
"parentMenuTitle": "基础资料",
|
||||||
|
"nextMenuId": 1300150011401,
|
||||||
|
"nextOrderNo": 113,
|
||||||
|
"menuIdPattern": "130015001{N}01,N 每次 +1(1→101,2→201...)",
|
||||||
|
"alreadyExists": false,
|
||||||
|
"existingMenuId": null
|
||||||
|
},
|
||||||
|
"apiPrefix": "/xslmes/mesXslMixingProductionPlan",
|
||||||
|
"stompCmd": "MIXING_PRODUCTION_PLAN_CHANGED",
|
||||||
|
"stompTopic": "/topic/sync/mes-mixing-production-plans",
|
||||||
|
"stompSubscriptionId": "sub-mes-xsl-mixing-production-plan",
|
||||||
|
"syncMode": "B",
|
||||||
|
"syncModeReason": "有/anon/免密端点,适合模式B",
|
||||||
|
"filterFields": [
|
||||||
|
"machineId",
|
||||||
|
"machineName",
|
||||||
|
"planNo",
|
||||||
|
"planType",
|
||||||
|
"materialName"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"javaName": "sortNo",
|
||||||
|
"csName": "SortNo",
|
||||||
|
"sqlName": "sort_no",
|
||||||
|
"javaType": "Integer",
|
||||||
|
"csType": "int?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "machineId",
|
||||||
|
"csName": "MachineId",
|
||||||
|
"sqlName": "machine_id",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": "id"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "machineName",
|
||||||
|
"csName": "MachineName",
|
||||||
|
"sqlName": "machine_name",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "shiftFlag",
|
||||||
|
"csName": "ShiftFlag",
|
||||||
|
"sqlName": "shift_flag",
|
||||||
|
"javaType": "Integer",
|
||||||
|
"csType": "int?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "planDate",
|
||||||
|
"csName": "PlanDate",
|
||||||
|
"sqlName": "plan_date",
|
||||||
|
"javaType": "Date",
|
||||||
|
"csType": "DateTime?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "planNo",
|
||||||
|
"csName": "PlanNo",
|
||||||
|
"sqlName": "plan_no",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "planId",
|
||||||
|
"csName": "PlanId",
|
||||||
|
"sqlName": "plan_id",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "planType",
|
||||||
|
"csName": "PlanType",
|
||||||
|
"sqlName": "plan_type",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "sourceOrderId",
|
||||||
|
"csName": "SourceOrderId",
|
||||||
|
"sqlName": "source_order_id",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "materialId",
|
||||||
|
"csName": "MaterialId",
|
||||||
|
"sqlName": "material_id",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "materialName",
|
||||||
|
"csName": "MaterialName",
|
||||||
|
"sqlName": "material_name",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "orderNo",
|
||||||
|
"csName": "OrderNo",
|
||||||
|
"sqlName": "order_no",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "orderDate",
|
||||||
|
"csName": "OrderDate",
|
||||||
|
"sqlName": "order_date",
|
||||||
|
"javaType": "Date",
|
||||||
|
"csType": "DateTime?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "formulaName",
|
||||||
|
"csName": "FormulaName",
|
||||||
|
"sqlName": "formula_name",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "planWeight",
|
||||||
|
"csName": "PlanWeight",
|
||||||
|
"sqlName": "plan_weight",
|
||||||
|
"javaType": "BigDecimal",
|
||||||
|
"csType": "double?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "plannedCarCount",
|
||||||
|
"csName": "PlannedCarCount",
|
||||||
|
"sqlName": "planned_car_count",
|
||||||
|
"javaType": "Integer",
|
||||||
|
"csType": "int?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "scheduledCarCount",
|
||||||
|
"csName": "ScheduledCarCount",
|
||||||
|
"sqlName": "scheduled_car_count",
|
||||||
|
"javaType": "Integer",
|
||||||
|
"csType": "int?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "finishedCarCount",
|
||||||
|
"csName": "FinishedCarCount",
|
||||||
|
"sqlName": "finished_car_count",
|
||||||
|
"javaType": "Integer",
|
||||||
|
"csType": "int?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "planCount",
|
||||||
|
"csName": "PlanCount",
|
||||||
|
"sqlName": "plan_count",
|
||||||
|
"javaType": "Integer",
|
||||||
|
"csType": "int?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"javaName": "remark",
|
||||||
|
"csName": "Remark",
|
||||||
|
"sqlName": "remark",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": false,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pkField": {
|
||||||
|
"javaName": "id",
|
||||||
|
"csName": "Id",
|
||||||
|
"sqlName": "id",
|
||||||
|
"javaType": "String",
|
||||||
|
"csType": "string?",
|
||||||
|
"comment": "",
|
||||||
|
"isPk": true,
|
||||||
|
"isAudit": false,
|
||||||
|
"isIzEnable": false,
|
||||||
|
"required": false,
|
||||||
|
"dictCode": null
|
||||||
|
},
|
||||||
|
"auditFields": [
|
||||||
|
"TenantId",
|
||||||
|
"SysOrgCode",
|
||||||
|
"CreateBy",
|
||||||
|
"CreateTime",
|
||||||
|
"UpdateBy",
|
||||||
|
"UpdateTime",
|
||||||
|
"DelFlag"
|
||||||
|
],
|
||||||
|
"dbConfig": {
|
||||||
|
"url": "jdbc:mysql://localhost:3306/jeecg-boot-dev?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai",
|
||||||
|
"username": "root",
|
||||||
|
"configFile": "jeecg-boot\\jeecg-boot-module\\jeecg-boot-module-airag\\src\\main\\resources\\application.yml"
|
||||||
|
},
|
||||||
|
"dbColumns": [],
|
||||||
|
"csEntityStub": "public class MesXslMixingProductionPlan\n{\n public string? Id { get; set; }\n public int? SortNo { get; set; }\n public string? MachineId { get; set; } [Dict:id]\n public string? MachineName { get; set; }\n public int? ShiftFlag { get; set; }\n public DateTime? PlanDate { get; set; }\n public string? PlanNo { get; set; }\n public string? PlanId { get; set; }\n public string? PlanType { get; set; }\n public string? SourceOrderId { get; set; }\n public string? MaterialId { get; set; }\n public string? MaterialName { get; set; }\n public string? OrderNo { get; set; }\n public DateTime? OrderDate { get; set; }\n public string? FormulaName { get; set; }\n public double? PlanWeight { get; set; }\n public int? PlannedCarCount { get; set; }\n public int? ScheduledCarCount { get; set; }\n public int? FinishedCarCount { get; set; }\n public int? PlanCount { get; set; }\n public string? Remark { get; set; }\n public int? TenantId { get; set; }\n public string? SysOrgCode { get; set; }\n public string? CreateBy { get; set; }\n public DateTime? CreateTime { get; set; }\n public string? UpdateBy { get; set; }\n public DateTime? UpdateTime { get; set; }\n public int? DelFlag { get; set; }\n // 只读显示属性:\n // public string StatusText => Status == \"1\" ? \"停用\" : \"启用\";\n}",
|
||||||
|
"generationHints": {
|
||||||
|
"eventClassName": "MesXslMixingProductionPlanChangedEvent",
|
||||||
|
"serviceInterface": "IMixingProductionPlanService",
|
||||||
|
"serviceImpl": "MixingProductionPlanService",
|
||||||
|
"syncCoordinator": "MixingProductionPlanSyncCoordinator",
|
||||||
|
"listViewModel": "MixingProductionPlanListViewModel",
|
||||||
|
"editDialogViewModel": "MixingProductionPlanEditDialogViewModel",
|
||||||
|
"listView": "MixingProductionPlanListView",
|
||||||
|
"editDialogView": "MixingProductionPlanEditDialogView",
|
||||||
|
"pendingOpsFile": "mes-xsl-mixing-production-plan-pending-ops.json",
|
||||||
|
"cacheFile": "mes-xsl-mixing-production-plan-cache.json",
|
||||||
|
"nextMenuId": 1300150011401,
|
||||||
|
"nextMenuOrderNo": 113,
|
||||||
|
"backendFilesToModify": [
|
||||||
|
"jeecg-boot\\jeecg-boot-module\\jeecg-module-xslmes\\src\\main\\java\\org\\jeecg\\modules\\xslmes\\controller\\MesXslDesktopAnonController.java",
|
||||||
|
"jeecg-boot\\jeecg-boot-module\\jeecg-module-xslmes\\src\\main\\java\\org\\jeecg\\modules\\xslmes\\service\\MesXslStompNotifyService.java",
|
||||||
|
"jeecg-boot\\jeecg-boot-module\\jeecg-module-xslmes\\src\\main\\java\\org\\jeecg\\modules\\xslmes\\controller\\MesXslMixingProductionPlanController.java",
|
||||||
|
"jeecg-boot-base-core/.../ShiroConfig.java"
|
||||||
|
],
|
||||||
|
"wpfFilesToModify": [
|
||||||
|
"yy-admin-master\\YY.Admin\\Module\\SyncModule.cs",
|
||||||
|
"yy-admin-master\\YY.Admin\\Module\\NavigationExtensions.cs",
|
||||||
|
"yy-admin-master\\YY.Admin\\Infrastructure\\Hubs\\StompWebSocketService.cs",
|
||||||
|
"yy-admin-master\\YY.Admin.Core\\SeedData\\SysMenuSeedData.cs",
|
||||||
|
"YY.Admin.Core/SeedData/SysTenantMenuSeedData.cs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Prism.Events;
|
||||||
|
|
||||||
|
namespace YY.Admin.Core.Events;
|
||||||
|
|
||||||
|
public class MixingProductionPlanChangedPayload
|
||||||
|
{
|
||||||
|
public string Action { get; set; } = string.Empty;
|
||||||
|
public string? MixingProductionPlanId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MixingProductionPlanChangedEvent : PubSubEvent<MixingProductionPlanChangedPayload> { }
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using YY.Admin.Core.Entity;
|
||||||
|
|
||||||
|
namespace YY.Admin.Core.Services;
|
||||||
|
|
||||||
|
/// <summary>密炼生产计划(MES 只读同步)</summary>
|
||||||
|
public interface IMixingProductionPlanService
|
||||||
|
{
|
||||||
|
Task<MixingProductionPlanPageResult> PageAsync(
|
||||||
|
int pageNo, int pageSize,
|
||||||
|
DateTime? planDateFrom = null,
|
||||||
|
DateTime? planDateTo = null,
|
||||||
|
string? machineName = null,
|
||||||
|
int? shiftFlag = null,
|
||||||
|
string? planNo = null,
|
||||||
|
string? materialName = null,
|
||||||
|
CancellationToken ct = default);
|
||||||
|
|
||||||
|
Task<List<MesXslMixingProductionPlan>> GetAllCachedAsync(CancellationToken ct = default);
|
||||||
|
|
||||||
|
/// <returns>本地缓存是否有变更(有差异才写入)</returns>
|
||||||
|
Task<bool> SyncFromRemoteAsync(CancellationToken ct = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record MixingProductionPlanPageResult(
|
||||||
|
List<MesXslMixingProductionPlan> Records,
|
||||||
|
long Total,
|
||||||
|
int PageNo,
|
||||||
|
int PageSize);
|
||||||
@@ -14,7 +14,8 @@ public interface IRubberQuickTestStdService
|
|||||||
|
|
||||||
Task<MesXslRubberQuickTestStd?> GetByIdAsync(string id, CancellationToken ct = default);
|
Task<MesXslRubberQuickTestStd?> GetByIdAsync(string id, CancellationToken ct = default);
|
||||||
|
|
||||||
Task SyncFromRemoteAsync(CancellationToken ct = default);
|
/// <returns>本地缓存是否有变更(有差异才写入)</returns>
|
||||||
|
Task<bool> SyncFromRemoteAsync(CancellationToken ct = default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record RubberQuickTestStdPageResult(
|
public record RubberQuickTestStdPageResult(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace YY.Admin.Core.Entity;
|
namespace YY.Admin.Core.Entity;
|
||||||
|
|
||||||
/// <summary>密炼生产计划维护(桌面端快检记录筛选用)</summary>
|
/// <summary>密炼生产计划维护(MES 数据源,桌面端只读同步)</summary>
|
||||||
public class MesXslMixingProductionPlan
|
public class MesXslMixingProductionPlan
|
||||||
{
|
{
|
||||||
public string? Id { get; set; }
|
public string? Id { get; set; }
|
||||||
@@ -8,21 +8,43 @@ public class MesXslMixingProductionPlan
|
|||||||
public string? MachineId { get; set; }
|
public string? MachineId { get; set; }
|
||||||
public string? MachineName { get; set; }
|
public string? MachineName { get; set; }
|
||||||
|
|
||||||
public string? MorningPlanId { get; set; }
|
/// <summary>班次标识:1早班 2中班 3晚班</summary>
|
||||||
public string? MorningPlanType { get; set; }
|
public int? ShiftFlag { get; set; }
|
||||||
public string? MorningOrderNo { get; set; }
|
|
||||||
public DateTime? MorningOrderDate { get; set; }
|
|
||||||
public string? MorningFormulaName { get; set; }
|
|
||||||
|
|
||||||
public string? NoonPlanId { get; set; }
|
/// <summary>计划日期(密炼日期)</summary>
|
||||||
public string? NoonPlanType { get; set; }
|
public DateTime? PlanDate { get; set; }
|
||||||
public string? NoonOrderNo { get; set; }
|
|
||||||
public DateTime? NoonOrderDate { get; set; }
|
|
||||||
public string? NoonFormulaName { get; set; }
|
|
||||||
|
|
||||||
public string? NightPlanId { get; set; }
|
public string? PlanNo { get; set; }
|
||||||
public string? NightPlanType { get; set; }
|
public string? PlanId { get; set; }
|
||||||
public string? NightOrderNo { get; set; }
|
public string? PlanType { get; set; }
|
||||||
public DateTime? NightOrderDate { get; set; }
|
public string? SourceOrderId { get; set; }
|
||||||
public string? NightFormulaName { get; set; }
|
public string? MaterialId { get; set; }
|
||||||
|
public string? MaterialName { get; set; }
|
||||||
|
public string? OrderNo { get; set; }
|
||||||
|
public DateTime? OrderDate { get; set; }
|
||||||
|
public string? FormulaName { get; set; }
|
||||||
|
public double? PlanWeight { get; set; }
|
||||||
|
public int? PlannedCarCount { get; set; }
|
||||||
|
public int? ScheduledCarCount { get; set; }
|
||||||
|
public int? FinishedCarCount { get; set; }
|
||||||
|
/// <summary>计划数量(MES plan_count)</summary>
|
||||||
|
public int? PlanCount { get; set; }
|
||||||
|
public string? Remark { get; set; }
|
||||||
|
public int? TenantId { get; set; }
|
||||||
|
public string? SysOrgCode { get; set; }
|
||||||
|
public string? CreateBy { get; set; }
|
||||||
|
public DateTime? CreateTime { get; set; }
|
||||||
|
public string? UpdateBy { get; set; }
|
||||||
|
public DateTime? UpdateTime { get; set; }
|
||||||
|
public int? DelFlag { get; set; }
|
||||||
|
|
||||||
|
public string ShiftFlagText => ShiftFlag switch
|
||||||
|
{
|
||||||
|
1 => "早班",
|
||||||
|
2 => "中班",
|
||||||
|
3 => "晚班",
|
||||||
|
_ => ShiftFlag?.ToString() ?? string.Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
public string PlanDateText => PlanDate?.ToString("yyyy-MM-dd") ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
namespace YY.Admin.Core.Helper;
|
||||||
|
|
||||||
|
/// <summary>MES 只读数据:远端列表与本地缓存按 Id 对比合并</summary>
|
||||||
|
public static class MesReadOnlyCacheMergeHelper
|
||||||
|
{
|
||||||
|
public sealed record MergeResult(int Added, int Updated, int Removed)
|
||||||
|
{
|
||||||
|
public bool HasChanges => Added > 0 || Updated > 0 || Removed > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对比远端与本地,返回合并后的列表及变更统计。
|
||||||
|
/// 内容相同则保留本地副本;<paramref name="mergeUpdated"/> 可定制变更行的合并策略(如保留本地子表)。
|
||||||
|
/// </summary>
|
||||||
|
public static (List<T> Merged, MergeResult Stats) Merge<T>(
|
||||||
|
IReadOnlyList<T> local,
|
||||||
|
IReadOnlyList<T> remote,
|
||||||
|
Func<T, string?> getId,
|
||||||
|
Func<T, T, bool> isContentEqual,
|
||||||
|
Func<T, T> clone,
|
||||||
|
Func<T, T, T>? mergeUpdated = null)
|
||||||
|
{
|
||||||
|
var localById = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var item in local)
|
||||||
|
{
|
||||||
|
var id = getId(item);
|
||||||
|
if (!string.IsNullOrWhiteSpace(id))
|
||||||
|
localById[id] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
var remoteIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var merged = new List<T>(remote.Count);
|
||||||
|
int added = 0, updated = 0;
|
||||||
|
|
||||||
|
foreach (var remoteItem in remote)
|
||||||
|
{
|
||||||
|
var id = getId(remoteItem);
|
||||||
|
if (string.IsNullOrWhiteSpace(id))
|
||||||
|
{
|
||||||
|
merged.Add(clone(remoteItem));
|
||||||
|
added++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteIds.Add(id);
|
||||||
|
|
||||||
|
if (!localById.TryGetValue(id, out var localItem))
|
||||||
|
{
|
||||||
|
merged.Add(clone(remoteItem));
|
||||||
|
added++;
|
||||||
|
}
|
||||||
|
else if (!isContentEqual(localItem, remoteItem))
|
||||||
|
{
|
||||||
|
merged.Add(mergeUpdated != null ? mergeUpdated(localItem, remoteItem) : clone(remoteItem));
|
||||||
|
updated++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
merged.Add(clone(localItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int removed = localById.Keys.Count(id => !remoteIds.Contains(id));
|
||||||
|
return (merged, new MergeResult(added, updated, removed));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,8 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
|
|||||||
new SysMenu{ Id=1300150011201, Pid=1300150000101, Title="快检记录", Path="/xslmes/rubberQuickTestOperation", Name="rubberQuickTestOperation", Component="RubberQuickTestOperationView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=111 },
|
new SysMenu{ Id=1300150011201, Pid=1300150000101, Title="快检记录", Path="/xslmes/rubberQuickTestOperation", Name="rubberQuickTestOperation", Component="RubberQuickTestOperationView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=111 },
|
||||||
// 胶料快检实验标准(桌面端只读)
|
// 胶料快检实验标准(桌面端只读)
|
||||||
new SysMenu{ Id=1300150011301, Pid=1300150000101, Title="胶料快检实验标准", Path="/xslmes/mesXslRubberQuickTestStd", Name="mesXslRubberQuickTestStd", Component="RubberQuickTestStdListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=112 },
|
new SysMenu{ Id=1300150011301, Pid=1300150000101, Title="胶料快检实验标准", Path="/xslmes/mesXslRubberQuickTestStd", Name="mesXslRubberQuickTestStd", Component="RubberQuickTestStdListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=112 },
|
||||||
|
// 密炼计划
|
||||||
|
new SysMenu{ Id=1300150011401, Pid=1300150000101, Title="密炼计划", Path="/xslmes/mesXslMixingProductionPlan", Name="mesXslMixingProductionPlan", Component="MixingProductionPlanListView", Icon="", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=113 },
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class SysTenantMenuSeedData : ISqlSugarEntitySeedData<SysTenantMenu>
|
|||||||
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011101},
|
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011101},
|
||||||
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011201},
|
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011201},
|
||||||
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011301},
|
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011301},
|
||||||
|
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300150011401},
|
||||||
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200012101},
|
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200012101},
|
||||||
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200012111},
|
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200012111},
|
||||||
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200012121},
|
new SysTenantMenu(){ TenantId=1300000000001,MenuId=1300200012121},
|
||||||
|
|||||||
@@ -0,0 +1,326 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Prism.Events;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Web;
|
||||||
|
using YY.Admin.Core;
|
||||||
|
using YY.Admin.Core.Entity;
|
||||||
|
using YY.Admin.Core.Events;
|
||||||
|
using YY.Admin.Core.Helper;
|
||||||
|
using YY.Admin.Core.Services;
|
||||||
|
namespace YY.Admin.Services.Service.MixingProductionPlan;
|
||||||
|
|
||||||
|
/// <summary>密炼生产计划:MES 只读拉取 + 本地缓存,断网读缓存,联网刷新</summary>
|
||||||
|
public class MixingProductionPlanService : IMixingProductionPlanService, ISingletonDependency
|
||||||
|
{
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly INetworkMonitor _networkMonitor;
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
private readonly ILoggerService _logger;
|
||||||
|
private readonly SemaphoreSlim _syncLock = new(1, 1);
|
||||||
|
private readonly object _cacheLock = new();
|
||||||
|
private readonly string _cacheFilePath;
|
||||||
|
private List<MesXslMixingProductionPlan> _localCache = new();
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions _jsonOpts = new()
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
Converters = { new NullableDateTimeJsonConverter() }
|
||||||
|
};
|
||||||
|
|
||||||
|
public MixingProductionPlanService(
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
IConfiguration configuration,
|
||||||
|
INetworkMonitor networkMonitor,
|
||||||
|
IEventAggregator eventAggregator,
|
||||||
|
ILoggerService logger)
|
||||||
|
{
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
_configuration = configuration;
|
||||||
|
_networkMonitor = networkMonitor;
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
var appDataDir = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
|
"YY.Admin", "sync-cache");
|
||||||
|
Directory.CreateDirectory(appDataDir);
|
||||||
|
_cacheFilePath = Path.Combine(appDataDir, "mes-xsl-mixing-production-plan-cache.json");
|
||||||
|
|
||||||
|
LoadCacheFromDisk();
|
||||||
|
_logger.Information($"[密炼计划] 服务初始化,缓存={_localCache.Count},在线={_networkMonitor.IsOnline}");
|
||||||
|
|
||||||
|
_networkMonitor.StatusChanged += OnNetworkStatusChanged;
|
||||||
|
if (_networkMonitor.IsOnline)
|
||||||
|
_ = Task.Run(() => SyncFromRemoteAsync(CancellationToken.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BaseUrl => (_configuration.GetValue<string>("JeecgIntegration:BaseUrl") ?? "http://localhost:8080/jeecg-boot").TrimEnd('/');
|
||||||
|
private int DefaultTenantId => (int?)_configuration.GetValue<long?>("JeecgIntegration:DefaultTenantId") ?? 1002;
|
||||||
|
private HttpClient CreateClient() => _httpClientFactory.CreateClient("JeecgApi");
|
||||||
|
|
||||||
|
public async Task<MixingProductionPlanPageResult> PageAsync(
|
||||||
|
int pageNo, int pageSize,
|
||||||
|
DateTime? planDateFrom = null,
|
||||||
|
DateTime? planDateTo = null,
|
||||||
|
string? machineName = null,
|
||||||
|
int? shiftFlag = null,
|
||||||
|
string? planNo = null,
|
||||||
|
string? materialName = null,
|
||||||
|
CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
if (_networkMonitor.IsOnline)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await SyncFromRemoteAsync(ct).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼计划] 列表拉取失败,使用本地缓存:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MesXslMixingProductionPlan> source;
|
||||||
|
lock (_cacheLock)
|
||||||
|
source = _localCache.Select(Clone).ToList();
|
||||||
|
|
||||||
|
var filtered = ApplyFilters(source, planDateFrom, planDateTo, machineName, shiftFlag, planNo, materialName);
|
||||||
|
var total = filtered.Count;
|
||||||
|
var records = filtered
|
||||||
|
.Skip(Math.Max(0, (pageNo - 1) * pageSize))
|
||||||
|
.Take(pageSize)
|
||||||
|
.ToList();
|
||||||
|
return new MixingProductionPlanPageResult(records, total, pageNo, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<MesXslMixingProductionPlan>> GetAllCachedAsync(CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
if (_networkMonitor.IsOnline)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await SyncFromRemoteAsync(ct).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼计划] 全量拉取失败,使用本地缓存:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_cacheLock)
|
||||||
|
return _localCache.Select(Clone).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SyncFromRemoteAsync(CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
await _syncLock.WaitAsync(ct).ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_networkMonitor.IsOnline)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var all = new List<MesXslMixingProductionPlan>();
|
||||||
|
int pageNo = 1;
|
||||||
|
const int pageSize = 500;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var query = HttpUtility.ParseQueryString(string.Empty);
|
||||||
|
query["pageNo"] = pageNo.ToString();
|
||||||
|
query["pageSize"] = pageSize.ToString();
|
||||||
|
query["tenantId"] = DefaultTenantId.ToString();
|
||||||
|
var url = $"{BaseUrl}/xslmes/mesXslMixingProductionPlan/anon/list?{query}";
|
||||||
|
|
||||||
|
using var client = CreateClient();
|
||||||
|
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
|
||||||
|
resp.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
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 page = resultEl.GetProperty("records")
|
||||||
|
.Deserialize<List<MesXslMixingProductionPlan>>(_jsonOpts) ?? new();
|
||||||
|
all.AddRange(page);
|
||||||
|
|
||||||
|
long total = 0;
|
||||||
|
if (resultEl.TryGetProperty("total", out var totalEl)) total = totalEl.GetInt64();
|
||||||
|
if (all.Count >= total || page.Count < pageSize) break;
|
||||||
|
pageNo++;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MesXslMixingProductionPlan> localSnapshot;
|
||||||
|
lock (_cacheLock)
|
||||||
|
localSnapshot = _localCache.Select(Clone).ToList();
|
||||||
|
|
||||||
|
var (merged, stats) = MesReadOnlyCacheMergeHelper.Merge(
|
||||||
|
localSnapshot,
|
||||||
|
all,
|
||||||
|
x => x.Id,
|
||||||
|
IsPlanContentEqual,
|
||||||
|
Clone);
|
||||||
|
|
||||||
|
if (!stats.HasChanges)
|
||||||
|
{
|
||||||
|
_logger.Information($"[密炼计划] 与 MES 对比无差异,跳过更新 count={merged.Count}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_cacheLock)
|
||||||
|
{
|
||||||
|
_localCache = merged;
|
||||||
|
SaveCacheToDiskUnsafe();
|
||||||
|
}
|
||||||
|
_logger.Information(
|
||||||
|
$"[密炼计划] 差异同步完成 total={merged.Count} 新增={stats.Added} 变更={stats.Updated} 删除={stats.Removed}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼计划] 远程同步失败:{ex.Message}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_syncLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNetworkStatusChanged(bool isOnline)
|
||||||
|
{
|
||||||
|
if (!isOnline) return;
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!await SyncFromRemoteAsync(CancellationToken.None).ConfigureAwait(false))
|
||||||
|
return;
|
||||||
|
_eventAggregator.GetEvent<MixingProductionPlanChangedEvent>()
|
||||||
|
.Publish(new MixingProductionPlanChangedPayload { Action = "reconnect" });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼计划] 重连同步失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsPlanContentEqual(MesXslMixingProductionPlan a, MesXslMixingProductionPlan b) =>
|
||||||
|
string.Equals(GetPlanFingerprint(a), GetPlanFingerprint(b), StringComparison.Ordinal);
|
||||||
|
|
||||||
|
private static string GetPlanFingerprint(MesXslMixingProductionPlan x) =>
|
||||||
|
JsonSerializer.Serialize(Clone(x), _jsonOpts);
|
||||||
|
private static List<MesXslMixingProductionPlan> ApplyFilters(
|
||||||
|
List<MesXslMixingProductionPlan> source,
|
||||||
|
DateTime? planDateFrom,
|
||||||
|
DateTime? planDateTo,
|
||||||
|
string? machineName,
|
||||||
|
int? shiftFlag,
|
||||||
|
string? planNo,
|
||||||
|
string? materialName)
|
||||||
|
{
|
||||||
|
IEnumerable<MesXslMixingProductionPlan> q = source;
|
||||||
|
if (planDateFrom.HasValue)
|
||||||
|
q = q.Where(x => x.PlanDate?.Date >= planDateFrom.Value.Date);
|
||||||
|
if (planDateTo.HasValue)
|
||||||
|
q = q.Where(x => x.PlanDate?.Date <= planDateTo.Value.Date);
|
||||||
|
if (!string.IsNullOrWhiteSpace(machineName))
|
||||||
|
q = q.Where(x => (x.MachineName ?? "").Contains(machineName.Trim(), StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (shiftFlag.HasValue && shiftFlag.Value > 0)
|
||||||
|
q = q.Where(x => x.ShiftFlag == shiftFlag.Value);
|
||||||
|
if (!string.IsNullOrWhiteSpace(planNo))
|
||||||
|
q = q.Where(x => (x.PlanNo ?? "").Contains(planNo.Trim(), StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (!string.IsNullOrWhiteSpace(materialName))
|
||||||
|
q = q.Where(x => (x.MaterialName ?? "").Contains(materialName.Trim(), StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
return q
|
||||||
|
.OrderByDescending(x => x.PlanDate ?? DateTime.MinValue)
|
||||||
|
.ThenBy(x => x.SortNo ?? int.MaxValue)
|
||||||
|
.ThenBy(x => x.MachineName)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadCacheFromDisk()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(_cacheFilePath)) return;
|
||||||
|
_localCache = JsonSerializer.Deserialize<List<MesXslMixingProductionPlan>>(
|
||||||
|
File.ReadAllText(_cacheFilePath), _jsonOpts) ?? new();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_localCache = new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveCacheToDiskUnsafe() =>
|
||||||
|
File.WriteAllText(_cacheFilePath, JsonSerializer.Serialize(_localCache, _jsonOpts));
|
||||||
|
|
||||||
|
private static MesXslMixingProductionPlan Clone(MesXslMixingProductionPlan x) => new()
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
SortNo = x.SortNo,
|
||||||
|
MachineId = x.MachineId,
|
||||||
|
MachineName = x.MachineName,
|
||||||
|
ShiftFlag = x.ShiftFlag,
|
||||||
|
PlanDate = x.PlanDate,
|
||||||
|
PlanNo = x.PlanNo,
|
||||||
|
PlanId = x.PlanId,
|
||||||
|
PlanType = x.PlanType,
|
||||||
|
SourceOrderId = x.SourceOrderId,
|
||||||
|
MaterialId = x.MaterialId,
|
||||||
|
MaterialName = x.MaterialName,
|
||||||
|
OrderNo = x.OrderNo,
|
||||||
|
OrderDate = x.OrderDate,
|
||||||
|
FormulaName = x.FormulaName,
|
||||||
|
PlanWeight = x.PlanWeight,
|
||||||
|
PlannedCarCount = x.PlannedCarCount,
|
||||||
|
ScheduledCarCount = x.ScheduledCarCount,
|
||||||
|
FinishedCarCount = x.FinishedCarCount,
|
||||||
|
PlanCount = x.PlanCount,
|
||||||
|
Remark = x.Remark,
|
||||||
|
TenantId = x.TenantId,
|
||||||
|
SysOrgCode = x.SysOrgCode,
|
||||||
|
CreateBy = x.CreateBy,
|
||||||
|
CreateTime = x.CreateTime,
|
||||||
|
UpdateBy = x.UpdateBy,
|
||||||
|
UpdateTime = x.UpdateTime,
|
||||||
|
DelFlag = x.DelFlag
|
||||||
|
};
|
||||||
|
|
||||||
|
private sealed class NullableDateTimeJsonConverter : JsonConverter<DateTime?>
|
||||||
|
{
|
||||||
|
private static readonly string[] Formats =
|
||||||
|
[
|
||||||
|
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss.fff",
|
||||||
|
"yyyy-MM-ddTHH:mm:ss", "yyyy-MM-ddTHH:mm:ss.fff",
|
||||||
|
"yyyy-MM-ddTHH:mm:ssZ", "yyyy-MM-ddTHH:mm:ss.fffZ",
|
||||||
|
"yyyy-MM-dd"
|
||||||
|
];
|
||||||
|
|
||||||
|
public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.Null) return null;
|
||||||
|
if (reader.TokenType == JsonTokenType.String)
|
||||||
|
{
|
||||||
|
var raw = reader.GetString();
|
||||||
|
if (string.IsNullOrWhiteSpace(raw)) return null;
|
||||||
|
if (DateTime.TryParseExact(raw, Formats, System.Globalization.CultureInfo.InvariantCulture,
|
||||||
|
System.Globalization.DateTimeStyles.AssumeLocal, out var dt)) return dt;
|
||||||
|
if (DateTime.TryParse(raw, out var fb)) return fb;
|
||||||
|
}
|
||||||
|
throw new JsonException($"无法转换为 DateTime?,token={reader.TokenType}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime? value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (value.HasValue) writer.WriteStringValue(value.Value.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
else writer.WriteNullValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
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.MixingProductionPlan;
|
||||||
|
|
||||||
|
public class MixingProductionPlanSyncCoordinator : ISingletonDependency
|
||||||
|
{
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
|
public MixingProductionPlanSyncCoordinator(
|
||||||
|
IEventAggregator eventAggregator,
|
||||||
|
SyncPollManager pollManager,
|
||||||
|
ILoggerService logger)
|
||||||
|
{
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
_eventAggregator.GetEvent<RemoteCommandReceivedEvent>()
|
||||||
|
.Subscribe(OnRemoteCommand, ThreadOption.BackgroundThread);
|
||||||
|
|
||||||
|
pollManager.Register("密炼计划", () =>
|
||||||
|
{
|
||||||
|
_eventAggregator.GetEvent<MixingProductionPlanChangedEvent>()
|
||||||
|
.Publish(new MixingProductionPlanChangedPayload { Action = "poll" });
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
|
||||||
|
_logger.Information("[密炼计划] MixingProductionPlanSyncCoordinator 已启动");
|
||||||
|
}
|
||||||
|
|
||||||
|
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("MIXING_PRODUCTION_PLAN_CHANGED", StringComparison.OrdinalIgnoreCase) ?? true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
doc.RootElement.TryGetProperty("action", out var actionEl);
|
||||||
|
doc.RootElement.TryGetProperty("mixingProductionPlanId", out var idEl);
|
||||||
|
|
||||||
|
var changed = new MixingProductionPlanChangedPayload
|
||||||
|
{
|
||||||
|
Action = actionEl.GetString() ?? string.Empty,
|
||||||
|
MixingProductionPlanId = idEl.ValueKind == JsonValueKind.String ? idEl.GetString() : null
|
||||||
|
};
|
||||||
|
_logger.Information($"[密炼计划] STOMP action={changed.Action}, id={changed.MixingProductionPlanId}");
|
||||||
|
_eventAggregator.GetEvent<MixingProductionPlanChangedEvent>().Publish(changed);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warning($"[密炼计划] 处理 STOMP 命令失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ public class RubberQuickTestOperationService : IRubberQuickTestOperationService,
|
|||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
private readonly INetworkMonitor _networkMonitor;
|
private readonly INetworkMonitor _networkMonitor;
|
||||||
|
private readonly IMixingProductionPlanService _mixingProductionPlanService;
|
||||||
private readonly ILoggerService _logger;
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions _jsonOpts = new()
|
private static readonly JsonSerializerOptions _jsonOpts = new()
|
||||||
@@ -27,11 +28,13 @@ public class RubberQuickTestOperationService : IRubberQuickTestOperationService,
|
|||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
INetworkMonitor networkMonitor,
|
INetworkMonitor networkMonitor,
|
||||||
|
IMixingProductionPlanService mixingProductionPlanService,
|
||||||
ILoggerService logger)
|
ILoggerService logger)
|
||||||
{
|
{
|
||||||
_httpClientFactory = httpClientFactory;
|
_httpClientFactory = httpClientFactory;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_networkMonitor = networkMonitor;
|
_networkMonitor = networkMonitor;
|
||||||
|
_mixingProductionPlanService = mixingProductionPlanService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,32 +43,7 @@ public class RubberQuickTestOperationService : IRubberQuickTestOperationService,
|
|||||||
|
|
||||||
public async Task<List<MesXslMixingProductionPlan>> GetMixingProductionPlansAsync(CancellationToken ct = default)
|
public async Task<List<MesXslMixingProductionPlan>> GetMixingProductionPlansAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (!_networkMonitor.IsOnline)
|
var result = await _mixingProductionPlanService.GetAllCachedAsync(ct).ConfigureAwait(false);
|
||||||
throw new InvalidOperationException("网络未连接,无法加载密炼生产计划");
|
|
||||||
|
|
||||||
var result = new List<MesXslMixingProductionPlan>();
|
|
||||||
int pageNo = 1;
|
|
||||||
const int pageSize = 500;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var url = $"{BaseUrl}/xslmes/mesXslMixingProductionPlan/anon/list?pageNo={pageNo}&pageSize={pageSize}&tenantId={DefaultTenantId}";
|
|
||||||
using var client = _httpClientFactory.CreateClient("JeecgApi");
|
|
||||||
var resp = await client.GetAsync(url, ct).ConfigureAwait(false);
|
|
||||||
resp.EnsureSuccessStatusCode();
|
|
||||||
var json = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
|
||||||
using var doc = JsonDocument.Parse(json);
|
|
||||||
if (!doc.RootElement.TryGetProperty("result", out var resultEl)) break;
|
|
||||||
if (resultEl.TryGetProperty("records", out var recordsEl))
|
|
||||||
{
|
|
||||||
var page = recordsEl.Deserialize<List<MesXslMixingProductionPlan>>(_jsonOpts);
|
|
||||||
if (page != null) result.AddRange(page);
|
|
||||||
}
|
|
||||||
long total = 0;
|
|
||||||
if (resultEl.TryGetProperty("total", out var totalEl)) total = totalEl.GetInt64();
|
|
||||||
if (result.Count >= total || (resultEl.TryGetProperty("records", out var r2) && r2.GetArrayLength() < pageSize)) break;
|
|
||||||
pageNo++;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Information($"[快检记录] 加载密炼生产计划 {result.Count} 条");
|
_logger.Information($"[快检记录] 加载密炼生产计划 {result.Count} 条");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using System.Web;
|
|||||||
using YY.Admin.Core;
|
using YY.Admin.Core;
|
||||||
using YY.Admin.Core.Entity;
|
using YY.Admin.Core.Entity;
|
||||||
using YY.Admin.Core.Events;
|
using YY.Admin.Core.Events;
|
||||||
|
using YY.Admin.Core.Helper;
|
||||||
using YY.Admin.Core.Services;
|
using YY.Admin.Core.Services;
|
||||||
|
|
||||||
namespace YY.Admin.Services.Service.RubberQuickTestStd;
|
namespace YY.Admin.Services.Service.RubberQuickTestStd;
|
||||||
@@ -115,7 +116,7 @@ public class RubberQuickTestStdService : IRubberQuickTestStdService, ISingletonD
|
|||||||
var entity = resultEl.Deserialize<MesXslRubberQuickTestStd>(_jsonOpts);
|
var entity = resultEl.Deserialize<MesXslRubberQuickTestStd>(_jsonOpts);
|
||||||
if (entity != null)
|
if (entity != null)
|
||||||
{
|
{
|
||||||
UpsertLocalCacheMain(entity);
|
UpsertIfChanged(entity);
|
||||||
return CloneMain(entity);
|
return CloneMain(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,14 +135,14 @@ public class RubberQuickTestStdService : IRubberQuickTestStdService, ISingletonD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SyncFromRemoteAsync(CancellationToken ct = default)
|
public async Task<bool> SyncFromRemoteAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
await _syncLock.WaitAsync(ct).ConfigureAwait(false);
|
await _syncLock.WaitAsync(ct).ConfigureAwait(false);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!_networkMonitor.IsOnline)
|
if (!_networkMonitor.IsOnline)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
var query = HttpUtility.ParseQueryString(string.Empty);
|
var query = HttpUtility.ParseQueryString(string.Empty);
|
||||||
query["pageNo"] = "1";
|
query["pageNo"] = "1";
|
||||||
@@ -158,16 +159,37 @@ public class RubberQuickTestStdService : IRubberQuickTestStdService, ISingletonD
|
|||||||
var records = doc.RootElement.GetProperty("result").GetProperty("records")
|
var records = doc.RootElement.GetProperty("result").GetProperty("records")
|
||||||
.Deserialize<List<MesXslRubberQuickTestStd>>(_jsonOpts) ?? new();
|
.Deserialize<List<MesXslRubberQuickTestStd>>(_jsonOpts) ?? new();
|
||||||
|
|
||||||
|
List<MesXslRubberQuickTestStd> localSnapshot;
|
||||||
|
lock (_cacheLock)
|
||||||
|
localSnapshot = _localCache.Select(CloneMain).ToList();
|
||||||
|
|
||||||
|
var (merged, stats) = MesReadOnlyCacheMergeHelper.Merge(
|
||||||
|
localSnapshot,
|
||||||
|
records,
|
||||||
|
x => x.Id,
|
||||||
|
IsStdListContentEqual,
|
||||||
|
CloneMain,
|
||||||
|
MergeStdUpdated);
|
||||||
|
|
||||||
|
if (!stats.HasChanges)
|
||||||
|
{
|
||||||
|
_logger.Information($"[快检实验标准] 与 MES 对比无差异,跳过更新 count={merged.Count}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
lock (_cacheLock)
|
lock (_cacheLock)
|
||||||
{
|
{
|
||||||
_localCache = records.Select(CloneMain).ToList();
|
_localCache = merged;
|
||||||
SaveCacheToDiskUnsafe();
|
SaveCacheToDiskUnsafe();
|
||||||
}
|
}
|
||||||
_logger.Information($"[快检实验标准] 同步完成 count={records.Count}");
|
_logger.Information(
|
||||||
|
$"[快检实验标准] 差异同步完成 total={merged.Count} 新增={stats.Added} 变更={stats.Updated} 删除={stats.Removed}");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Warning($"[快检实验标准] 远程同步失败:{ex.Message}");
|
_logger.Warning($"[快检实验标准] 远程同步失败:{ex.Message}");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -180,7 +202,8 @@ public class RubberQuickTestStdService : IRubberQuickTestStdService, ISingletonD
|
|||||||
if (!isOnline) return;
|
if (!isOnline) return;
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await SyncFromRemoteAsync(CancellationToken.None).ConfigureAwait(false);
|
if (!await SyncFromRemoteAsync(CancellationToken.None).ConfigureAwait(false))
|
||||||
|
return;
|
||||||
_eventAggregator.GetEvent<RubberQuickTestStdChangedEvent>()
|
_eventAggregator.GetEvent<RubberQuickTestStdChangedEvent>()
|
||||||
.Publish(new RubberQuickTestStdChangedPayload { Action = "reconnect" });
|
.Publish(new RubberQuickTestStdChangedPayload { Action = "reconnect" });
|
||||||
});
|
});
|
||||||
@@ -202,19 +225,67 @@ public class RubberQuickTestStdService : IRubberQuickTestStdService, ISingletonD
|
|||||||
return q.OrderByDescending(x => x.CreateTime ?? DateTime.MinValue).ToList();
|
return q.OrderByDescending(x => x.CreateTime ?? DateTime.MinValue).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpsertLocalCacheMain(MesXslRubberQuickTestStd entity)
|
private bool UpsertIfChanged(MesXslRubberQuickTestStd entity)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(entity.Id)) return;
|
if (string.IsNullOrWhiteSpace(entity.Id)) return false;
|
||||||
lock (_cacheLock)
|
lock (_cacheLock)
|
||||||
{
|
{
|
||||||
var idx = _localCache.FindIndex(x => string.Equals(x.Id, entity.Id, StringComparison.OrdinalIgnoreCase));
|
var idx = _localCache.FindIndex(x => string.Equals(x.Id, entity.Id, StringComparison.OrdinalIgnoreCase));
|
||||||
var copy = CloneMain(entity);
|
if (idx >= 0)
|
||||||
if (idx >= 0) _localCache[idx] = copy;
|
{
|
||||||
else _localCache.Insert(0, copy);
|
if (IsStdDetailContentEqual(_localCache[idx], entity))
|
||||||
|
return false;
|
||||||
|
_localCache[idx] = CloneMain(entity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_localCache.Insert(0, CloneMain(entity));
|
||||||
|
}
|
||||||
SaveCacheToDiskUnsafe();
|
SaveCacheToDiskUnsafe();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsStdListContentEqual(MesXslRubberQuickTestStd a, MesXslRubberQuickTestStd b) =>
|
||||||
|
string.Equals(GetStdListFingerprint(a), GetStdListFingerprint(b), StringComparison.Ordinal);
|
||||||
|
|
||||||
|
private static bool IsStdDetailContentEqual(MesXslRubberQuickTestStd a, MesXslRubberQuickTestStd b) =>
|
||||||
|
string.Equals(GetStdDetailFingerprint(a), GetStdDetailFingerprint(b), StringComparison.Ordinal);
|
||||||
|
|
||||||
|
private static string GetStdListFingerprint(MesXslRubberQuickTestStd x)
|
||||||
|
{
|
||||||
|
var snap = CloneMain(x);
|
||||||
|
snap.LineList = null;
|
||||||
|
return JsonSerializer.Serialize(snap, _jsonOpts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetStdDetailFingerprint(MesXslRubberQuickTestStd x) =>
|
||||||
|
JsonSerializer.Serialize(CloneMain(x), _jsonOpts);
|
||||||
|
|
||||||
|
private static MesXslRubberQuickTestStd MergeStdUpdated(MesXslRubberQuickTestStd local, MesXslRubberQuickTestStd remote)
|
||||||
|
{
|
||||||
|
var copy = CloneMain(remote);
|
||||||
|
if ((copy.LineList == null || copy.LineList.Count == 0) && local.LineList is { Count: > 0 })
|
||||||
|
{
|
||||||
|
copy.LineList = local.LineList.Select(l => new MesXslRubberQuickTestStdLine
|
||||||
|
{
|
||||||
|
Id = l.Id,
|
||||||
|
StdId = l.StdId,
|
||||||
|
DataPointId = l.DataPointId,
|
||||||
|
PointName = l.PointName,
|
||||||
|
LowerLimit = l.LowerLimit,
|
||||||
|
UpperLimit = l.UpperLimit,
|
||||||
|
LowerWarn = l.LowerWarn,
|
||||||
|
UpperWarn = l.UpperWarn,
|
||||||
|
TargetValue = l.TargetValue,
|
||||||
|
SortNo = l.SortNo
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpsertLocalCacheMain(MesXslRubberQuickTestStd entity) => UpsertIfChanged(entity);
|
||||||
|
|
||||||
private void LoadCacheFromDisk()
|
private void LoadCacheFromDisk()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Prism.Events;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using YY.Admin.Core;
|
using YY.Admin.Core;
|
||||||
using YY.Admin.Core.Events;
|
using YY.Admin.Core.Events;
|
||||||
using YY.Admin.Core.Events;
|
|
||||||
|
|
||||||
namespace YY.Admin.Services.Service.RubberQuickTestStd;
|
namespace YY.Admin.Services.Service.RubberQuickTestStd;
|
||||||
|
|
||||||
@@ -21,8 +20,6 @@ public class RubberQuickTestStdSyncCoordinator : ISingletonDependency
|
|||||||
|
|
||||||
_eventAggregator.GetEvent<RemoteCommandReceivedEvent>()
|
_eventAggregator.GetEvent<RemoteCommandReceivedEvent>()
|
||||||
.Subscribe(OnRemoteCommand, ThreadOption.BackgroundThread);
|
.Subscribe(OnRemoteCommand, ThreadOption.BackgroundThread);
|
||||||
_eventAggregator.GetEvent<NetworkStatusChangedEvent>()
|
|
||||||
.Subscribe(OnNetworkStatusChanged, ThreadOption.BackgroundThread);
|
|
||||||
|
|
||||||
pollManager.Register("胶料快检实验标准", () =>
|
pollManager.Register("胶料快检实验标准", () =>
|
||||||
{
|
{
|
||||||
@@ -34,14 +31,6 @@ public class RubberQuickTestStdSyncCoordinator : ISingletonDependency
|
|||||||
_logger.Information("[快检实验标准] RubberQuickTestStdSyncCoordinator 已启动");
|
_logger.Information("[快检实验标准] RubberQuickTestStdSyncCoordinator 已启动");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNetworkStatusChanged(NetworkStatusChangedPayload payload)
|
|
||||||
{
|
|
||||||
if (!payload.IsOnline) return;
|
|
||||||
_logger.Information("[快检实验标准] 网络恢复,触发补偿刷新");
|
|
||||||
_eventAggregator.GetEvent<RubberQuickTestStdChangedEvent>()
|
|
||||||
.Publish(new RubberQuickTestStdChangedPayload { Action = "reconnect" });
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRemoteCommand(RemoteCommandPayload payload)
|
private void OnRemoteCommand(RemoteCommandPayload payload)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -178,6 +178,10 @@ public class StompWebSocketService : ISignalRService
|
|||||||
await SendFrameAsync(
|
await SendFrameAsync(
|
||||||
BuildSubscribeFrame("sub-mes-rubber-quick-test-stds", "/topic/sync/mes-rubber-quick-test-stds"),
|
BuildSubscribeFrame("sub-mes-rubber-quick-test-stds", "/topic/sync/mes-rubber-quick-test-stds"),
|
||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
// 密炼生产计划变更:订阅 /topic/sync/mes-mixing-production-plans
|
||||||
|
await SendFrameAsync(
|
||||||
|
BuildSubscribeFrame("sub-mes-xsl-mixing-production-plan", "/topic/sync/mes-mixing-production-plans"),
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
// 订阅服务端 PONG 回复(应用层假在线检测)
|
// 订阅服务端 PONG 回复(应用层假在线检测)
|
||||||
await SendFrameAsync(
|
await SendFrameAsync(
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using YY.Admin.Views.Print;
|
|||||||
using YY.Admin.Views.MixerMaterialTareStrategy;
|
using YY.Admin.Views.MixerMaterialTareStrategy;
|
||||||
using YY.Admin.Views.RubberQuickTest;
|
using YY.Admin.Views.RubberQuickTest;
|
||||||
using YY.Admin.Views.RubberQuickTestStd;
|
using YY.Admin.Views.RubberQuickTestStd;
|
||||||
|
using YY.Admin.Views.MixingProductionPlan;
|
||||||
|
|
||||||
namespace YY.Admin
|
namespace YY.Admin
|
||||||
{
|
{
|
||||||
@@ -101,6 +102,8 @@ namespace YY.Admin
|
|||||||
containerRegistry.RegisterForNavigation<RubberQuickTestOperationView>();
|
containerRegistry.RegisterForNavigation<RubberQuickTestOperationView>();
|
||||||
// 胶料快检实验标准(只读)
|
// 胶料快检实验标准(只读)
|
||||||
containerRegistry.RegisterForNavigation<RubberQuickTestStdListView>();
|
containerRegistry.RegisterForNavigation<RubberQuickTestStdListView>();
|
||||||
|
// 密炼计划(只读)
|
||||||
|
containerRegistry.RegisterForNavigation<MixingProductionPlanListView>();
|
||||||
// 打印设置
|
// 打印设置
|
||||||
containerRegistry.RegisterForNavigation<PrintSettingsView>();
|
containerRegistry.RegisterForNavigation<PrintSettingsView>();
|
||||||
// 打印模板列表
|
// 打印模板列表
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ using YY.Admin.Services.Service.WeightRecord;
|
|||||||
using YY.Admin.Services.Service.Print;
|
using YY.Admin.Services.Service.Print;
|
||||||
using YY.Admin.Services.Service.RubberQuickTest;
|
using YY.Admin.Services.Service.RubberQuickTest;
|
||||||
using YY.Admin.Services.Service.RubberQuickTestStd;
|
using YY.Admin.Services.Service.RubberQuickTestStd;
|
||||||
|
using YY.Admin.Services.Service.MixingProductionPlan;
|
||||||
|
|
||||||
namespace YY.Admin.Module;
|
namespace YY.Admin.Module;
|
||||||
|
|
||||||
@@ -95,6 +96,10 @@ public class SyncModule : IModule
|
|||||||
containerRegistry.RegisterSingleton<IRubberQuickTestStdService, RubberQuickTestStdService>();
|
containerRegistry.RegisterSingleton<IRubberQuickTestStdService, RubberQuickTestStdService>();
|
||||||
containerRegistry.RegisterSingleton<RubberQuickTestStdSyncCoordinator>();
|
containerRegistry.RegisterSingleton<RubberQuickTestStdSyncCoordinator>();
|
||||||
|
|
||||||
|
// 密炼计划(MES 只读同步)
|
||||||
|
containerRegistry.RegisterSingleton<IMixingProductionPlanService, MixingProductionPlanService>();
|
||||||
|
containerRegistry.RegisterSingleton<MixingProductionPlanSyncCoordinator>();
|
||||||
|
|
||||||
var serviceCollection = new ServiceCollection();
|
var serviceCollection = new ServiceCollection();
|
||||||
serviceCollection.AddTransient<DisconnectGuardHandler>();
|
serviceCollection.AddTransient<DisconnectGuardHandler>();
|
||||||
serviceCollection.AddHttpClient("JeecgApi", (sp, client) =>
|
serviceCollection.AddHttpClient("JeecgApi", (sp, client) =>
|
||||||
@@ -167,6 +172,8 @@ public class SyncModule : IModule
|
|||||||
_ = containerProvider.Resolve<PrintBizTemplateBindSyncCoordinator>();
|
_ = containerProvider.Resolve<PrintBizTemplateBindSyncCoordinator>();
|
||||||
// 胶料快检实验标准只读同步协调器
|
// 胶料快检实验标准只读同步协调器
|
||||||
_ = containerProvider.Resolve<RubberQuickTestStdSyncCoordinator>();
|
_ = containerProvider.Resolve<RubberQuickTestStdSyncCoordinator>();
|
||||||
|
// 密炼计划只读同步协调器
|
||||||
|
_ = containerProvider.Resolve<MixingProductionPlanSyncCoordinator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
|
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
|
||||||
|
|||||||
@@ -162,6 +162,11 @@ namespace YY.Admin.ViewModels.Control
|
|||||||
["/xslmes/mesXslRubberQuickTestStd"] = "RubberQuickTestStdListView",
|
["/xslmes/mesXslRubberQuickTestStd"] = "RubberQuickTestStdListView",
|
||||||
["mesXslRubberQuickTestStd"] = "RubberQuickTestStdListView",
|
["mesXslRubberQuickTestStd"] = "RubberQuickTestStdListView",
|
||||||
|
|
||||||
|
// 已实现页面:密炼计划(只读)
|
||||||
|
["MixingProductionPlanListView"] = "MixingProductionPlanListView",
|
||||||
|
["/xslmes/mesXslMixingProductionPlan"] = "MixingProductionPlanListView",
|
||||||
|
["mesXslMixingProductionPlan"] = "MixingProductionPlanListView",
|
||||||
|
|
||||||
// 已实现页面:打印设置
|
// 已实现页面:打印设置
|
||||||
["PrintSettingsView"] = "PrintSettingsView",
|
["PrintSettingsView"] = "PrintSettingsView",
|
||||||
["/system/printSettings"] = "PrintSettingsView",
|
["/system/printSettings"] = "PrintSettingsView",
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
using HandyControl.Controls;
|
||||||
|
using Prism.Events;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using YY.Admin.Core;
|
||||||
|
using YY.Admin.Core.Entity;
|
||||||
|
using YY.Admin.Core.Events;
|
||||||
|
using YY.Admin.Core.Helper;
|
||||||
|
using YY.Admin.Core.Services;
|
||||||
|
using YY.Admin.Services.Service;
|
||||||
|
|
||||||
|
namespace YY.Admin.ViewModels.MixingProductionPlan;
|
||||||
|
|
||||||
|
public class MixingProductionPlanListViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
private readonly IMixingProductionPlanService _planService;
|
||||||
|
private SubscriptionToken? _changedToken;
|
||||||
|
|
||||||
|
private ObservableCollection<MesXslMixingProductionPlan> _items = new();
|
||||||
|
public ObservableCollection<MesXslMixingProductionPlan> Items
|
||||||
|
{
|
||||||
|
get => _items;
|
||||||
|
set => SetProperty(ref _items, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long _total;
|
||||||
|
public long Total { get => _total; set => SetProperty(ref _total, value); }
|
||||||
|
|
||||||
|
private int _pageNo = 1;
|
||||||
|
public int PageNo { get => _pageNo; set => SetProperty(ref _pageNo, value); }
|
||||||
|
|
||||||
|
private int _pageSize = 20;
|
||||||
|
public int PageSize { get => _pageSize; set => SetProperty(ref _pageSize, value); }
|
||||||
|
|
||||||
|
private DateTime? _filterPlanDateFrom;
|
||||||
|
public DateTime? FilterPlanDateFrom { get => _filterPlanDateFrom; set => SetProperty(ref _filterPlanDateFrom, value); }
|
||||||
|
|
||||||
|
private DateTime? _filterPlanDateTo;
|
||||||
|
public DateTime? FilterPlanDateTo { get => _filterPlanDateTo; set => SetProperty(ref _filterPlanDateTo, value); }
|
||||||
|
|
||||||
|
private string? _filterMachineName;
|
||||||
|
public string? FilterMachineName { get => _filterMachineName; set => SetProperty(ref _filterMachineName, value); }
|
||||||
|
|
||||||
|
private int? _filterShiftFlag;
|
||||||
|
public int? FilterShiftFlag { get => _filterShiftFlag; set => SetProperty(ref _filterShiftFlag, value); }
|
||||||
|
|
||||||
|
private string? _filterPlanNo;
|
||||||
|
public string? FilterPlanNo { get => _filterPlanNo; set => SetProperty(ref _filterPlanNo, value); }
|
||||||
|
|
||||||
|
private string? _filterMaterialName;
|
||||||
|
public string? FilterMaterialName { get => _filterMaterialName; set => SetProperty(ref _filterMaterialName, value); }
|
||||||
|
|
||||||
|
public ObservableCollection<KeyValuePair<string, int?>> ShiftOptions { get; } = new()
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, int?>("全部", null),
|
||||||
|
new KeyValuePair<string, int?>("早班", 1),
|
||||||
|
new KeyValuePair<string, int?>("中班", 2),
|
||||||
|
new KeyValuePair<string, int?>("晚班", 3)
|
||||||
|
};
|
||||||
|
|
||||||
|
public DelegateCommand SearchCommand { get; }
|
||||||
|
public DelegateCommand ResetCommand { get; }
|
||||||
|
public DelegateCommand PrevPageCommand { get; }
|
||||||
|
public DelegateCommand NextPageCommand { get; }
|
||||||
|
|
||||||
|
public MixingProductionPlanListViewModel(
|
||||||
|
IMixingProductionPlanService planService,
|
||||||
|
IContainerExtension container,
|
||||||
|
IRegionManager regionManager) : base(container, regionManager)
|
||||||
|
{
|
||||||
|
_planService = planService;
|
||||||
|
|
||||||
|
SearchCommand = new DelegateCommand(async () => { PageNo = 1; await LoadAsync(); });
|
||||||
|
ResetCommand = new DelegateCommand(async () =>
|
||||||
|
{
|
||||||
|
FilterPlanDateFrom = null;
|
||||||
|
FilterPlanDateTo = null;
|
||||||
|
FilterMachineName = null;
|
||||||
|
FilterShiftFlag = null;
|
||||||
|
FilterPlanNo = null;
|
||||||
|
FilterMaterialName = null;
|
||||||
|
PageNo = 1;
|
||||||
|
await LoadAsync();
|
||||||
|
});
|
||||||
|
PrevPageCommand = new DelegateCommand(async () => { if (PageNo > 1) { PageNo--; await LoadAsync(); } });
|
||||||
|
NextPageCommand = new DelegateCommand(async () => { if ((long)PageNo * PageSize < Total) { PageNo++; await LoadAsync(); } });
|
||||||
|
|
||||||
|
_changedToken = _eventAggregator.GetEvent<MixingProductionPlanChangedEvent>()
|
||||||
|
.Subscribe(async _ => await LoadAsync(), ThreadOption.UIThread);
|
||||||
|
|
||||||
|
_ = InitializeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await UIHelper.WaitForRenderAsync();
|
||||||
|
await LoadAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"密炼计划列表初始化失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LoadAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IsLoading = true;
|
||||||
|
var result = await _planService.PageAsync(
|
||||||
|
PageNo, PageSize,
|
||||||
|
FilterPlanDateFrom, FilterPlanDateTo,
|
||||||
|
FilterMachineName, FilterShiftFlag,
|
||||||
|
FilterPlanNo, FilterMaterialName);
|
||||||
|
Items = new ObservableCollection<MesXslMixingProductionPlan>(result.Records);
|
||||||
|
Total = result.Total;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Growl.Error($"加载密炼计划失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CleanUp()
|
||||||
|
{
|
||||||
|
base.CleanUp();
|
||||||
|
if (_changedToken != null)
|
||||||
|
{
|
||||||
|
_eventAggregator.GetEvent<MixingProductionPlanChangedEvent>().Unsubscribe(_changedToken);
|
||||||
|
_changedToken = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,9 +25,10 @@ public class MixingPlanShiftOption
|
|||||||
public string? PlanId { get; set; }
|
public string? PlanId { get; set; }
|
||||||
public string? OrderNo { get; set; }
|
public string? OrderNo { get; set; }
|
||||||
public string? FormulaName { get; set; }
|
public string? FormulaName { get; set; }
|
||||||
|
public string? MaterialName { get; set; }
|
||||||
public string DisplayText => string.IsNullOrWhiteSpace(OrderNo)
|
public string DisplayText => string.IsNullOrWhiteSpace(OrderNo)
|
||||||
? FormulaName ?? string.Empty
|
? MaterialName ?? FormulaName ?? string.Empty
|
||||||
: $"{OrderNo} | {FormulaName}";
|
: $"{OrderNo} | {MaterialName ?? FormulaName}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public class QuickTestInspectCellViewModel : BindableBase
|
public class QuickTestInspectCellViewModel : BindableBase
|
||||||
@@ -217,7 +218,7 @@ public class RubberQuickTestOperationViewModel : BaseViewModel
|
|||||||
public string? ProductionOrderNo => _selectedPlan?.OrderNo;
|
public string? ProductionOrderNo => _selectedPlan?.OrderNo;
|
||||||
public string? MachineName => _selectedPlan?.MachineName ?? SelectedMachine;
|
public string? MachineName => _selectedPlan?.MachineName ?? SelectedMachine;
|
||||||
public string? WorkShiftDisplay => SelectedShift?.Name ?? string.Empty;
|
public string? WorkShiftDisplay => SelectedShift?.Name ?? string.Empty;
|
||||||
public string? RubberMaterialName => _selectedPlan?.FormulaName;
|
public string? RubberMaterialName => _selectedPlan?.MaterialName ?? _selectedPlan?.FormulaName;
|
||||||
|
|
||||||
private string? _trainNo;
|
private string? _trainNo;
|
||||||
public string? TrainNo
|
public string? TrainNo
|
||||||
@@ -286,32 +287,25 @@ public class RubberQuickTestOperationViewModel : BaseViewModel
|
|||||||
_allShiftOptions.Clear();
|
_allShiftOptions.Clear();
|
||||||
foreach (var row in _allPlans)
|
foreach (var row in _allPlans)
|
||||||
{
|
{
|
||||||
AddShiftOption(row, "morning", "1", "早班", row.MorningPlanId, row.MorningOrderDate, row.MorningOrderNo, row.MorningFormulaName);
|
if (string.IsNullOrWhiteSpace(row.PlanId)) continue;
|
||||||
AddShiftOption(row, "noon", "2", "中班", row.NoonPlanId, row.NoonOrderDate, row.NoonOrderNo, row.NoonFormulaName);
|
var shiftCode = row.ShiftFlag?.ToString() ?? string.Empty;
|
||||||
AddShiftOption(row, "night", "3", "晚班", row.NightPlanId, row.NightOrderDate, row.NightOrderNo, row.NightFormulaName);
|
_allShiftOptions.Add(new MixingPlanShiftOption
|
||||||
|
{
|
||||||
|
PlanRowId = row.Id ?? string.Empty,
|
||||||
|
MachineId = row.MachineId,
|
||||||
|
MachineName = row.MachineName,
|
||||||
|
ShiftKey = shiftCode,
|
||||||
|
ShiftCode = shiftCode,
|
||||||
|
ShiftName = row.ShiftFlagText,
|
||||||
|
OrderDate = row.PlanDate,
|
||||||
|
PlanId = row.PlanId,
|
||||||
|
OrderNo = row.OrderNo,
|
||||||
|
FormulaName = row.FormulaName,
|
||||||
|
MaterialName = row.MaterialName
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddShiftOption(
|
|
||||||
MesXslMixingProductionPlan row, string shiftKey, string shiftCode, string shiftName,
|
|
||||||
string? planId, DateTime? orderDate, string? orderNo, string? formulaName)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(planId) || string.IsNullOrWhiteSpace(orderNo)) return;
|
|
||||||
_allShiftOptions.Add(new MixingPlanShiftOption
|
|
||||||
{
|
|
||||||
PlanRowId = row.Id ?? string.Empty,
|
|
||||||
MachineId = row.MachineId,
|
|
||||||
MachineName = row.MachineName,
|
|
||||||
ShiftKey = shiftKey,
|
|
||||||
ShiftCode = shiftCode,
|
|
||||||
ShiftName = shiftName,
|
|
||||||
OrderDate = orderDate,
|
|
||||||
PlanId = planId,
|
|
||||||
OrderNo = orderNo,
|
|
||||||
FormulaName = formulaName
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<MixingPlanShiftOption> FilteredByDate =>
|
private IEnumerable<MixingPlanShiftOption> FilteredByDate =>
|
||||||
_allShiftOptions.Where(o => o.OrderDate?.Date == MixingDate.Date);
|
_allShiftOptions.Where(o => o.OrderDate?.Date == MixingDate.Date);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<UserControl x:Class="YY.Admin.Views.MixingProductionPlan.MixingProductionPlanListView"
|
||||||
|
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">
|
||||||
|
|
||||||
|
<Grid Style="{StaticResource BaseViewStyle}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Border Grid.Row="0" CornerRadius="4" Margin="0 0 -10 0">
|
||||||
|
<hc:Row>
|
||||||
|
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
||||||
|
<hc:DatePicker SelectedDate="{Binding FilterPlanDateFrom}"
|
||||||
|
Margin="0 0 10 10"
|
||||||
|
hc:InfoElement.Title="密炼日期起"
|
||||||
|
hc:InfoElement.TitlePlacement="Left"
|
||||||
|
hc:InfoElement.TitleWidth="80"
|
||||||
|
hc:InfoElement.Placeholder="开始日期"/>
|
||||||
|
</hc:Col>
|
||||||
|
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
||||||
|
<hc:DatePicker SelectedDate="{Binding FilterPlanDateTo}"
|
||||||
|
Margin="0 0 10 10"
|
||||||
|
hc:InfoElement.Title="密炼日期止"
|
||||||
|
hc:InfoElement.TitlePlacement="Left"
|
||||||
|
hc:InfoElement.TitleWidth="80"
|
||||||
|
hc:InfoElement.Placeholder="结束日期"/>
|
||||||
|
</hc:Col>
|
||||||
|
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
||||||
|
<hc:TextBox Text="{Binding FilterMachineName, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Margin="0 0 10 10"
|
||||||
|
hc:InfoElement.Title="机台名称"
|
||||||
|
hc:InfoElement.TitlePlacement="Left"
|
||||||
|
hc:InfoElement.TitleWidth="80"
|
||||||
|
hc:InfoElement.Placeholder="机台名称"
|
||||||
|
hc:InfoElement.ShowClearButton="True"/>
|
||||||
|
</hc:Col>
|
||||||
|
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
||||||
|
<hc:ComboBox SelectedValuePath="Value"
|
||||||
|
DisplayMemberPath="Key"
|
||||||
|
ItemsSource="{Binding ShiftOptions}"
|
||||||
|
SelectedValue="{Binding FilterShiftFlag}"
|
||||||
|
Margin="0 0 10 10"
|
||||||
|
hc:InfoElement.Title="班次"
|
||||||
|
hc:InfoElement.TitlePlacement="Left"
|
||||||
|
hc:InfoElement.TitleWidth="80"
|
||||||
|
hc:InfoElement.Placeholder="全部"/>
|
||||||
|
</hc:Col>
|
||||||
|
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
||||||
|
<hc:TextBox Text="{Binding FilterPlanNo, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Margin="0 0 10 10"
|
||||||
|
hc:InfoElement.Title="计划号"
|
||||||
|
hc:InfoElement.TitlePlacement="Left"
|
||||||
|
hc:InfoElement.TitleWidth="80"
|
||||||
|
hc:InfoElement.Placeholder="计划号"
|
||||||
|
hc:InfoElement.ShowClearButton="True"/>
|
||||||
|
</hc:Col>
|
||||||
|
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
|
||||||
|
<hc:TextBox Text="{Binding FilterMaterialName, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Margin="0 0 10 10"
|
||||||
|
hc:InfoElement.Title="胶料名称"
|
||||||
|
hc:InfoElement.TitlePlacement="Left"
|
||||||
|
hc:InfoElement.TitleWidth="80"
|
||||||
|
hc:InfoElement.Placeholder="胶料名称"
|
||||||
|
hc:InfoElement.ShowClearButton="True"/>
|
||||||
|
</hc:Col>
|
||||||
|
</hc:Row>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Grid.Row="1" Margin="0,10">
|
||||||
|
<hc:UniformSpacingPanel Spacing="10">
|
||||||
|
<Button Style="{StaticResource ButtonPrimary}" Command="{Binding SearchCommand}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<md:PackIcon Kind="Search"/>
|
||||||
|
<TextBlock Text="搜索" Style="{StaticResource IconButtonStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button Style="{StaticResource ButtonDefault}" Command="{Binding ResetCommand}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<md:PackIcon Kind="Refresh"/>
|
||||||
|
<TextBlock Text="重置" Style="{StaticResource IconButtonStyle}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<TextBlock Text="数据来自 MES 密炼生产计划维护,桌面端只读;断网时显示本地缓存,联网后自动刷新"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||||
|
FontSize="12"/>
|
||||||
|
</hc:UniformSpacingPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<DataGrid Grid.Row="2"
|
||||||
|
ItemsSource="{Binding Items}"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
SelectionMode="Single"
|
||||||
|
GridLinesVisibility="Horizontal"
|
||||||
|
HorizontalGridLinesBrush="#FFEDEDED"
|
||||||
|
VerticalGridLinesBrush="Transparent"
|
||||||
|
HeadersVisibility="All"
|
||||||
|
ColumnHeaderStyle="{StaticResource CusDataGridColumnHeaderStyle}"
|
||||||
|
Style="{StaticResource CusDataGridStyle}"
|
||||||
|
VerticalScrollBarVisibility="Auto"
|
||||||
|
HorizontalScrollBarVisibility="Auto">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="密炼日期" Binding="{Binding PlanDateText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="110"/>
|
||||||
|
<DataGridTextColumn Header="机台名称" Binding="{Binding MachineName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
|
||||||
|
<DataGridTextColumn Header="班次" Binding="{Binding ShiftFlagText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="80"/>
|
||||||
|
<DataGridTextColumn Header="计划号" Binding="{Binding PlanNo}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
|
||||||
|
<DataGridTextColumn Header="计划数量" Binding="{Binding PlanCount}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="90"/>
|
||||||
|
<DataGridTextColumn Header="胶料名称" Binding="{Binding MaterialName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="*"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<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"
|
||||||
|
Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||||
|
<Button Content="上一页" Command="{Binding PrevPageCommand}" Style="{StaticResource ButtonDefault}" Margin="0,0,4,0" Width="80"/>
|
||||||
|
<TextBlock Text="{Binding PageNo, StringFormat=第 {0} 页}" VerticalAlignment="Center" Margin="8,0"
|
||||||
|
Foreground="{DynamicResource PrimaryTextBrush}"/>
|
||||||
|
<Button Content="下一页" Command="{Binding NextPageCommand}" Style="{StaticResource ButtonDefault}" Width="80"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace YY.Admin.Views.MixingProductionPlan;
|
||||||
|
|
||||||
|
public partial class MixingProductionPlanListView : UserControl
|
||||||
|
{
|
||||||
|
public MixingProductionPlanListView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user