diff --git a/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor b/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor index c84d7209e..cd18ce8ee 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor +++ b/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor @@ -41,6 +41,7 @@ DoubleClickToEdit="DoubleClickToEdit" OnDoubleClickCellCallback="OnDoubleClickCellCallback" OnDoubleClickRowCallback="OnDoubleClickRowCallback" + RowContentTemplate="RowContentTemplate" OnClickRowCallback="OnClickRowCallback"> diff --git a/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor.cs b/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor.cs index a10abb0c6..0e50f9c86 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor.cs +++ b/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor.cs @@ -13,6 +13,10 @@ namespace ThingsGateway.Admin.Razor; [CascadingTypeParameter(nameof(TItem))] public partial class AdminTable where TItem : class, new() { + + public List Columns => Instance?.Columns; + + /// [Parameter] public EventCallback> SelectedRowsChanged { get; set; } @@ -40,6 +44,10 @@ public partial class AdminTable where TItem : class, new() /// [Parameter] public Func? OnDoubleClickRowCallback { get; set; } + /// + [Parameter] + public RenderFragment>? RowContentTemplate { get; set; } + /// [Parameter] public Func? OnClickRowCallback { get; set; } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 60f7e961d..7eb2d180f 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,9 +1,9 @@ - 10.11.99 - 10.11.99 - 10.11.99 + 10.11.100 + 10.11.100 + 10.11.100 10.11.6 10.11.6 8.0.20 diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor new file mode 100644 index 000000000..c3f490f8e --- /dev/null +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor @@ -0,0 +1,26 @@ +@namespace ThingsGateway.Gateway.Razor +@using System.Text.Json.Nodes +@using Microsoft.Extensions.Hosting +@using ThingsGateway.Admin.Application +@using ThingsGateway.Admin.Razor +@using ThingsGateway.Gateway.Application +@inherits ComponentDefault +@typeparam TItem + + +@foreach (var col in RowContent.Columns) +{ + + + @{ + + @GetValue(col, RowContent.Row) + + } + + +} diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor.cs new file mode 100644 index 000000000..16c4fda81 --- /dev/null +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor.cs @@ -0,0 +1,226 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://thingsgateway.cn/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +using System.Collections.Concurrent; + +using ThingsGateway.NewLife.Threading; + +using TouchSocket.Core; + +namespace ThingsGateway.Gateway.Razor; + +public partial class VariableRow : IDisposable +{ + [Parameter] + public TableRowContext? RowContent { get; set; } + + public void Dispose() + { + timer?.SafeDispose(); + GC.SuppressFinalize(this); + } + TimerX? timer; + protected override void OnInitialized() + { + timer = new TimerX(Refresh, null, 1000, 1000, "VariableRow"); + base.OnInitialized(); + } + private Task Refresh(object? state) + { + return InvokeAsync(StateHasChanged); + } + + protected override void OnParametersSet() + { + FixedCellClassStringCache?.Clear(); + CellClassStringCache?.Clear(); + base.OnParametersSet(); + } + /// + /// 获得 指定单元格数据方法 + /// + /// + /// + /// + protected RenderFragment GetValue(ITableColumn col, TItem item) => builder => + { + if (col.Template != null) + { + builder.AddContent(0, col.Template(item)); + } + else if (col.ComponentType == typeof(ColorPicker)) + { + // 自动化处理 ColorPicker 组件 + builder.AddContent(10, col.RenderColor(item)); + } + else + { + builder.AddContent(20, col.RenderValue(item)); + } + }; + + internal static string? GetDoubleClickCellClassString(bool trigger) => CssBuilder.Default() +.AddClass("is-dbcell", trigger) +.Build(); + + /// + /// 获得指定列头固定列样式 + /// + /// + /// + /// + protected string? GetFixedCellStyleString(ITableColumn col, int margin = 0) + { + string? ret = null; + if (col.Fixed) + { + ret = IsTail(col) ? GetRightStyle(col, margin) : GetLeftStyle(col); + } + return ret; + } + + private string? GetLeftStyle(ITableColumn col) + { + var columns = RowContent.Columns.ToList(); + var defaultWidth = 200; + var width = 0; + var start = 0; + var index = columns.IndexOf(col); + //if (GetFixedDetailRowHeaderColumn) + //{ + // width += DetailColumnWidth; + //} + //if (GetFixedMultipleSelectColumn) + //{ + // width += MultiColumnWidth; + //} + if (GetFixedLineNoColumn) + { + width += LineNoColumnWidth; + } + while (index > start) + { + var column = columns[start++]; + width += column.Width ?? defaultWidth; + } + return $"left: {width}px;"; + } + private bool GetFixedLineNoColumn = false; + + private string? GetRightStyle(ITableColumn col, int margin) + { + var columns = RowContent.Columns.ToList(); + var defaultWidth = 200; + var width = 0; + var index = columns.IndexOf(col); + + // after + while (index + 1 < columns.Count) + { + var column = columns[index++]; + width += column.Width ?? defaultWidth; + } + //if (ShowExtendButtons && FixedExtendButtonsColumn) + { + width += ExtendButtonColumnWidth; + } + + // 如果是固定表头时增加滚动条位置 + if (IsFixedHeader && (index + 1) == columns.Count) + { + width += margin; + } + return $"right: {width}px;"; + } + private bool IsFixedHeader = true; + + public int LineNoColumnWidth { get; set; } = 60; + public int ExtendButtonColumnWidth { get; set; } = 220; + + + private bool IsTail(ITableColumn col) + { + var middle = Math.Floor(RowContent.Columns.Count() * 1.0 / 2); + var index = Columns.IndexOf(col); + return middle < index; + } + private ConcurrentDictionary CellClassStringCache { get; } = new(ReferenceEqualityComparer.Instance); + + /// + /// 获得 Cell 文字样式 + /// + /// + /// + /// + /// + protected string? GetCellClassString(ITableColumn col, bool hasChildren, bool inCell) => CellClassStringCache.GetOrAdd(col, col => CssBuilder.Default("table-cell") + .AddClass(col.GetAlign().ToDescriptionString(), col.Align == Alignment.Center || col.Align == Alignment.Right) + .AddClass("is-wrap", col.GetTextWrap()) + .AddClass("is-ellips", col.GetTextEllipsis()) + .AddClass("is-tips", col.GetShowTips()) + .AddClass("is-resizable", AllowResizing) + .AddClass("is-tree", IsTree && hasChildren) + .AddClass("is-incell", inCell) + .AddClass(col.CssClass) + .Build()); + + private bool AllowResizing = true; + private bool IsTree = false; + + private ConcurrentDictionary FixedCellClassStringCache { get; } = new(ReferenceEqualityComparer.Instance); + /// + /// 获得指定列头固定列样式 + /// + /// + /// + protected string? GetFixedCellClassString(ITableColumn col) => FixedCellClassStringCache.GetOrAdd(col, col => CssBuilder.Default() + .AddClass("fixed", col.Fixed) + .AddClass("fixed-right", col.Fixed && IsTail(col)) + .AddClass("fr", IsLastColumn(col)) + .AddClass("fl", IsFirstColumn(col)) + .Build()); + + + [Parameter] + public Func> ColumnsFunc { get; set; } + public List Columns => ColumnsFunc(); + + private ConcurrentDictionary LastFixedColumnCache { get; } = new(ReferenceEqualityComparer.Instance); + private bool IsLastColumn(ITableColumn col) => LastFixedColumnCache.GetOrAdd(col, col => + { + var ret = false; + if (col.Fixed && !IsTail(col)) + { + var index = Columns.IndexOf(col) + 1; + ret = index < Columns.Count && Columns[index].Fixed == false; + } + return ret; + }); + private ConcurrentDictionary FirstFixedColumnCache { get; } = new(ReferenceEqualityComparer.Instance); + private bool IsFirstColumn(ITableColumn col) => FirstFixedColumnCache.GetOrAdd(col, col => + { + var ret = false; + if (col.Fixed && IsTail(col)) + { + // 查找前一列是否固定 + var index = Columns.IndexOf(col) - 1; + if (index > 0) + { + ret = !Columns[index].Fixed; + } + } + return ret; + }); + + + + + +} diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRowHelpers.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRowHelpers.cs new file mode 100644 index 000000000..c23336b79 --- /dev/null +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRowHelpers.cs @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://thingsgateway.cn/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +namespace ThingsGateway.Gateway.Razor; + +internal static class VariableRowHelpers +{ + internal static Alignment GetAlign(this ITableColumn col) => col.Align ?? Alignment.None; + internal static bool GetTextWrap(this ITableColumn col) => col.TextWrap ?? false; + internal static bool GetShowTips(this ITableColumn col) => col.ShowTips ?? false; + + + internal static RenderFragment RenderColor(this ITableColumn col, TItem item) => builder => + { + var val = GetItemValue(col, item); + var v = val?.ToString() ?? "#000"; + var style = $"background-color: {v};"; + builder.OpenElement(0, "div"); + builder.AddAttribute(1, "class", "is-color"); + builder.AddAttribute(2, "style", style); + builder.CloseElement(); + }; + internal static object? GetItemValue(this ITableColumn col, TItem item) + { + var fieldName = col.GetFieldName(); + object? ret; + if (item is IDynamicObject dynamicObject) + { + ret = dynamicObject.GetValue(fieldName); + } + else + { + ret = Utility.GetPropertyValue(item, fieldName); + + if (ret != null) + { + var t = ret.GetType(); + if (t.IsEnum) + { + // 如果是枚举这里返回 枚举的描述信息 + var itemName = ret.ToString(); + if (!string.IsNullOrEmpty(itemName)) + { + ret = Utility.GetDisplayName(t, itemName); + } + } + } + } + return ret; + } + internal static bool GetTextEllipsis(this ITableColumn col) => col.TextEllipsis ?? false; + +} \ No newline at end of file diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor index e7d73593e..59778b99e 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor @@ -20,8 +20,7 @@ ShowExtendButtons=true ShowToolbar="true" ShowExportButton - IsAutoRefresh - AutoRefreshInterval="1000" + ShowDefaultButtons=true ShowSearch=false ExtendButtonColumnWidth=220 @@ -85,6 +84,11 @@ + + + + + @if ((AuthorizeButton("导出"))) diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor.cs index 15760b3d4..90b137018 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor.cs +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor.cs @@ -15,13 +15,17 @@ using ThingsGateway.Admin.Application; using ThingsGateway.Admin.Razor; using ThingsGateway.DB; using ThingsGateway.Extension.Generic; -using ThingsGateway.NewLife.Extension; using ThingsGateway.NewLife.Json.Extension; namespace ThingsGateway.Gateway.Razor; public partial class VariableRuntimeInfo : IDisposable { + public List ColumnsFunc() + { + return table?.Columns; + } + #if !Management [Parameter] public ChannelDeviceTreeItem SelectModel { get; set; } @@ -84,6 +88,12 @@ public partial class VariableRuntimeInfo : IDisposable return Task.CompletedTask; } + protected override void OnParametersSet() + { + base.OnParametersSet(); + scheduler?.Trigger(); + } + private async Task Notify(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return;