kyy
2025-11-20 aaa11d26637075b5c6d0c2162359dbafbe09de87
DevApp/Gs.DevApp/DevFrm/QC/MesQcExceptional.cs
@@ -9,22 +9,22 @@
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Gs.DevApp.DevFrm.QC
{
    public partial class MesQcExceptional : DevExpress.XtraEditors.XtraForm
    {
        string _webServiceName = "MesQcExceptionalManager/";
        List<FilterEntity> _filterList = new List<FilterEntity>();
        public void Initialize(string param)
        {
            // 使用param进行初始化工作
            Gs.DevApp.ToolBox.UtilityHelper.JumpTab(xtraTabControl1, 0);
            getModel(param);
        }
        public MesQcExceptional()
        {
            InitializeComponent();
@@ -37,7 +37,7 @@
            toolBarMenu1.btnFChkClick += ToolBarMenu1_btnFChkClick;
            toolBarMenu1.btnChkClick += ToolBarMenu1_btnChkClick;
            this.toolBarMenu1.btnEscClick += ToolBarMenu1_btnEscClick;
            this.toolBarMenu1.getXmlConfig();
            Gs.DevApp.ToolBox.UtilityHelper.SetGridViewParameterMx(gvMx1);
            Gs.DevApp.ToolBox.UtilityHelper.SetGridViewParameter(gridView1, picCheckBox, this, "chkStatus", "", (value) =>
@@ -53,10 +53,10 @@
            }, lbGuid);
            getPageList(1);
            pageBar1.PagerEvent += PageBar1_PagerEvent;
            
        }
       
        private void GridView1_ColumnFilterChanged(object sender, EventArgs e)
@@ -101,7 +101,7 @@
            frm.UpdateParent += Frm_UpdateParent;
            frm.ShowDialog();
        }
        /// <summary>
        /// 查询回调
        /// </summary>
@@ -112,7 +112,7 @@
            _filterList = e.FilterList;
            getPageList(1);
        }
        /// <summary>
        /// 刷新事件
        /// </summary>
@@ -205,7 +205,7 @@
                // 创建需要特殊处理的表格视图列表
                List<DevExpress.XtraGrid.Views.Grid.GridView> gvList = new List<DevExpress.XtraGrid.Views.Grid.GridView>();
                gvList.Add(gvMx1); // 添加明细表视图1
                // 启用编辑模式下的所有控件
                // 参数说明:
                // - this.layoutMx1.Controls:要启用的控件容器
@@ -216,7 +216,7 @@
            
            }
        }
        /// <summary>
        /// 保存 -按钮点击事件 处理异常单明细的保存逻辑
        /// </summary>
@@ -245,22 +245,26 @@
            // 4. 收集明细数据
            for (var i = 0; i < gvMx1.DataRowCount; i++)
            {
                var row = gvMx1.GetDataRow(i);
                if (row != null)
                {
                    // 获取当前明细行的GUID
                    Guid? _guid = UtilityHelper.ToGuid(row["guid"].ToString());
                    // 添加明细项到请求对象
                    // 说明:
                    // ① 旧逻辑使用 row["process"].ToString(),后台只要把列名改成 processName/gs003 或存在大小写差异,就会抛出 “Column 'process' does not belong to table” 异常;
                    // ② GetRowString 会在 DataTable 中按不区分大小写的方式逐个匹配候选列名,并把 DBNull/null 统一转换为空字符串,兼容不同接口与历史数据;
                    // ③ 字段列表提供多个候选值(process/processName/processDesc/processWay/gs003),确保后端字段命名调整时仍能成功保存。
                    _obj.list.Add(new
                    {
                        Guid = _guid,
                        BatchQty = (row["batchQty"].ToString()),       // 批次数量
                        GfRkqty = (row["gfRkqty"].ToString()),         // 合格入库数量
                        LfRkqtyz = (row["lfRkqty"].ToString()),        // 不合格入库数量
                        HandResult = (row["handResult"].ToString()),        // 选别类型
                        ChooseType = (row["chooseType"].ToString()),        // 处理意见
                        Process = (row["process"].ToString()),        // 退料方式
                        BatchQty = GetRowString(row, "batchQty"),       // 批次数量
                        //GfRkqty = GetRowString(row, "gfRkqty"),         // 合格入库数
                        //LfRkqtyz = GetRowString(row, "lfRkqty"),        // 临放入库数
                        HandResult = GetRowString(row, "handResult"),        // 选别意见
                        ChooseType = GetRowString(row, "chooseType"),        // 异常类别
                        Process = GetRowString(row, "process"),        // 退料方式
                    });
                }
@@ -291,7 +295,7 @@
                    getModel(lbGuid.Text.Trim());
                    // 刷新列表页数据
                    getPageList(this.pageBar1.CurrentPage); 
                    // 跳转到查看结果页面
                    Gs.DevApp.ToolBox.UtilityHelper.JumpTab(xtraTabControl1, 6);
                }
