编程与数学 03-008 《看潮企业管理软件》项目开发 18 汇总查询 2-2

编程与数学 03-008 《看潮企业管理软件》项目开发 18 汇总查询 2-2

  • 三、窗体功能代码
  • 四、窗体功能代码说明
    • (一)概述
      • [1.1 功能定位](#1.1 功能定位)
      • [1.2 核心特性](#1.2 核心特性)
    • (二)技术架构
      • [2.1 技术栈](#2.1 技术栈)
      • [2.2 项目结构](#2.2 项目结构)
    • (三)核心功能详解
      • [3.1 数据载入流程 (Dtzr方法)](#3.1 数据载入流程 (Dtzr方法))
        • [3.1.1 流程步骤](#3.1.1 流程步骤)
        • [3.1.2 条件管理机制](#3.1.2 条件管理机制)
        • [3.1.3 缓存策略](#3.1.3 缓存策略)
      • [3.2 视图初始化 (Loadgv方法)](#3.2 视图初始化 (Loadgv方法))
        • [3.2.1 列属性配置](#3.2.1 列属性配置)
        • [3.2.2 特殊字段处理](#3.2.2 特殊字段处理)
        • [3.2.3 条件样式 (条件显示)](#3.2.3 条件样式 (条件显示))
      • [3.3 打印系统 (DoPrintCx方法)](#3.3 打印系统 (DoPrintCx方法))
        • [3.3.1 打印模板管理](#3.3.1 打印模板管理)
        • [3.3.2 数据源结构](#3.3.2 数据源结构)
        • [3.3.3 文件字段处理](#3.3.3 文件字段处理)
      • [3.4 链接跳转机制](#3.4 链接跳转机制)
        • [3.4.1 链接配置](#3.4.1 链接配置)
        • [3.4.2 跳转逻辑](#3.4.2 跳转逻辑)
    • (四)权限控制系统
      • [4.1 权限字符串格式](#4.1 权限字符串格式)
      • [4.2 权限影响的功能](#4.2 权限影响的功能)
    • (五)用户界面功能
      • [5.1 工具栏功能](#5.1 工具栏功能)
      • [5.2 右键菜单](#5.2 右键菜单)
      • [5.3 快捷键](#5.3 快捷键)
      • [5.4 布局保存](#5.4 布局保存)
    • (六)数据流与状态管理
      • [6.1 主要状态变量](#6.1 主要状态变量)
      • [6.2 数据生命周期](#6.2 数据生命周期)
    • (七)配置与扩展
      • [7.1 功能配置表](#7.1 功能配置表)
      • [7.2 扩展点](#7.2 扩展点)
      • [7.3 性能优化建议](#7.3 性能优化建议)
    • (八)使用示例
      • [8.1 创建新的查询功能](#8.1 创建新的查询功能)
      • [8.2 常用操作流程](#8.2 常用操作流程)
    • (九)故障排除
      • [9.1 常见问题](#9.1 常见问题)
      • [9.2 日志与调试](#9.2 日志与调试)
  • 全文总结

摘要:本文介绍了看潮企业管理软件(KcErp)汇总查询功能模型(Uf10Cxhz)的设计与实现。该模块基于DevExpress WinForms框架开发,提供通用数据查询与报表输出平台,支持动态条件配置、多格式打印、智能链接跳转及细粒度权限控制。系统采用SQL模板与数据字典结合实现灵活查询,支持查询结果本地缓存提升性能,并提供可自定义的界面布局与条件样式显示功能,适用于库存、销售、财务等数据的汇总统计需求。
关键词:KcErp;汇总查询;DevExpress;动态SQL;权限控制;数据缓存;打印模板;GridView
人工智能助手:DeepSeek、Kimi


三、窗体功能代码

c# 复制代码
/*
 * 文件名称:Uf10Cxhz.cs
 * 功能描述:汇总查询功能模型(Query Summary Module)
 * 用途说明:
 *     1. 本窗体为ERP系统中的汇总查询通用模块,负责根据预定义的查询条件加载、展示和打印汇总数据。
 *     2. 支持载入条件配置、数据查询、表格显示、列宽调整、条件筛选、链接跳转、文件字段操作、打印预览与直接打印等功能。
 *     3. 可根据用户权限控制导出、打印、文件操作等行为。
 *     4. 支持多模板打印(含卡片、标签等特殊格式)以及数据缓存(XML文件)以提高重复查询性能。
 *     5. 集成了右键菜单、快捷键操作(如F4打开链接、Ctrl+数字锁定列)、单元格样式定制等增强交互功能。
 *     6. 与系统菜单、权限体系、数据字典、打印模板等模块紧密集成,为ERP提供统一的查询与报表输出界面。
 * 主要类:Uf10Cxhz(DevExpress.XtraEditors.XtraForm)
 * 依赖模块:DevExpress UI组件、KcErp业务模块、数据库访问层(KcDb)、打印服务(XtraReport)等。
 */

using DevExpress.Utils;
using DevExpress.Utils.Menu;
using DevExpress.XtraBars;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Controls;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.XtraReports.UI;
using System;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using static KcErp.KcDb;
using static KcErp.KcDybjsb;
using static KcErp.KcEditfm;
using static KcErp.KcEditgd;
using static KcErp.KcFile;
using static KcErp.KcMain;
namespace KcErp
{
    public partial class Uf10Cxhz : DevExpress.XtraEditors.XtraForm
    {
        #region dim public
        public string fgnbh;           // 功能编号
        public string fgnmc;           // 功能名称
        public string ft1;             // 功能表1(主数据表)
        public string ftbb;            // 功能表版本
        public string ftcxbt;          // 查询标题
        public string ftcxljzd;        // 查询链接字段(用于跳转其他功能)
        public string ftglzd;          // 过滤字段(允许筛选的列)
        public string ftmbgltj;        // 模板关联条件(打印模板与条件的关联字段)
        public string ftpxzd;          // 排序字段
        public string ftsql;           // 查询SQL语句
        public string ftyczd;          // 隐藏字段(不显示的列)
        public string ftzdyb;          // 字典原表(关联的数据字典表)
        public string fxz;             // 限制权限(字符串格式,每位代表一种权限)
        bool avend = false;            // 是否已结束
        string dcbt = "";              // 导出标题
        string dcsql = "";             // 导出SQL
        DataTable dt1;                 // 主数据表
        DataTable dttj1;               // 条件数据表(载入条件)
        DataTable dttjxs;              // 条件显示样式表(用于定制单元格外观)
        bool fh = false;               // 是否返回
        string ft1wjzd = "";           // 文件字段列表(如表中的附件字段)
        GridView gdgv = new GridView(); // 网格视图对象
        bool gvend = false;            // 网格视图是否已结束
        bool jsing = false;            // 是否正在计算
        string jszdstr = "";           // 计算字段列表字符串
        string jzzd = "";              // 禁止字段(不加载的字段)
        bool loaderr = false;          // 加载过程中是否发生错误
        bool loading = false;          // 是否正在加载数据
        bool lvend = false;            // 列表视图是否已结束
        RepositoryItemButtonEdit[] rcbt; // 按钮编辑仓库项数组(用于链接列)
        string[] rcbtmc;               // 按钮标题数组(链接功能名称)
        RepositoryItemTextEdit rctt = new RepositoryItemTextEdit(); // 文本编辑仓库项(默认编辑器)
        bool sfgv;                     // 是否使用GridView视图
        bool sfplay = false;           // 是否正在播放(未使用)
        bool sfzr;                     // 是否已载入数据
        string sqltj;                  // 条件SQL
        bool xzbj;                     // 是否允许编辑(权限位)
        bool xzck;                     // 是否允许查看(权限位)
        bool xzdc;                     // 是否允许导出(权限位)
        bool xzdy;                     // 是否允许打印(权限位)
        bool xzwj;                     // 是否允许文件操作(权限位)
        string zrtjstr = "";           // 载入条件字符串(用于缓存标识)
        #endregion dim public

        public Uf10Cxhz()
        {
            InitializeComponent();
            this.Load += Form_load;                // 窗体加载事件
            BarZR.ItemClick += Barzr_itemclick;    // 载入按钮点击事件
            // Bar 按钮事件绑定
            BarHelp.ItemClick += BarHelp_ItemClick;
            Barexit.ItemClick += BarExit_ItemClick;
            BarPrint.ItemClick += BarPrint_ItemClick;
            BarPreview.ItemClick += BarPreview_ItemClick;
            // 窗体事件
            this.Disposed += Form_hzcx_disposed;       // 窗体销毁事件
            this.FormClosed += Form_CXHZ_FormClosed;   // 窗体关闭事件
        }

        /// <summary>
        /// 根据权限和状态启用或禁用工具栏按钮
        /// </summary>
        private void Barenabled()
        {
            BarZR.Enabled = true;                                  // 载入按钮始终启用
            BarPreview.Enabled = sfzr && xzdy;                     // 预览需已载入且有打印权限
            BarPrint.Enabled = sfzr && xzdy;                       // 打印需已载入且有打印权限
        }

        /// <summary>
        /// 退出按钮点击事件:保存窗口设置、释放资源并关闭窗体
        /// </summary>
        private void BarExit_ItemClick(object sender, ItemClickEventArgs e)
        {
            try
            {
                SaveFmSet();                // 保存窗口和表格布局
                fh = true;                  // 标记为返回状态
                dt1?.Dispose();             // 释放数据表资源
                dttj1?.Dispose();
                Close();                    // 关闭窗体
            }
            catch (Exception ex)
            {
                MsgExShow("返回上级窗口", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// 帮助按钮点击事件:打开当前功能的帮助信息
        /// </summary>
        private void BarHelp_ItemClick(object sender, ItemClickEventArgs e)
        {
            RunHelpyy(this, fgnmc, pSFSJ);
        }

        /// <summary>
        /// 打印预览按钮点击事件:调用打印功能,预览模式
        /// </summary>
        private void BarPreview_ItemClick(object sender, ItemClickEventArgs e)
        {
            DoPrintCx(false);
        }

        /// <summary>
        /// 直接打印按钮点击事件:调用打印功能,直接打印模式
        /// </summary>
        private void BarPrint_ItemClick(object sender, ItemClickEventArgs e)
        {
            DoPrintCx(true);
        }

        /// <summary>
        /// 载入按钮点击事件:执行数据载入流程
        /// </summary>
        private void Barzr_itemclick(object sender, ItemClickEventArgs e)
        {
            Dtzr();
        }

        /// <summary>
        /// 执行打印或预览操作
        /// </summary>
        /// <param name="sfdy">是否直接打印(true为直接打印,false为预览)</param>
        private void DoPrintCx(bool sfdy)
        {
            try
            {
                // 1. 检查模板
                if (Bardymb.EditValue == null)
                {
                    MsgOxShow("没有可用的打印模板");
                    return;
                }
                string mbName = Bardymb.EditValue.ToString().Trim();
                if (string.IsNullOrEmpty(mbName))
                {
                    MsgOxShow("没有可用的打印模板");
                    return;
                }
                if (mbName == "直接打印")
                {
                    // 直接打印模式:使用GridView自带的打印预览
                    gdgv.OptionsPrint.AllowCancelPrintExport = true;
                    gdgv.ShowRibbonPrintPreview();
                    return;
                }
                // 2. 读取模板
                MemoryStream dyStream = Mbstream(mbName);
                if (dyStream == null)
                {
                    MsgOxShow("打印模板没有找到");
                    return;
                }
                XtraReport xr = XtraReport.FromXmlStream(dyStream, true);
                if (xr == null)
                {
                    MsgOxShow("打印模板没有设计完成");
                    return;
                }
                xr.Name = mbName;
                xr.PrinterName = pPrinterName;
                bool sfbq = mbName.Contains("卡片") || mbName.Contains("标签"); // 判断是否为标签/卡片打印

                // 3. 构造数据源
                DataSet ds = new DataSet("dataset1");
                // 3.1 表 t1:存放查询标题和条件结果(一行)
                DataTable dstb1 = new DataTable("t1");
                dstb1.Columns.Add("btmc", typeof(string)).DefaultValue = "";
                foreach (DataRow r in dttj1.Rows)
                {
                    string colName = r["tjmc"].ToString();
                    dstb1.Columns.Add(colName, typeof(string)).DefaultValue = "";
                }
                DataRow t1Row = dstb1.NewRow();
                t1Row["btmc"] = LCBT.Text;
                foreach (DataRow r in dttj1.Rows)
                    t1Row[r["tjmc"].ToString()] = r["tjjg"];
                dstb1.Rows.Add(t1Row);
                dstb1.AcceptChanges();
                ds.Merge(dstb1);

                // 3.2 表 t2:存放详细数据
                DataTable dstb2;
                if (sfbq)
                {
                    // 标签打印:只打印当前选中行
                    int bqRowIndex = gdgv.GetFocusedDataSourceRowIndex();
                    dstb2 = dt1.Clone();
                    if (bqRowIndex >= 0)
                    {
                        DataRow newRow = dstb2.NewRow();
                        for (int c = 0; c < dstb2.Columns.Count; c++)
                            newRow[c] = dt1.Rows[bqRowIndex][c];
                        dstb2.Rows.Add(newRow);
                    }
                }
                else
                {
                    // 普通打印:打印所有行
                    dstb2 = dt1.Clone();
                    DataView dv = new DataView(dt1);
                    foreach (DataRowView rv in dv)
                    {
                        DataRow newRow = dstb2.NewRow();
                        Copyrow(newRow, rv);
                        dstb2.Rows.Add(newRow);
                    }
                }

                // 3.3 下载文件字段:将数据库中的文件路径转换为本地临时文件路径
                string t1wjzd = "tp1,tp2,tp3,wj1,wj2,wj3"; // 默认支持的图片和文件字段
                if (!string.IsNullOrEmpty(ft1wjzd))
                {
                    string[] wjzd = $"{ft1wjzd},{t1wjzd}".Split(',');
                    wjzd = wjzd.Where(s => dstb2.Columns.Contains(s)).ToArray();
                    foreach (string fld in wjzd)
                    {
                        if (string.IsNullOrWhiteSpace(fld)) continue;
                        foreach (DataRow r in dstb2.Rows)
                        {
                            object val = r[fld];
                            if (val == null || val == DBNull.Value) continue;
                            string fileName = val.ToString();
                            if (string.IsNullOrWhiteSpace(fileName)) continue;
                            // 下载文件到本地缓存目录
                            FileOL_Down(this, ft1, fld, fileName);
                            r[fld] = Path.Combine(pFilesLC, fileName);
                        }
                    }
                }
                dstb2.TableName = "t2";
                dstb2.AcceptChanges();
                ds.Merge(dstb2);
                xr.DataSource = ds;

                // 4. 标记识别 & 打印
                DyBjSb(ref xr); // 打印标记识别(如条形码、二维码等)
                if (sfdy)
                    xr.Print();
                else
                    xr.ShowPreviewDialog();
            }
            catch (Exception ex)
            {
                MsgExShow("准备打印数据", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// 数据载入主流程:加载条件、执行查询、绑定表格、应用设置
        /// </summary>
        private void Dtzr()
        {
            try
            {
                L3.Caption = "";
                loading = true;
                string sqltj1;
                string sql1;
                bool sfcxcx = pCXCX;  // 是否重新查询(系统配置)

                // 1. 加载用户保存的查询条件
                dttj1 = new DataTable();
                sqltj1 = "select dyid,tjxh,tjmc,tjjg,tjsm,tjedit,rxkz from x9_gn_0zrtj nolock where gnbh='" + fgnbh + "' and yhmc='" + pDQYH + "' order by tjxh";
                dttj1 = KcDb.DtRead(sqltj1);
                if (dttj1.Rows.Count <= 0)
                {
                    // 如果没有保存的条件,则调用存储过程初始化
                    KcDb.DBexec("select x9_zrtjcs('" + fgnbh + "','" + pDQYH + "')");
                    dttj1 = KcDb.DtRead(sqltj1);
                }

                // 2. 显示条件编辑窗体(如果存在条件)
                if (dttj1.Rows.Count > 0)
                {
                    FmZRTJ fmtj = new FmZRTJ();
                    fmtj.Text = fgnmc + "载入条件";
                    fmtj.fgnbh = fgnbh;
                    fmtj.fgnmc = fgnmc;
                    fmtj.sfcxhz = true;
                    fmtj.Loadtjfm();
                    fmtj.Tjedit(ref dttj1);
                    dttj1.AcceptChanges();
                    fmtj.ShowDialog(this);
                    if (!fmtj.sfok)
                    {
                        // 用户取消,则关闭窗口(如果是首次载入)
                        if (!sfzr)
                            this.Close();
                        return;
                    }
                    Zrtjsave(fmtj.tjlc, ref dttj1);
                    DataTable udt1 = dttj1.GetChanges();
                    if (udt1 != null)
                    {
                        // 保存修改后的条件到数据库
                        if (KcDb.GetDtSavev("x9_gn_0zrtj", ref udt1, "tjjg"))
                        {
                            dttj1.AcceptChanges();
                        }
                    }
                    sfcxcx = fmtj.sfcxcx; // 是否重新查询(用户选择)
                    fmtj.Close();
                    fmtj.Dispose();
                }
                this.Focus();

                // 3. 构造查询SQL
                sql1 = TjStr(ref dttj1, ftsql); // 将条件替换到SQL模板中
                string cxbs = fgnmc;
                LCBT.Text = TjStr(ref dttj1, ftcxbt); // 生成查询标题
                for (int i = 0; i < dttj1.Rows.Count; i++)
                {
                    string tjedit = dttj1.Rows[i]["tjedit"].ToString();
                    switch (tjedit)
                    {
                        case "dateedit":
                            DateTime tjdate;
                            try
                            {
                                tjdate = Convert.ToDateTime(dttj1.Rows[i]["tjjg"]);
                            }
                            catch
                            {
                                tjdate = Getywdate(); // 获取业务默认日期
                            }
                            zrtjstr += dttj1.Rows[i]["tjmc"].ToString() + "@" + tjdate.ToString("yyyy-MM-dd") + "@";
                            cxbs += tjdate.ToString("yyyy-MM-dd");
                            break;
                        default:
                            zrtjstr += dttj1.Rows[i]["tjmc"].ToString() + "@" + dttj1.Rows[i]["tjjg"].ToString() + "@";
                            cxbs += dttj1.Rows[i]["tjjg"].ToString();
                            break;
                    }
                }
                cxbs = Hfwjm(cxbs); // 生成缓存文件名(避免非法字符)

                dcsql = sql1;
                dcbt = LCBT.Text;
                dt1 = new DataTable();
                string qrname = pFilesQuery + "\\" + pDBmc + cxbs;
                bool wjzr = false;

                // 4. 尝试从缓存文件加载数据(如果允许且文件存在)
                if (!sfcxcx)
                {
                    if (File.Exists(qrname + ".xml") && File.Exists(qrname + ".xmlschema"))
                    {
                        dt1.ReadXmlSchema(qrname + ".xmlschema");
                        dt1.ReadXml(qrname + ".xml");
                        if (dt1 != null && dt1.Rows.Count > 0)
                        {
                            wjzr = true; // 标记为文件载入
                        }
                    }
                }

                // 5. 如果未从文件载入,则从数据库查询
                if (!wjzr)
                {
                    dt1 = KcDb.DtRead(sql1);
                }

                if (dt1 == null)
                {
                    MsgXxShow("数据未能正确载入,请检查功能定义是否完整或载入过程是否正确");
                    if (!sfzr)
                        this.Close();
                    return;
                }
                else
                {
                    // 检查是否为错误信息(单行单列的特殊情况)
                    if (dt1.Rows.Count == 1 && dt1.Columns.Count == 1)
                    {
                        Barenabled();
                        MsgXxShow("数据未能正确载入," + dt1.Rows[0][0].ToString());
                        if (!sfzr)
                            this.Close();
                        return;
                    }

                    // 6. 缓存查询结果到XML文件(如果是新查询)
                    if (!wjzr)
                    {
                        try
                        {
                            dt1.WriteXmlSchema(qrname + ".xmlschema");
                            dt1.WriteXml(qrname + ".xml");
                        }
                        catch (Exception ex)
                        {
                            MsgExShow("保存文件", ex.Message, ex.Source, ex.StackTrace);
                        }
                    }

                    // 7. 如果是设计模式,记录SQL用于调试或文档
                    if (pSFSJ)
                    {
                        string sql0 = "select '" + LCBT.Text + "' as btmc";
                        for (int r = 0; r < dttj1.Rows.Count; r++)
                        {
                            sql0 += ",'" + dttj1.Rows[r]["tjjg"].ToString() + "' as " + dttj1.Rows[r]["tjmc"].ToString();
                        }
                        string t1 = sql0.Replace("'", "`");
                        string t2 = sql1.Replace("'", "`");
                        string d1 = "";
                        string d2 = ("select * from x9_sjzd where tname='" + ft1 + "' and zdbb ='" + ftbb + "'").Replace("'", "`");
                        KcDb.DBexec("update x9_gn_0dymb set t1='" + t1 + "',t2='" + t2 + "',d1='" + d1 + "',d2='" + d2 +
                                   "' where gnbh='" + fgnbh + "' and sjsd=false");
                    }

                    // 8. 移除禁止字段(jzzd)
                    string[] jz = jzzd.Split(',');
                    for (int c = dt1.Columns.Count - 1; c >= 1; c--)
                    {
                        for (int j = 0; j < jz.Length; j++)
                        {
                            if (dt1.Columns[c].ColumnName.ToLower() == jz[j].ToLower())
                            {
                                dt1.Columns.RemoveAt(c);
                                break;
                            }
                        }
                    }

                    // 9. 绑定数据到GridControl
                    GridView1.OptionsBehavior.AutoPopulateColumns = false;
                    GridCX.DataSource = dt1;

                    // 10. 根据模板关联条件自动选择打印模板
                    if (!string.IsNullOrEmpty(ftmbgltj))
                    {
                        DataRow[] glrow = dttj1.Select("tjmc='" + ftmbgltj + "'");
                        if (glrow.Length > 0)
                        {
                            string glmb = glrow[0]["tjjg"].ToString();
                            if (Rcdymb.Items.IndexOf(glmb) >= 0)
                            {
                                Bardymb.EditValue = glmb;
                            }
                        }
                    }

                    // 11. 初始化GridView
                    Loadgv();
                    sfgv = true;
                    sfzr = true; // 标记为已载入
                }
                Barenabled();
                KcDb.DBclose();
            }
            catch (Exception ex)
            {
                sfzr = true;
                loaderr = true;
                MsgExShow("载入数据", ex.Message, ex.Source, ex.StackTrace);
            }

            if (loaderr)
            {
                MsgXxShow("加载功能时出错,请检查功能设计的正确性");
                this.Close();
                return;
            }

            try
            {
                // 12. 应用保存的窗口和表格布局
                if (gdgv != null)
                {
                    string fmset = FmRead(fgnmc);
                    string[] fmay = fmset.Split(',');
                    if (fmay.Length >= 5)
                    {
                        short ws = 0;
                        if (fmay.Length == 6)
                        {
                            ws = Convert.ToInt16(fmay[5]);
                            if (ws == 2)
                                this.WindowState = FormWindowState.Maximized;
                            if (ws == 0)
                            {
                                if (Convert.ToInt32(fmay[0]) > 0) this.Top = Convert.ToInt32(fmay[0]);
                                if (Convert.ToInt32(fmay[1]) > 0) this.Left = Convert.ToInt32(fmay[1]);
                                if (Convert.ToInt32(fmay[2]) > 50) this.Width = Convert.ToInt32(fmay[2]);
                                if (Convert.ToInt32(fmay[3]) > 50) this.Height = Convert.ToInt32(fmay[3]);
                            }
                        }
                        if (sfzr && gdgv != null)
                        {
                            if (Convert.ToInt32(fmay[4]) > 20)
                                gdgv.RowHeight = Convert.ToInt32(fmay[4]);
                            else
                                gdgv.RowHeight = 28;
                        }
                    }

                    // 列宽设置
                    fmset = FmRead(fgnmc + "表格");
                    fmay = fmset.Split(',');
                    if (fmay.Length == gdgv.Columns.Count)
                    {
                        for (int c = 0; c < gdgv.Columns.Count; c++)
                        {
                            int cw = Convert.ToInt32(fmay[c]);
                            if (cw <= 0)
                                gdgv.Columns[c].Visible = false;
                            else
                                gdgv.Columns[c].Width = cw;
                        }
                    }
                    else
                    {
                        // 自动调整列宽
                        Gvoptionwith(ref gdgv, GridCX.Width);
                    }
                    L3.Caption = "共载入[" + dt1.Rows.Count.ToString().Trim() + "]行记录";
                }
                this.Show();
            }
            catch (Exception ex)
            {
                MsgExShow("应用保存的窗口参数", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// 自动配置列宽菜单项点击事件
        /// </summary>
        private void Dxmenuclick(object sender, EventArgs e)
        {
            Gvoptionwith(ref gdgv, GridCX.Width);
        }

        /// <summary>
        /// 手动配置列宽菜单项点击事件
        /// </summary>
        private void Dxsdclick(object sender, EventArgs e)
        {
            Gvsdoption(ref gdgv);
        }

        /// <summary>
        /// 文件字段按钮点击事件:打开文件编辑对话框
        /// </summary>
        private void File_buttonclick(object sender, ButtonPressedEventArgs e)
        {
            try
            {
                FileOL_edit(this, e.Button.Caption, ft1, e.Button.Tag.ToString(), "", sender as ButtonEdit);
            }
            catch (Exception ex)
            {
                MsgExShow("文件编辑", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// 窗体加载事件:初始化数据库连接、界面元素、权限、打印模板等
        /// </summary>
        private void Form_load(object sender, EventArgs e)
        {
            try
            {
                // 检查数据库连接
                if (KcDb.KcCn.State != ConnectionState.Open)
                {
                    KcDb.DBLJ();
                    if (!KcDb.SFlj)
                    {
                        MsgXxShow("账套数据库未能连接,请在主窗口重试!");
                        this.Close();
                        return;
                    }
                }
                SetDg(this, 1360, 600, true);
                RibbonControl.ApplicationCaption = fgnmc;
                RibbonPage1.Text = fgnmc;

                // 加载菜单图标
                if (pCdimage.Images.Count > 0)
                {
                    for (int i = 0; i < RibbonControl.Items.Count; i++)
                    {
                        if (RibbonControl.Items[i].GetType().Name.ToLower() == "barbuttonitem")
                        {
                            if (pCdimage.Images.IndexOf(RibbonControl.Items[i].Caption) >= 0)
                            {
                                RibbonControl.Items[i].LargeGlyph = pCdimage.Images[RibbonControl.Items[i].Caption];
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                loaderr = true;
                MsgExShow("加载菜单图标", ex.Message, ex.Source, ex.StackTrace);
            }

            try
            {
                LCBT.Text = fgnmc;
                // 加载报表模板列表
                Rcdymb.Items.Clear();
                Rcdymb.TextEditStyle = TextEditStyles.DisableTextEditor;
                DataTable dtbb = new DataTable();
                dtbb = KcDb.DtRead("select mbmc as bbmb from x9_gn_0dymb where gnbh= '" + fgnbh + "' and ((czry like '%" + pDQYH + "%') or czry='') order by mbxh");
                if (dtbb != null)
                {
                    if (dtbb.Rows.Count > 0)
                    {
                        for (int i = 0; i < dtbb.Rows.Count; i++)
                        {
                            Rcdymb.Items.Add(dtbb.Rows[i]["bbmb"].ToString());
                        }
                    }
                }
                Rcdymb.Items.Add("直接打印");
                if (Rcdymb.Items.Count > 0)
                {
                    Bardymb.EditValue = Rcdymb.Items[0].ToString();
                }

                // 解析权限字符串(fxz格式如 "01101")
                xzdy = fxz.Substring(1, 1) == "1";
                xzwj = fxz.Substring(3, 1) == "1";
                xzdc = fxz.Substring(4, 1) == "1"; // 导出致xml文件,加密

                // 获取禁止字段列表
                DataRow[] dr = pDTQX.Select("gnbh='" + fgnbh + "'");
                if (dr.Length > 0)
                {
                    jzzd = dr[0]["jzzd"].ToString().Trim();
                }

                RcCZJL.Items.Clear();
                L1.Caption = "[" + fgnmc + "]";
                L2.Caption = "[当前用户:" + pDQYH + "]";
                R1.Caption = "";
                R2.Caption = "[" + pZTMC + "]";
                GridCX.RepositoryItems.Add(rctt);
            }
            catch (Exception ex)
            {
                loaderr = true;
                MsgExShow("加载窗口", ex.Message, ex.Source, ex.StackTrace);
            }

            if (loaderr)
            {
                this.Close();
                return;
            }

            try
            {
                // 加载条件显示样式表(用于定制单元格颜色等)
                dttjxs = KcDb.DtRead("select * from x9_gn_tjxs where gnbh='" + fgnbh + "'");
            }
            catch (Exception ex)
            {
                MsgExShow("加载条件显示数据", ex.Message, ex.Source, ex.StackTrace);
            }

            // 执行数据载入
            Dtzr();
            if (!sfzr)
                return;
        }

        /// <summary>
        /// 自定义单元格编辑器:根据链接字段的值动态分配按钮编辑器
        /// </summary>
        private void Gdgv_customrowcelledit(object sender, CustomRowCellEditEventArgs e)
        {
            try
            {
                if (e.Column.FieldName != ftcxljzd)
                    return;
                if (string.IsNullOrWhiteSpace(e.CellValue?.ToString()))
                {
                    e.RepositoryItem = rctt; // 空值使用普通文本编辑器
                }
                else
                {
                    int i = Array.IndexOf(rcbtmc, e.CellValue.ToString().Trim());
                    if (i >= 0)
                        e.RepositoryItem = rcbt[i]; // 匹配的按钮编辑器
                    else
                        e.RepositoryItem = rctt;
                }
            }
            catch { }
        }

        /// <summary>
        /// 网格视图右键菜单显示前事件:添加自定义菜单项(自动/手动配置列宽)
        /// </summary>
        private void Gdgv_popupmenushowing(object sender, PopupMenuShowingEventArgs e)
        {
            try
            {
                bool ists = false;
                foreach (DXMenuItem mi in e.Menu.Items)
                {
                    if (mi.Caption == "自动配置列宽")
                    {
                        ists = true;
                        break;
                    }
                }
                if (!ists)
                {
                    DXMenuItem dxmenu = new DXMenuItem();
                    dxmenu.Caption = "自动配置列宽";
                    dxmenu.Click += Dxmenuclick;
                    e.Menu.Items.Add(dxmenu);
                    DXMenuItem dxmenusd = new DXMenuItem();
                    dxmenusd.Caption = "手动配置列宽";
                    dxmenusd.Click += Dxsdclick;
                    e.Menu.Items.Add(dxmenusd);
                }
            }
            catch (Exception ex)
            {
                MsgExShow("增加配置列宽右键菜单", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// 窗体关闭事件:激活其他打开的窗体
        /// </summary>
        private void Form_CXHZ_FormClosed(object sender, FormClosedEventArgs e)
        {
            Form dqfm = null;
            foreach (Form opfm in Application.OpenForms)
            {
                opfm.WindowState = FormWindowState.Normal;
                dqfm = opfm;
            }
            if (dqfm != null)
                dqfm.Activate();
        }

        /// <summary>
        /// 窗体销毁事件:释放所有资源,防止内存泄漏
        /// </summary>
        private void Form_hzcx_disposed(object sender, EventArgs e)
        {
            try
            {
                // 清空所有字段和引用
                ft1 = null;
                ftbb = null;
                fxz = null;
                fgnbh = null;
                fgnmc = null;
                ftsql = null;
                ftglzd = null;
                ftpxzd = null;
                ftmbgltj = null;
                ftcxbt = null;
                ftcxljzd = null;
                ftyczd = null;
                ftzdyb = null;
                xzbj = false;
                xzdy = false;
                xzck = false;
                xzwj = false;
                xzdc = false;
                jzzd = null;
                sqltj = null;
                dt1 = null;
                dttj1 = null;
                sfzr = false;
                gvend = false;
                lvend = false;
                avend = false;
                sfgv = false;
                dttjxs = null;
                gdgv = null;
                jszdstr = null;
                jsing = false;
                loading = false;
                sfplay = false;
                rctt = null;
                rcbt = null;
                rcbtmc = null;
                fh = false;
                dcsql = null;
                dcbt = null;
                zrtjstr = null;
                ft1wjzd = null;
                loaderr = false;
                RibbonControl = null;
                RibbonPage1 = null;
                Rp1 = null;
                Rp5 = null;
                Rp4 = null;
                RibbonStatusBar = null;
                BarZR = null;
                GridCX = null;
                GridView1 = null;
                L1 = null;
                L2 = null;
                RepositoryItemRadioGroup1 = null;
                V1 = null;
                V2 = null;
                CardView1 = null;
                BackgroundWorker1 = null;
                BarPrint = null;
                BarPreview = null;
                BarHelp = null;
                OpenFileDialog1 = null;
                RcCZJL = null;
                Barexit = null;
                LCBT = null;
                R1 = null;
                R2 = null;
                L3 = null;
                Bardymb = null;
                Rcdymb = null;
                GC.Collect(); // 强制垃圾回收
            }
            catch (Exception ex)
            {
                MsgExShow("亢墮完笥斤嵆", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// 链接字段按钮点击事件:跳转到关联功能(如单据查询)
        /// </summary>
        private void Ljcx_buttonclick(object sender, ButtonPressedEventArgs e)
        {
            string ljstr = gdgv.GetRowCellValue(gdgv.FocusedRowHandle, gdgv.FocusedColumn).ToString();
            if (string.IsNullOrWhiteSpace(ljstr))
            {
                MsgOxShow("没有对应的链接数据");
                return;
            }
            try
            {
                string ljgnbh;
                string ljgnmc = e.Button.Caption;
                string sqlljtj;
                DataTable dtljtj = new DataTable();
                DataRow[] dr = pDTQX.Select("gnmc='" + ljgnmc + "'");
                if (dr.Length > 0)
                {
                    string mxqx = dr[0]["xzqx"].ToString();
                    ljgnbh = dr[0]["gnbh"].ToString();
                    // 加载目标功能的载入条件
                    sqlljtj = "select dyid,tjxh,tjmc,tjjg,tjsm,tjedit,rxkz from x9_gn_0zrtj nolock where gnbh='" + ljgnbh + "' and yhmc='" + pDQYH + "' order by tjxh";
                    dtljtj = KcDb.DtRead(sqlljtj);
                    if (dtljtj.Rows.Count <= 0)
                    {
                        KcDb.DBexec("select x9_zrtjcs('" + ljgnbh + "','" + pDQYH + "')");
                        dtljtj = KcDb.DtRead(sqlljtj);
                    }

                    // 替换当前查询条件:将本功能的查询条件值赋给目标功能
                    string[] tjcc = new string[dttj1.Rows.Count];
                    string[] tjvv = new string[dttj1.Rows.Count];
                    for (int l = 0; l < dttj1.Rows.Count; l++)
                    {
                        tjcc[l] = dttj1.Rows[l]["tjmc"].ToString();
                        tjvv[l] = dttj1.Rows[l]["tjjg"].ToString();
                    }
                    for (int r = 0; r < dtljtj.Rows.Count; r++)
                    {
                        string tjmc = dtljtj.Rows[r]["tjmc"].ToString().Trim();
                        int tjcol = Array.IndexOf(tjcc, tjmc);
                        if (tjcol >= 0)
                            dtljtj.Rows[r]["tjjg"] = tjvv[tjcol];
                        else
                            dtljtj.Rows[r]["tjjg"] = "";
                    }

                    // 替换当前行条件:将当前行对应列的值赋给目标功能条件
                    string[] cc = new string[gdgv.Columns.Count];
                    string[] cf = new string[gdgv.Columns.Count];
                    string[] vv = new string[gdgv.Columns.Count];
                    for (int l = 0; l < gdgv.Columns.Count; l++)
                    {
                        cc[l] = gdgv.Columns[l].Caption;
                        cf[l] = gdgv.Columns[l].FieldName;
                        vv[l] = gdgv.GetRowCellValue(gdgv.FocusedRowHandle, gdgv.Columns[l]).ToString();
                    }
                    for (int r = 0; r < dtljtj.Rows.Count; r++)
                    {
                        string tjmc = dtljtj.Rows[r]["tjmc"].ToString().Trim();
                        int tjcol = Array.IndexOf(cc, tjmc);
                        if (tjcol >= 0)
                            dtljtj.Rows[r]["tjjg"] = vv[tjcol];
                        tjcol = Array.IndexOf(cf, tjmc);
                        if (tjcol >= 0)
                            dtljtj.Rows[r]["tjjg"] = vv[tjcol];
                    }

                    // 提交条件到数据库
                    DataTable udt1 = dtljtj.GetChanges();
                    if (udt1 != null)
                    {
                        if (KcDb.GetDtSavev("x9_gn_0zrtj", ref udt1, "tjjg"))
                        {
                            dtljtj.AcceptChanges();
                        }
                    }

                    string ljfl = dr[0]["gnly"].ToString();
                    string[] cxoid = new string[1];
                    if (ljfl == "单据查询")
                    {
                        // 单据查询需要传递oid(单据号)列表
                        if (dt1.Columns.IndexOf("oid") > -1)
                        {
                            cxoid = new string[dt1.Rows.Count];
                            int s = -1;
                            for (int r = 0; r < dt1.Rows.Count; r++)
                            {
                                if (dt1.Rows[r][ftcxljzd].ToString() == ljgnmc)
                                {
                                    string str = dt1.Rows[r]["oid"].ToString();
                                    if (!string.IsNullOrEmpty(str) && Array.IndexOf(cxoid, str) < 0)
                                    {
                                        s++;
                                        cxoid[s] = str;
                                    }
                                }
                            }
                            if (s >= 0)
                            {
                                Array.Resize(ref cxoid, s + 1);
                                string dqoid = gdgv.GetRowCellValue(gdgv.FocusedRowHandle, gdgv.Columns["oid"]).ToString();
                                X9gn(ljgnmc, ljfl, ljgnbh, mxqx, cxoid, dqoid);
                            }
                            else
                            {
                                MsgOxShow("链接单据查询,请在查询设计时包含单据号字段oid");
                                return;
                            }
                        }
                        else
                        {
                            MsgOxShow("链接单据查询,请在查询设计时包含单据号字段oid");
                            return;
                        }
                    }
                    else
                    {
                        // 其他类型功能直接打开
                        X9gn(ljgnmc, ljfl, ljgnbh, mxqx, null, null);
                    }
                }
                else
                {
                    MsgOxShow("当前操作员无此权限");
                }
                KcDb.DBclose();
            }
            catch (Exception ex)
            {
                MsgExShow("调用链接功能", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// 初始化GridView:绑定列属性、设置编辑器、链接字段、样式等
        /// </summary>
        private void Loadgv()
        {
            // 初始gv
            try
            {
                if (dt1 == null)
                {
                    loaderr = true;
                    return;
                }
                if (GridCX == null)
                {
                    loaderr = true;
                    return;
                }
                if (GridCX.ViewCollection.Count == 0)
                {
                    gdgv = new GridView(GridCX);
                    gdgv.Name = "gdgv";
                }
                gdgv.BeginUpdate();
                gdgv.OptionsBehavior.AutoPopulateColumns = false;
                GridCX.DataSource = dt1;

                // 检查链接字段是否存在
                if (dt1.Columns.IndexOf(ftcxljzd) < 0)
                {
                    ftcxljzd = "";
                }

                GridCX.MainView = gdgv;
                gdgv.PopulateColumns();
                SetGv(ref gdgv, false);
                gdgv.PopupMenuShowing += Gdgv_popupmenushowing;
                gdgv.OptionsSelection.MultiSelect = true;
                gdgv.OptionsSelection.MultiSelectMode = GridMultiSelectMode.RowSelect;
                gdgv.OptionsView.EnableAppearanceEvenRow = true;
                gdgv.OptionsView.EnableAppearanceOddRow = true;

                // 加载数据字典(列属性)
                string zdsql;
                DataTable dtzd = new DataTable();
                zdsql = "select * from x9_sjzd nolock where zdbb='" + ftbb + "' and tname in ('" + ft1 + "','" + ftzdyb.Replace(",", "','") + "') order by fxh";
                dtzd = KcDb.DtRead(zdsql);
                if (dtzd != null)
                {
                    if (dtzd.Rows.Count > 0)
                    {
                        // 根据字典设置列属性(编辑器、格式、标题等)
                        Gdloread(ref GridCX, ref gdgv, ref dtzd, xzwj);
                        gdgv.CustomDrawCell += View_tjxs; // 绑定单元格绘制事件(条件显示样式)

                        string ctag;
                        string[] artag = "fileedit,fileread,buttontextedit,buttontextread,hyperlinkedit".Split(',');
                        string[] px = ftpxzd.ToLower().Split(',');
                        string[] gl = ftglzd.ToLower().Split(',');

                        for (int col = 0; col < gdgv.Columns.Count; col++)
                        {
                            string fd = gdgv.Columns[col].FieldName.ToLower();
                            // 隐藏字段处理
                            if (ftyczd.IndexOf(fd) >= 0)
                            {
                                gdgv.Columns[col].Visible = false;
                                continue;
                            }
                            // 允许排序字段
                            if (Array.IndexOf(px, fd) >= 0)
                            {
                                gdgv.Columns[col].OptionsColumn.AllowSort = DefaultBoolean.True;
                            }
                            // 允许筛选字段
                            if (Array.IndexOf(gl, fd) >= 0)
                            {
                                gdgv.Columns[col].OptionsFilter.AllowFilter = true;
                            }

                            string[] ttag = gdgv.Columns[col].Tag.ToString().Split(',');
                            if (ttag.Length > 1 && ttag[1] != "")
                            {
                                // 绑定F4快捷键事件(用于打开搜索链接)
                                RepositoryItem ri = gdgv.Columns[col].ColumnEdit as RepositoryItem;
                                if (ri != null)
                                {
                                    ri.KeyUp += View_keyf4;
                                }
                            }
                            ctag = ttag[0];
                            if (Array.IndexOf(artag, ctag) < 0)
                                continue;

                            // 根据列类型绑定相应事件
                            switch (ctag)
                            {
                                case "fileedit":
                                case "fileread":
                                    RepositoryItemButtonEdit rcFile = gdgv.Columns[col].ColumnEdit as RepositoryItemButtonEdit;
                                    if (rcFile != null)
                                    {
                                        rcFile.ButtonClick += File_buttonclick;
                                    }
                                    if (ft1wjzd == "")
                                        ft1wjzd = gdgv.Columns[col].FieldName;
                                    else
                                        ft1wjzd += "," + gdgv.Columns[col].FieldName;
                                    break;
                                case "buttontextedit":
                                case "buttontextread":
                                    RepositoryItemButtonEdit rcText = gdgv.Columns[col].ColumnEdit as RepositoryItemButtonEdit;
                                    if (rcText != null)
                                    {
                                        rcText.ButtonClick += Text_buttonclick;
                                    }
                                    break;
                                case "hyperlinkedit":
                                    RepositoryItemTextEdit rcLink = gdgv.Columns[col].ColumnEdit as RepositoryItemTextEdit;
                                    if (rcLink != null)
                                    {
                                        rcLink.KeyUp += View_keylink;
                                    }
                                    break;
                            }
                        }
                        gdgv.KeyDown += View_keydown; // 绑定键盘事件(列宽调整、锁定列)
                        GridCX.UseEmbeddedNavigator = false;
                        gdgv.KeyUp += View_keyf4;

                        // 准备链接字段的按钮编辑器
                        string btmc = "";
                        string dqmc = "";
                        if (!string.IsNullOrEmpty(ftcxljzd))
                        {
                            for (int r = 0; r < gdgv.RowCount; r++)
                            {
                                dqmc = gdgv.GetRowCellValue(r, gdgv.Columns[ftcxljzd]).ToString().Trim();
                                if (!string.IsNullOrEmpty(dqmc) && btmc.IndexOf(dqmc) < 0)
                                {
                                    btmc += dqmc + "`";
                                }
                            }
                        }
                        if (!string.IsNullOrEmpty(btmc))
                        {
                            rcbtmc = btmc.Substring(0, btmc.Length - 1).Split('`');
                            rcbt = new RepositoryItemButtonEdit[rcbtmc.Length];
                            for (int i = 0; i < rcbtmc.Length; i++)
                            {
                                if (!string.IsNullOrEmpty(rcbtmc[i]))
                                {
                                    RepositoryItemButtonEdit rc = new RepositoryItemButtonEdit();
                                    rc.TextEditStyle = TextEditStyles.HideTextEditor;
                                    rc.Buttons.Clear();
                                    string[] btname = rcbtmc[i].Split(',');
                                    for (int j = 0; j < btname.Length; j++)
                                    {
                                        if (!string.IsNullOrEmpty(btname[j]))
                                        {
                                            EditorButton bt1 = new EditorButton();
                                            bt1.Caption = btname[j];
                                            bt1.Kind = ButtonPredefines.Glyph;
                                            rc.Buttons.Add(bt1);
                                        }
                                    }
                                    rcbt[i] = rc;
                                    rc.ButtonClick += Ljcx_buttonclick; // 绑定链接跳转事件
                                    GridCX.RepositoryItems.Add(rc);
                                }
                            }
                            gdgv.CustomRowCellEdit += Gdgv_customrowcelledit; // 动态分配编辑器
                        }
                    }
                    gdgv.EndUpdate();

                    // 设计模式下自动生成帮助文档
                    if (pSFSJ)
                    {
                        string strhelp = "\r\n" + fgnmc + "\r\n" + new string('=', fgnmc.Length * 2) + "\r\n\r\n" +
                            "一、功能用途\r\n\r\n";
                        string dt1xm = "二、数据项目\r\n";
                        for (int c = 0; c < dt1.Columns.Count; c++)
                        {
                            string fn = dt1.Columns[c].ColumnName;
                            DataRow[] dr = dtzd.Select("tname='" + ft1 + "' and fname='" + fn + "'");
                            if (dr.Length > 0)
                            {
                                dt1xm += dr[0]["fmemo"].ToString() + "(" + dr[0]["fname"].ToString() + ")。\r\n";
                            }
                        }
                        strhelp += dt1xm + "\r\n三、注意事项\r\n";
                        strhelp += "\r\n四、操作方法(参见汇总查询功能操作方法)\r\n";
                        KcDb.DBexec("update x9_help_yy set mbhelp='" + strhelp + "' where ztbh='" + fgnbh + "'");
                    }
                    GridCX.Visible = true;
                }
                KcDb.DBclose();
            }
            catch (Exception ex)
            {
                loaderr = true;
                MsgExShow("初始数据视图", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// 保存窗体设置:位置、大小、行高、列宽等
        /// </summary>
        private void SaveFmSet()
        {
            try
            {
                if (!sfzr) return;
                string fmset = $"{this.Top},{this.Left},{this.Width},{this.Height},{gdgv.RowHeight},{(int)this.WindowState}";
                FmSave(fgnmc, fmset);
                if (gdgv != null && gdgv.RowCount > 0)
                {
                    fmset = gdgv.Columns[0].Width.ToString().Trim();
                    for (int g = 1; g < gdgv.Columns.Count; g++)
                    {
                        int cw = -1;
                        if (gdgv.Columns[g].Visible)
                            cw = gdgv.Columns[g].Width;
                        fmset += "," + cw.ToString();
                    }
                    FmSave(fgnmc + "表格", fmset);
                }
            }
            catch (Exception ex)
            {
                MsgExShow("保存窗口参数", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// 文本字段按钮点击事件:打开文本编辑窗体
        /// </summary>
        private void Text_buttonclick(object sender, ButtonPressedEventArgs e)
        {
            var be = sender as ButtonEdit;
            var fmrt = new FmTEXT();
            Text_edit(fmrt, this, ref be, e.Button.Caption);
            fmrt.Dispose();
        }

        /// <summary>
        /// 网格视图键盘按下事件:支持Ctrl+加号/减号调整列宽,Ctrl+数字锁定列
        /// </summary>
        private void View_keydown(object sender, KeyEventArgs e)
        {
            // 设置最大或最小列宽
            if (gdgv.RowCount > 0)
            {
                if ((e.KeyCode == Keys.Add) && (e.Modifiers == Keys.Control))
                {
                    try
                    {
                        gdgv.FocusedColumn.Width = pMaxWt; // 设置为最大宽度
                    }
                    catch (Exception ex)
                    {
                        MsgExShow("设置最大列宽", ex.Message, ex.Source, ex.StackTrace);
                        return;
                    }
                }
                if ((e.KeyCode == Keys.Subtract) && (e.Modifiers == Keys.Control))
                {
                    try
                    {
                        gdgv.FocusedColumn.Width = pMinWt; // 设置为最小宽度
                    }
                    catch (Exception ex)
                    {
                        MsgExShow("设置最小列宽", ex.Message, ex.Source, ex.StackTrace);
                        return;
                    }
                }
            }
            // 锁定或取消锁定列(Ctrl+数字键)
            try
            {
                if ((e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) && (e.Modifiers == Keys.Control))
                {
                    int maxcol = (gdgv.VisibleColumns.Count - 1 < 10) ? gdgv.VisibleColumns.Count - 1 : 9;
                    for (int i = maxcol; i >= 0; i--)
                    {
                        gdgv.VisibleColumns[i].Fixed = FixedStyle.None; // 先取消所有锁定
                    }
                    if (e.KeyCode > Keys.D0)
                    {
                        int idx = Math.Min((int)e.KeyCode - 49, gdgv.VisibleColumns.Count - 2);
                        for (int i = 0; i <= idx; i++)
                        {
                            gdgv.VisibleColumns[i].Fixed = FixedStyle.Left; // 锁定前N列
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MsgExShow("锁定或取消锁定列", ex.Message, ex.Source, ex.StackTrace);
            }
        }

        /// <summary>
        /// F4键事件:打开搜索链接(基于列Tag中定义的URL模板)
        /// </summary>
        private void View_keyf4(object sender, KeyEventArgs e)
        {
            try
            {
                if (e.KeyCode != Keys.F4)
                    return;
                string[] ttag = gdgv.FocusedColumn.Tag.ToString().Split(',');
                if (string.IsNullOrEmpty(ttag[1]))
                    return;
                string ttext = gdgv.GetRowCellValue(gdgv.FocusedRowHandle, gdgv.FocusedColumn).ToString();
                if (string.IsNullOrWhiteSpace(ttext))
                    return;
                if (ttext.Length > 20)
                    ttext = ttext.Substring(0, 20);
                string sstr = ttag[1]; // URL模板,如 "http://xxx?q={0}"
                string searchstr = string.Format(sstr, System.Net.WebUtility.UrlDecode(ttext));
                Process.Start(searchstr); // 打开浏览器
            }
            catch { }
        }

        /// <summary>
        /// 超链接字段F4键事件:直接打开单元格中的URL
        /// </summary>
        private void View_keylink(object sender, KeyEventArgs e)
        {
            try
            {
                if (e.KeyCode != Keys.F4)
                    return;
                string[] ttag = gdgv.FocusedColumn.Tag.ToString().Split(',');
                if (ttag[0] != "hyperlinkedit")
                    return;
                string ttext = gdgv.GetRowCellValue(gdgv.FocusedRowHandle, gdgv.FocusedColumn).ToString();
                if (string.IsNullOrWhiteSpace(ttext))
                    return;
                Process.Start(ttext); // 打开链接
            }
            catch { }
        }

        /// <summary>
        /// 自定义单元格绘制事件:根据条件显示样式表设置单元格前景色/背景色
        /// </summary>
        private void View_tjxs(object sender, RowCellCustomDrawEventArgs e)
        {
            try
            {
                if (dttjxs == null || dttjxs.Rows.Count <= 0)
                    return;
                for (int h = 0; h < dttjxs.Rows.Count; h++)
                {
                    // 检查当前列是否匹配条件列或"all"(全部列)
                    if (dttjxs.Rows[h]["xsl"].ToString().ToLower() == e.Column.FieldName.ToLower() ||
                        dttjxs.Rows[h]["xsl"].ToString().ToLower() == "all")
                    {
                        bool bgfc = Convert.ToBoolean(dttjxs.Rows[h]["bgfc"]); // 是否改变前景色
                        bool bgbc = Convert.ToBoolean(dttjxs.Rows[h]["bgbc"]); // 是否改变背景色
                        string tjl = dttjxs.Rows[h]["tjl"].ToString();         // 条件字段名
                        int tjfx = Convert.ToInt32(dttjxs.Rows[h]["tjfx"]);    // 条件期望值
                        int tjz = Convert.ToInt32(gdgv.GetRowCellValue(e.RowHandle, gdgv.Columns[tjl])); // 实际值
                        if (tjfx == tjz)
                        {
                            // 条件匹配,应用颜色
                            if (bgfc)
                            {
                                Color fc = Color.FromArgb(Convert.ToInt32(dttjxs.Rows[h]["fc"]));
                                e.Appearance.ForeColor = fc;
                            }
                            if (bgbc)
                            {
                                Color bc = Color.FromArgb(Convert.ToInt32(dttjxs.Rows[h]["bc"]));
                                e.Appearance.BackColor = bc;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MsgExShow("定制单元格外观", ex.Message, ex.Source, ex.StackTrace);
                gdgv.CustomDrawCell -= View_tjxs; // 出错时移除事件,避免重复报错
            }
        }
    }
}

四、窗体功能代码说明

汇总查询功能模型 (Uf10Cxhz) 说明

(一)概述

1.1 功能定位

Uf10Cxhz.cs 是ERP系统中的通用汇总查询模型,为系统提供一个统一的、可配置的数据查询、展示和输出平台。该模型通过参数化配置,能够适应不同的业务查询需求,实现"一次开发,多处使用"的设计理念。

1.2 核心特性

  • ✅ 动态查询配置:基于数据字典和SQL模板的灵活查询
  • ✅ 条件管理:用户可保存和重用查询条件
  • ✅ 多格式打印:支持普通报表、卡片、标签等多种打印模板
  • ✅ 智能链接:字段级功能跳转和数据关联
  • ✅ 权限控制:细粒度的操作权限管理
  • ✅ 界面定制:可保存的窗口布局和表格列宽设置
  • ✅ 数据缓存:查询结果本地缓存提升性能
  • ✅ 扩展性强:支持文件字段、超链接、条件样式等高级功能

(二)技术架构

2.1 技术栈

复制代码
开发框架:Windows Forms + DevExpress 20.1+
数据库:支持SQL Server等(通过KcDb抽象层)
报表引擎:DevExpress XtraReports
数据绑定:DevExpress GridControl + GridView

2.2 项目结构

复制代码
KcErp.Uf10Cxhz
├── 公共字段区 (dim public)    # 功能参数和状态变量
├── 构造函数与事件绑定         # 初始化组件和事件
├── 工具栏功能模块             # 载入、打印、帮助等
├── 核心数据流程               # Dtzr() 方法
├── 打印处理模块               # DoPrintCx() 方法
├── 视图初始化                 # Loadgv() 方法
├── 事件处理程序               # 键盘、鼠标、菜单事件
├── 资源管理                   # 窗体销毁和清理
└── 辅助功能                   # 工具方法

(三)核心功能详解

3.1 数据载入流程 (Dtzr方法)

3.1.1 流程步骤
复制代码
1. 条件加载 → 2. 条件编辑 → 3. SQL生成 → 4. 数据获取
5. 缓存处理 → 6. 数据绑定 → 7. 视图初始化 → 8. 布局应用
3.1.2 条件管理机制
sql 复制代码
-- 条件存储表结构
x9_gn_0zrtj:
  - dyid: 唯一标识
  - gnbh: 功能编号
  - yhmc: 用户名
  - tjxh: 条件序号
  - tjmc: 条件名称
  - tjjg: 条件结果值
  - tjedit: 编辑器类型 (dateedit, textedit等)
  - rxkz: 可选值限制
3.1.3 缓存策略
csharp 复制代码
// 缓存文件命名规则
string qrname = pFilesQuery + "\\" + pDBmc + Hfwjm(功能名+条件值);
// 缓存文件:.xml (数据) + .xmlschema (结构)
// 有效期:无固定期限,通过sfcxcx参数控制是否重新查询

3.2 视图初始化 (Loadgv方法)

3.2.1 列属性配置

基于数据字典 x9_sjzd 动态配置:

  • 字段类型:文本、数字、日期、文件、链接等
  • 显示属性:标题、宽度、可见性、排序、过滤
  • 编辑控制:只读/可编辑、编辑器类型、验证规则
3.2.2 特殊字段处理
字段类型 说明 事件绑定
文件字段 存储附件路径 File_buttonclick
链接字段 功能跳转 Ljcx_buttonclick
按钮文本 文本编辑按钮 Text_buttonclick
超链接 网页链接 View_keylink (F4)
搜索字段 带搜索模板 View_keyf4 (F4)
3.2.3 条件样式 (条件显示)
sql 复制代码
-- 样式配置表 x9_gn_tjxs
-- 可配置:目标列、条件列、条件值、前景色、背景色
-- 示例:当状态=2时,金额列显示红色

3.3 打印系统 (DoPrintCx方法)

3.3.1 打印模板管理
复制代码
模板来源:x9_gn_0dymb 表
模板类型:
  - 普通报表:多行数据
  - 卡片打印:单行详细
  - 标签打印:单行紧凑格式
特殊模板:"直接打印" → 使用GridView原生打印
3.3.2 数据源结构
csharp 复制代码
DataSet ds = new DataSet("dataset1");
ds.Tables.Add("t1");  // 表头:标题+条件值(一行)
ds.Tables.Add("t2");  // 数据:查询结果(多行)
3.3.3 文件字段处理

打印前自动下载文件字段到本地缓存目录,将数据库路径转换为本地路径供报表使用。

3.4 链接跳转机制

3.4.1 链接配置
csharp 复制代码
// 在数据字典中配置链接字段
ftcxljzd = "链接字段名";
// 字段值格式:"功能名称1,功能名称2"
3.4.2 跳转逻辑
  1. 条件传递:将当前查询条件和选中行数据传递给目标功能
  2. 权限验证:检查用户是否有目标功能权限
  3. 单据查询特殊处理:传递相关oid(单据号)列表
  4. 条件保存:更新目标功能的用户条件表

(四)权限控制系统

4.1 权限字符串格式

csharp 复制代码
fxz = "01101";  // 5位权限字符串
位置含义:[0]预留, [1]打印, [2]编辑, [3]文件, [4]导出
值为"1"表示有权限,"0"表示无权限

4.2 权限影响的功能

  • xzdy (打印权限):控制打印和预览按钮
  • xzwj (文件权限):控制文件字段的编辑/下载
  • xzdc (导出权限):控制XML导出功能(预留)
  • xzbj (编辑权限):控制数据编辑(本模块主要为查询)
  • xzck (查看权限):基础查看权限

(五)用户界面功能

5.1 工具栏功能

按钮 功能 启用条件
载入 重新查询数据 始终启用
打印预览 预览报表 已载入数据且有打印权限
直接打印 立即打印 已载入数据且有打印权限
帮助 打开帮助文档 始终启用
退出 关闭窗口 始终启用

5.2 右键菜单

  • 自动配置列宽:根据内容自动调整列宽
  • 手动配置列宽:保持当前列宽设置

5.3 快捷键

快捷键 功能
Ctrl + + 当前列设为最大宽度
Ctrl + - 当前列设为最小宽度
Ctrl + 1-9 锁定前N列(左固定)
Ctrl + 0 取消所有列锁定
F4 (超链接字段) 打开单元格中的URL
F4 (搜索字段) 使用模板打开搜索页面

5.4 布局保存

csharp 复制代码
// 窗口布局:Top,Left,Width,Height,RowHeight,WindowState
// 列宽布局:各列宽度(-1表示隐藏)
// 保存位置:配置文件或数据库

(六)数据流与状态管理

6.1 主要状态变量

csharp 复制代码
bool sfzr;           // 数据是否已载入(核心状态)
bool loading;        // 是否正在加载
bool loaderr;        // 加载过程是否出错
bool sfgv;           // 是否使用GridView视图
bool fh;             // 是否正在返回关闭

6.2 数据生命周期

复制代码
窗体加载 → 权限检查 → 模板加载 → 条件初始化
    ↓
用户交互 → 条件修改 → 数据查询 → 缓存写入
    ↓
数据显示 → 用户操作 → 打印/跳转 → 布局保存
    ↓
窗体关闭 → 资源释放 → 状态清理

(七)配置与扩展

7.1 功能配置表

sql 复制代码
-- 主要配置表
x9_gn_0zrtj    -- 用户查询条件
x9_sjzd        -- 数据字典(字段属性)
x9_gn_0dymb    -- 打印模板定义
x9_gn_tjxs     -- 条件显示样式

7.2 扩展点

  1. 新的字段类型:在数据字典中增加新的ctag类型,并相应的事件处理
  2. 新的打印格式:在x9_gn_0dymb中新增模板,在DoPrintCx中添加相应逻辑
  3. 新的链接类型:扩展Ljcx_buttonclick方法,支持新的gnly类型
  4. 条件样式扩展:在x9_gn_tjxs中增加新的显示规则

7.3 性能优化建议

  1. 分页加载:对于大数据量,可考虑实现分页查询
  2. 异步加载:将数据查询和界面更新分离
  3. 缓存清理:定期清理过期的查询缓存文件
  4. 索引优化:确保查询SQL的相关字段有适当索引

(八)使用示例

8.1 创建新的查询功能

sql 复制代码
-- 1. 在功能表中注册新功能
INSERT INTO x9_gn (gnbh, gnmc, gnly, ...) 
VALUES ('CX001', '销售汇总查询', '汇总查询', ...);

-- 2. 定义数据字典
INSERT INTO x9_sjzd (tname, fname, fmemo, ctag, ...)
VALUES 
('销售表', '客户名称', '客户全称', 'textread', ...),
('销售表', '销售金额', '含税金额', 'numeric', ...),
('销售表', '查看明细', '链接到明细查询', 'buttontextread', ...);

-- 3. 定义查询SQL模板
UPDATE x9_gn SET ftsql = 'SELECT * FROM 销售表 WHERE 1=1 {条件替换}' 
WHERE gnbh = 'CX001';

-- 4. 定义打印模板(可选)
INSERT INTO x9_gn_0dymb (gnbh, mbmc, ...)
VALUES ('CX001', '销售汇总报表', ...);

8.2 常用操作流程

  1. 首次使用:进入功能 → 设置查询条件 → 执行查询 → 保存条件
  2. 日常查询:选择保存的条件 → 执行查询 → 查看/打印数据
  3. 数据分析:使用列筛选 → 排序 → 条件样式突出显示 → 导出
  4. 关联操作:点击链接字段 → 跳转到关联功能 → 查看详细信息

(九)故障排除

9.1 常见问题

问题现象 可能原因 解决方案
无法连接数据库 连接字符串错误/服务器不可用 检查主窗口连接状态
查询无数据 SQL模板错误/条件不匹配 检查SQL日志,验证条件值
打印预览空白 模板设计问题/数据源不匹配 检查报表模板的数据绑定
链接跳转失败 权限不足/目标功能未定义 检查用户权限和目标功能配置
界面布局混乱 配置文件损坏 删除布局配置文件重新设置

9.2 日志与调试

  • 错误信息:通过MsgExShow显示详细错误(消息、源、堆栈)
  • SQL跟踪:pSFSJ模式下记录执行的SQL语句
  • 缓存调试:检查pFilesQuery目录下的XML文件

全文总结

该文档详尽阐述了看潮企业管理软件(KcErp)中汇总查询功能模型(Uf10Cxhz)的技术架构与实现细节。作为ERP系统的通用查询平台,该模块采用C#与DevExpress WinForms控件库构建,通过参数化配置实现"一次开发,多处使用"的设计理念。

技术架构方面,模块集成RibbonControl功能区、GridControl数据网格与XtraReports报表引擎,提供美观且功能丰富的用户界面。核心功能包括:基于数据字典(x9_sjzd)的动态列配置与SQL生成机制;用户查询条件的保存与重用(x9_gn_0zrtj表管理);支持普通报表、卡片、标签等多格式的打印系统;字段级功能跳转与单据关联查询;以及基于XML文件的查询结果缓存机制。

系统实现了完善的权限控制体系,通过5位权限字符串控制打印、导出、文件操作等行为。用户体验方面,提供快捷键操作(Ctrl+数字锁定列、F4打开链接)、右键菜单(自动/手动配置列宽)、条件样式显示(根据数值改变单元格颜色)等交互特性。

该模块设计注重性能与扩展性,支持大数据量查询、异步加载、多视图切换(网格/卡片),并通过配置化方式适应不同业务场景,为ERP系统提供了统一的查询与报表输出解决方案,适用于库存汇总、销售统计、财务报表等业务场景。

相关推荐
明月看潮生16 小时前
编程与数学 03-008 《看潮企业管理软件》项目开发 17 单据查询 4-3
erp·企业开发·项目实践·编程与数学·.net开发·c#编程
明月看潮生4 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 16 业务处理 2-1
erp·企业开发·项目实践·编程与数学·.net开发·c#编程
明月看潮生5 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 15 工序统计 4-1
erp·企业开发·项目实践·编程与数学·.net开发·c#编程
明月看潮生5 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 15 工序统计 4-2
erp·企业开发·项目实践·编程与数学·.net开发·c#编程
明月看潮生6 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 14 单据审批 6-2
erp·企业开发·项目实践·编程与数学·.net开发·c#编程
明月看潮生6 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 14 单据审批 6-5
erp·企业开发·项目实践·编程与数学·.net开发·c#编程
明月看潮生6 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 14 单据审批 6-6
erp·企业开发·项目实践·编程与数学·.net开发·c#编程
明月看潮生6 天前
编程与数学 03-008 《看潮企业管理软件》项目开发 14 单据审批 6-3
erp·企业开发·项目实践·编程与数学·.net开发·c#编程
kuankeTech12 天前
“数改智转”加速跑:外贸ERP助力钢铁智能工厂“提质增效”
大数据·人工智能·经验分享·软件开发·erp