SVN-Revision: r901
This commit is contained in:
tdx
2025-08-04 08:51:15 +00:00
parent 3a082804ec
commit 1c0737e1ae
2 changed files with 103 additions and 42 deletions
+94 -42
View File
@@ -7123,6 +7123,7 @@ namespace Lskj.Control
// 创建正常列的Band
GridBand normalBand = new GridBand() { Caption = "基本信息" };
normalBand.Fixed = FixedStyle.Left;
bandedGridControlEx.BandedView.Bands.Add(normalBand);
// 添加正常列到Band中
@@ -7156,11 +7157,15 @@ namespace Lskj.Control
bandedGridControlEx.BandedView.Columns.Add(col);
}
}
// 动态获取需要聚合的列
// 取得动态列名(自动处理 DateTime、string 及其它类型,不会抛转换异常)
var dynamicHeaders = originalDataSource.AsEnumerable()
.Select(r => r.Field<string>(GroupFieldprimary.FieldName))
.Distinct().Where(x => !string.IsNullOrEmpty(x)).ToList();
.Select(r => r[GroupFieldprimary.FieldName]) // 先拿 object
.Where(v => v != null && v != DBNull.Value) // 过滤空值
.Select(v => v is DateTime dt // ↙︎ DateTime 转指定格式
? dt.ToString("yyyy-MM-dd")
: v.ToString()) // 其它类型直接 ToString()
.Distinct()
.ToList();
// 为每个动态表头生成GridBand,并添加子列
foreach (string headerName in dynamicHeaders)
@@ -7217,36 +7222,64 @@ namespace Lskj.Control
/// <summary>
/// 把行转列、动态列拼接,并对指定列做求和
/// </summary>
/// <summary>
/// 把行转列、动态列拼接,并对指定列做求和(兼容 DateTime 作为 Header 列)
/// </summary>
private DataTable BuildDynamicDataSource(
DataTable original, // 原始数据
GridColumnModel keyCol, // 分组主键
GridColumnModel headerCol, // 动态列的“Header”列
GridColumnModel headerCol, // 动态列的“Header”列(可能是 DateTime
List<GridColumnModel> subCols, // Header 后要展开的子列
List<GridColumnModel> normalCols, // 普通列(取首行值)
List<GridColumnModel> sumColumns, // 需要求和的列
List<string> headers) // Header 值集合
List<string> headers) // Header 值集合(建议已格式化为 yyyy-MM-dd 等字符串)
{
// 统一把 Header 的原始值转成用于匹配/建列的键(string)
string formatKey(object v)
{
if (v == null || v == DBNull.Value) return null;
if (v is DateTime) return ((DateTime)v).ToString("yyyy-MM-dd"); // 如需其它格式,修改此处
return v.ToString();
}
string keyOf(object v) => formatKey(v) ?? string.Empty; // 避免 null 键
var dt = new DataTable();
/* ============ 1. 建列 ============ */
// 主键列
dt.Columns.Add(keyCol.FieldName, original.Columns[keyCol.FieldName].DataType);
// 普通列
var exist = new HashSet<string>(original.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName));
foreach (var col in normalCols.Where(c => exist.Contains(c.FieldName)
&& c.FieldName != keyCol.FieldName))
dt.Columns.Add(col.FieldName, original.Columns[col.FieldName].DataType);
// 辅助:原表已有列名
var exist = new HashSet<string>(original.Columns.Cast<DataColumn>().Select(c => c.ColumnName));
// 求和列
foreach (var col in sumColumns.Where(c => exist.Contains(c.FieldName)))
// 普通列:不重复、不覆盖主键
foreach (var col in normalCols)
{
if (col.FieldName == keyCol.FieldName) continue;
if (!exist.Contains(col.FieldName)) continue;
if (!dt.Columns.Contains(col.FieldName))
dt.Columns.Add(col.FieldName, original.Columns[col.FieldName].DataType);
}
// 动态列:统一 string
foreach (string h in headers)
// 求和列:类型沿用原列
foreach (var col in sumColumns)
{
if (!exist.Contains(col.FieldName)) continue;
if (!dt.Columns.Contains(col.FieldName))
dt.Columns.Add(col.FieldName, original.Columns[col.FieldName].DataType);
}
// 动态列:以 headers × subCols 展开;类型沿用子列类型
foreach (var h in headers)
{
foreach (var s in subCols)
dt.Columns.Add($"{h}_{s.FieldName}", original.Columns[s.FieldName].DataType);
{
if (!exist.Contains(s.FieldName)) continue;
string dyn = $"{h}_{s.FieldName}";
if (!dt.Columns.Contains(dyn))
dt.Columns.Add(dyn, original.Columns[s.FieldName].DataType);
}
}
/* ============ 2. 分组聚合 ============ */
foreach (var grp in original.AsEnumerable().GroupBy(r => r[keyCol.FieldName]))
@@ -7254,48 +7287,67 @@ namespace Lskj.Control
var newRow = dt.NewRow();
var first = grp.First();
// 主键值
newRow[keyCol.FieldName] = first[keyCol.FieldName];
// 普通列:取首行
foreach (var col in normalCols.Where(c => exist.Contains(c.FieldName)))
newRow[col.FieldName] = first[col.FieldName];
// ─── 求和列:只用第 1 个 Header 小组的行来累加 ───
foreach (var col in sumColumns.Where(c => exist.Contains(c.FieldName)))
foreach (var col in normalCols)
{
// ① 取当前分组里第一个 Header 值
var firstHeader = grp
.Select(r => r[headerCol.FieldName])
.FirstOrDefault();
// ② 过滤到只剩这个 Header 的行后求和
decimal sum = grp
.Where(r => Equals(r[headerCol.FieldName], firstHeader)
&& r[col.FieldName] != DBNull.Value)
.Sum(r => Convert.ToDecimal(r[col.FieldName]));
// ③ 写入结果行
newRow[col.FieldName] = sum;
if (!exist.Contains(col.FieldName)) continue;
if (!dt.Columns.Contains(col.FieldName)) continue;
newRow[col.FieldName] = first[col.FieldName];
}
// 求和列:以“当前分组中的第一个 Header 键”为准,仅汇总这一组
string firstHeader = grp.Select(r => keyOf(r[headerCol.FieldName])).FirstOrDefault();
// 动态列
var lookup = grp.ToLookup(r => r[headerCol.FieldName]?.ToString());
foreach (string h in headers)
foreach (var col in sumColumns)
{
if (!exist.Contains(col.FieldName)) continue;
if (!dt.Columns.Contains(col.FieldName)) continue;
decimal sum = 0m;
foreach (var r in grp)
{
if (keyOf(r[headerCol.FieldName]) != firstHeader) continue;
var v = r[col.FieldName];
if (v == null || v == DBNull.Value) continue;
sum += Convert.ToDecimal(v);
}
// 把 decimal 结果转换成目标列的数据类型再写入
var targetType = dt.Columns[col.FieldName].DataType;
object sumValue;
if (targetType == typeof(decimal)) sumValue = sum;
else if (targetType == typeof(int)) sumValue = Convert.ToInt32(sum);
else if (targetType == typeof(long)) sumValue = Convert.ToInt64(sum);
else if (targetType == typeof(double)) sumValue = Convert.ToDouble(sum);
else if (targetType == typeof(float)) sumValue = Convert.ToSingle(sum);
else sumValue = sum; // 兜底:直接写 decimal
newRow[col.FieldName] = sumValue;
}
// 动态列:用统一的键做命中
var lookup = grp.ToLookup(r => keyOf(r[headerCol.FieldName]));
foreach (var h in headers)
{
foreach (var s in subCols)
{
if (!exist.Contains(s.FieldName)) continue;
string dyn = $"{h}_{s.FieldName}";
var hit = lookup[h].FirstOrDefault();
newRow[dyn] = hit?[s.FieldName]?.ToString();
if (!dt.Columns.Contains(dyn)) continue;
var hit = lookup[keyOf(h)].FirstOrDefault(); // headers 已是 string
newRow[dyn] = hit == null ? DBNull.Value : hit[s.FieldName];
}
}
dt.Rows.Add(newRow);
}
return dt;
}
#endregion
#region
@@ -377,6 +377,7 @@ namespace Lskj.Control
}
else
{
if(this.ModuleGridObj.GridControlObj.CustomGroupBandEx!=null) this.ModuleGridObj.GridControlObj.CustomGroupBandEx.GridView.FocusedRowObjectChanged += new DevExpress.XtraGrid.Views.Base.FocusedRowObjectChangedEventHandler(OnGridViewFocusedRowObjectChanged);
this.ModuleGridObj.GridControlObj.GridView.FocusedRowObjectChanged += new DevExpress.XtraGrid.Views.Base.FocusedRowObjectChangedEventHandler(OnGridViewFocusedRowObjectChanged);
//this.ModuleGridObj.GridControlObj.GridView.RowCellClick += new RowCellClickEventHandler(GridView_RowCellClick);
this.ModuleGridObj.GridControlObj.GridView.MouseDown += new MouseEventHandler(GridView_MouseDown);
@@ -443,6 +444,10 @@ namespace Lskj.Control
else
{
DataRow rowItem = this.ModuleGridObj.GridControlObj.GetViewFocusedDataRow();
if (this.ModuleGridObj.GridControlObj.CustomGroupBandEx != null)
{
rowItem = this.ModuleGridObj.GridControlObj.CustomGroupBandEx.GetViewFocusedDataRow();
}
if (RefreshDetail)
{
XtraTabPage RefreshPage = this.TabObj.SelectedTabPage;
@@ -648,6 +653,10 @@ namespace Lskj.Control
}
DataRow rowItem = this.ModuleGridObj.GridControlObj.GetViewFocusedDataRow();
if (this.ModuleGridObj.GridControlObj.CustomGroupBandEx != null)
{
rowItem = this.ModuleGridObj.GridControlObj.CustomGroupBandEx.GetViewFocusedDataRow();
}
if (!string.IsNullOrEmpty(SysModel.PageVisbleCond))
{
string condSql = ReplaceHelper.ReplaceRowParam(rowItem, ReplaceHelper.ReplaceUserInfo(SysModel.PageVisbleCond));