@@ -301,8 +305,8 @@
                ToolBox.MsgHelper.Warning("提示:" + ex.Message);
            }
        }
        /// <summary>
        /// 
        /// </summary>
@@ -358,15 +362,15 @@
        {
            // 定义变量标记当前是否为编辑状态(默认非编辑状态)
            bool isEdit = false;
            // 若当前工具栏操作是"新增"(add),无需加载已有数据,直接返回
            // 原因:新增状态下应显示空白表单,而非加载历史数据
            if (toolBarMenu1.currentAction == "add") return;
            // 若当前工具栏操作是"编辑"(edit),将编辑状态标记为true
            // 后续界面控件的可编辑性将基于此变量判断
            if (toolBarMenu1.currentAction == "edit") isEdit = true;
            // 校验参数:若未传入有效的GUID(用户未选择任何行),提示用户并终止执行
            if (string.IsNullOrEmpty(strGuid))
            {
@@ -374,14 +378,14 @@
                MsgHelper.Warning("请先选择你要操作的行!");
                return;
            }
            // 构建请求参数对象:以匿名类形式封装要查询的实体主键(GUID)
            // 该参数将用于向服务端请求对应的实体数据
            var _obj = new
            {
                guid = strGuid,// 主键字段,服务端通过此值定位具体记录
            };
            try
            {
                // 调用HTTP工具类发送POST请求,获取服务端返回的JSON数据
@@ -390,25 +394,25 @@
                //   第二个参数:接口路径(结合_webServiceName前缀,完整路径为"MesQcExceptionalManager/GetModel")
                //   第三个参数:将查询参数序列化为JSON字符串
                string strJson = UtilityHelper.HttpPost("", _webServiceName + "GetModel", JsonConvert.SerializeObject(_obj));
                // 将服务端返回的JSON字符串转换为通用返回模型(ReturnModel)
                // ReturnModel包含状态码(rtnCode)、消息(rtnMsg)、数据(rtnData)三个核心字段
                ReturnModel<dynamic> _rtn = ToolBox.UtilityHelper.ReturnToDynamic(strJson);
                // 检查服务端返回状态:rtnCode > 0 表示请求成功(业务层面的成功)
                if (_rtn.rtnCode > 0)
                {
                    // 提取返回数据中的实体详情(dynamic类型可动态访问字段,无需预先定义实体类)
                    dynamic dy = _rtn.rtnData;
                    // 在界面的lbGuid标签上显示当前实体的GUID(用于标识当前操作的记录)
                    lbGuid.Text = strGuid;
                    // 创建网格视图列表,包含需要处理的明细网格视图gvMx1
                    // 后续将通过工具方法统一设置这些网格的属性(如可编辑性)
                    List<DevExpress.XtraGrid.Views.Grid.GridView> gvList = new List<DevExpress.XtraGrid.Views.Grid.GridView>();
                    gvList.Add(gvMx1);
                    // 调用工具方法批量设置界面控件的值和可编辑状态
                    // 参数说明:
                    //   this.layoutMx1.Controls:布局容器中的所有控件(文本框、下拉框等)
@@ -416,8 +420,8 @@
                    //   isEdit:是否为编辑状态(控制控件是否可编辑)
                    //   gvList:需要同步设置的网格视图列表
                    UtilityHelper.SetValueByObj(this.layoutMx1.Controls, dy, isEdit, gvList);
                    // 解析服务端返回的JSON,提取明细数据列表(存储在rtnData下的list字段中)
                    // 使用JObject解析JSON,便于提取嵌套结构的数据
                    JObject _job = JObject.Parse(strJson);
@@ -427,10 +431,11 @@
                    {
                        array.Add(a);
                    }
                    // 将明细数据的JSON数组转换为DataTable(适合作为网格控件的数据源)
                    DataTable dt = JsonConvert.DeserializeObject<DataTable>(array.ToString());
                    EnsureProcessColumn(dt);
                    // 若明细数据存在(DataTable行数>0)
                    if (dt.Rows.Count > 0)
                    {
@@ -463,6 +468,124 @@
                // 显示异常信息,便于调试和用户反馈
                ToolBox.MsgHelper.Warning("提示:" + ex.Message);
            }
        }
        /// <summary>
        /// 统一封装 DataRow 字段读取逻辑:按照候选列名列表依次查找真实列,忽略大小写并规避 DBNull。
        /// </summary>
        /// <param name="row">当前循环到的 DataRow,若为 null 直接返回空字符串。</param>
        /// <param name="columnCandidates">按优先级排列的候选列名集合(例如 process/processName/gs003)。</param>
        /// <returns>匹配到的列值;若所有候选列均不存在或值为 DBNull,则返回 string.Empty。</returns>
        /// <remarks>
        /// MesQcExceptional 的保存动作依赖该方法,以兼容后端接口字段改名或大小写差异,避免再次出现 “Column 'process' does not belong to table” 异常。
        /// </remarks>
        private static string GetRowString(DataRow row, params string[] columnCandidates)
        {
            if (row == null || row.Table == null || columnCandidates == null)
                return string.Empty;
            foreach (var candidate in columnCandidates)
            {
                if (string.IsNullOrEmpty(candidate))
                    continue;
                var match = FindMatchingColumn(row.Table, candidate);
                if (!string.IsNullOrEmpty(match))
                    return NormalizeValue(row[match]);
            }
            return string.Empty;
        }
        private static string FindMatchingColumn(DataTable table, string columnName)
        {
            if (table == null || string.IsNullOrEmpty(columnName))
                return null;
            foreach (DataColumn column in table.Columns)
            {
                if (string.Equals(column.ColumnName, columnName, StringComparison.OrdinalIgnoreCase))
                    return column.ColumnName;
            }
            return null;
        }
        private static string FindFirstAvailableColumn(DataTable table, params string[] candidates)
        {
            if (table == null || candidates == null)
                return null;
            foreach (var candidate in candidates)
            {
                var match = FindMatchingColumn(table, candidate);
                if (!string.IsNullOrEmpty(match))
                    return match;
            }
            return null;
        }
        private static bool HasColumnExact(DataTable table, string columnName)
        {
            if (table == null || string.IsNullOrEmpty(columnName))
                return false;
            foreach (DataColumn column in table.Columns)
            {
                if (string.Equals(column.ColumnName, columnName, StringComparison.Ordinal))
                    return true;
            }
            return false;
        }
        private static void CopyColumn(DataTable table, string sourceColumnName, string targetColumnName)
        {
            if (table == null || string.IsNullOrEmpty(sourceColumnName) || string.IsNullOrEmpty(targetColumnName))
                return;
            if (!HasColumnExact(table, targetColumnName))
                table.Columns.Add(targetColumnName, typeof(string));
            foreach (DataRow row in table.Rows)
            {
                row[targetColumnName] = NormalizeValue(row[sourceColumnName]);
            }
        }
        /// <summary>
        /// 在绑定 gvMx1 明细前确保 DataTable 中存在名为 process 的列:
        /// 1. 优先复用不区分大小写匹配到的同名列,保证 GridColumn23 的绑定不受影响;
        /// 2. 若无同名列,则尝试使用常见别名(processName/processDesc/processWay/gs003)复制生成;
        /// 3. 若仍无法定位有效列,则创建空的 process 列,避免前端访问时抛出列不存在异常。
        /// </summary>
        /// <param name="table">接口返回的明细 DataTable。</param>
        private static void EnsureProcessColumn(DataTable table)
        {
            if (table == null)
                return;
            var existing = FindMatchingColumn(table, "process");
            if (!string.IsNullOrEmpty(existing))
            {
                if (!HasColumnExact(table, "process"))
                    CopyColumn(table, existing, "process");
                return;
            }
            var fallback = FindFirstAvailableColumn(table, "processName", "processDesc", "processWay", "gs003");
            if (string.IsNullOrEmpty(fallback))
            {
                if (!HasColumnExact(table, "process"))
                    table.Columns.Add("process", typeof(string));
                return;
            }
            CopyColumn(table, fallback, "process");
        }
        private static string NormalizeValue(object value)
        {
            return value == null || value == DBNull.Value ? string.Empty : value.ToString();
        }
        /// <summary>
        /// 工具条事件
@@ -523,7 +646,7 @@
                var strJson = UtilityHelper.HttpPost("",  _webServiceName + "EditModelSubmit", JsonConvert.SerializeObject(_obj));
                var _rtn = UtilityHelper.ReturnToDynamic(strJson);
                MsgHelper.Warning(_rtn.rtnData.outMsg.ToString());
                if (_rtn.rtnCode > 0 && _rtn.rtnData.outSum * 1 > 0)
                {
                    if (xtraTabControl1.SelectedTabPageIndex == 1)
@@ -540,8 +663,8 @@
                MsgHelper.Warning("提示:" + ex.Message);
            }
        }
         
    }
}