diff --git a/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor b/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor index b27b18179..40ca14e4d 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor +++ b/src/Admin/ThingsGateway.Admin.Razor/Components/AdminTable.razor @@ -4,8 +4,8 @@
- where TItem : class, new() { + /// + [Parameter] + public Func OnColumnVisibleChanged { get; set; } + /// + [Parameter] + public Func,Task> OnColumnCreating { get; set; } /// [Parameter] public TableRenderMode RenderMode { get; set; } public List Columns => Instance?.Columns; + public IEnumerable GetVisibleColumns => Instance?.GetVisibleColumns(); + public List Rows => Instance?.Rows; + /// [Parameter] @@ -158,6 +167,9 @@ public partial class AdminTable where TItem : class, new() [Parameter] public IDataService DataService { get; set; } + [Parameter] + public string? Id { get; set; } + /// [Parameter] public Func CreateItemCallback { get; set; } diff --git a/src/Admin/ThingsGateway.Admin.Razor/Pages/HardwareInfoPage.razor.cs b/src/Admin/ThingsGateway.Admin.Razor/Pages/HardwareInfoPage.razor.cs index cdc5e09e9..b49514955 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Pages/HardwareInfoPage.razor.cs +++ b/src/Admin/ThingsGateway.Admin.Razor/Pages/HardwareInfoPage.razor.cs @@ -72,7 +72,7 @@ public partial class HardwareInfoPage : IDisposable ChartDataSource.Options.Title = Localizer[nameof(HistoryHardwareInfo)]; ChartDataSource.Options.X.Title = Localizer["DateTime"]; ChartDataSource.Options.Y.Title = Localizer["Data"]; - ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz")); + ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm")); ChartDataSource.Data.Add(new ChartDataset() { Tension = 0.4f, @@ -116,7 +116,7 @@ public partial class HardwareInfoPage : IDisposable else { var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos(); - ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd HH:mm zz")); + ChartDataSource.Labels = hisHardwareInfos.Select(a => a.Date.ToString("dd-HH:mm")); ChartDataSource.Data[0].Data = hisHardwareInfos.Select(a => (object)a.CpuUsage); ChartDataSource.Data[1].Data = hisHardwareInfos.Select(a => (object)a.MemoryUsage); ChartDataSource.Data[2].Data = hisHardwareInfos.Select(a => (object)a.DriveUsage); diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Components/QuickActions.razor b/src/Gateway/ThingsGateway.Gateway.Razor/Components/QuickActions.razor index 78ead797f..b084afccb 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Components/QuickActions.razor +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Components/QuickActions.razor @@ -1,5 +1,5 @@ @inherits ThingsGatewayModuleComponentBase -@attribute [JSModuleAutoLoader("Components/QuickActions.razor.js", AutoInvokeDispose = false)] +@attribute [JSModuleAutoLoader("Components/QuickActions.razor.js", AutoInvokeDispose = false, AutoInvokeInit = true)] @namespace ThingsGateway.Gateway.Razor
@@ -8,12 +8,12 @@
@HeaderText
- +
- AutoRestartThread ) Value="AutoRestartThread" ValueChanged="OnAutoRestartThreadChanged" Items="AutoRestartThreadBoolItems" /> + AutoRestartThread) Value="AutoRestartThread" ValueChanged="OnAutoRestartThreadChanged" Items="AutoRestartThreadBoolItems" />
diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/CellValue.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/CellValue.cs new file mode 100644 index 000000000..fb7d9b81d --- /dev/null +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/CellValue.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://thingsgateway.cn/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +namespace ThingsGateway.Gateway.Razor; + +public readonly struct CellValue +{ + public readonly string Field { get; } + public readonly string Value { get; } + public CellValue(string f, string v) { Field = f; Value = v; } +} diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableModelUtils.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableModelUtils.cs new file mode 100644 index 000000000..05bb273dd --- /dev/null +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableModelUtils.cs @@ -0,0 +1,81 @@ +using ThingsGateway.NewLife.Caching; +using ThingsGateway.NewLife.Json.Extension; + +namespace ThingsGateway.Gateway.Razor; + +public static class VariableModelUtils +{ + static MemoryCache MemoryCache = new(); + public static object GetPropertyValue(VariableRuntime model, string fieldName) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + + if (MemoryCache.TryGetValue(fieldName, out Func data)) + { + return data(model); + } + else + { + var ret = MemoryCache.GetOrAdd(fieldName, (fieldName) => + { + return LambdaExtensions.GetPropertyValueLambda(model, fieldName).Compile(); + })(model); + return ret; + } + } + + public static string GetValue(VariableRuntime row, string fieldName) + { + switch (fieldName) + { + case nameof(VariableRuntime.Value): + return row.Value?.ToSystemTextJsonString() ?? string.Empty; + case nameof(VariableRuntime.RawValue): + return row.RawValue?.ToSystemTextJsonString() ?? string.Empty; + case nameof(VariableRuntime.LastSetValue): + return row.LastSetValue?.ToSystemTextJsonString() ?? string.Empty; + case nameof(VariableRuntime.ChangeTime): + return row.ChangeTime.ToString("dd-HH:mm:ss.fff"); + + case nameof(VariableRuntime.CollectTime): + return row.CollectTime.ToString("dd-HH:mm:ss.fff"); + + case nameof(VariableRuntime.IsOnline): + return row.IsOnline.ToString(); + + case nameof(VariableRuntime.LastErrorMessage): + return row.LastErrorMessage; + + + case nameof(VariableRuntime.RuntimeType): + return row.RuntimeType; + default: + + var ret = VariableModelUtils.GetPropertyValue(row, 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 is string str ? str : ret?.ToString() ?? string.Empty; + } + } + + 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 bool GetTextEllipsis(this ITableColumn col) => col.TextEllipsis ?? false; +} diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor index d62e2f9cd..6df07d544 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor @@ -9,11 +9,18 @@ @foreach (var col in RowContent.Columns) { -
} 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 index ea99cf72e..441ea983c 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor.cs +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRow.razor.cs @@ -10,40 +10,36 @@ using System.Collections.Concurrent; -using ThingsGateway.NewLife.Threading; - -using TouchSocket.Core; - namespace ThingsGateway.Gateway.Razor; -public partial class VariableRow : IDisposable +public partial class VariableRow { [Parameter] public TableRowContext? RowContent { get; set; } - private bool Disposed; - public void Dispose() - { - Disposed = true; - timer?.SafeDispose(); - GC.SuppressFinalize(this); - } - TimerX? timer; - protected override void OnAfterRender(bool firstRender) - { - if (firstRender) - { - timer = new TimerX(Refresh, null, 1000, 1000, "VariableRow"); - } - base.OnAfterRender(firstRender); - } + //private bool Disposed; + //public void Dispose() + //{ + // Disposed = true; + // timer?.SafeDispose(); + // GC.SuppressFinalize(this); + //} + //TimerX? timer; + //protected override void OnAfterRender(bool firstRender) + //{ + // if (firstRender) + // { + // timer = new TimerX(Refresh, null, 1000, 1000, "VariableRow"); + // } + // base.OnAfterRender(firstRender); + //} - private Task Refresh(object? state) - { - if (!Disposed) - return InvokeAsync(StateHasChanged); - else - return Task.CompletedTask; - } + //private Task Refresh(object? state) + //{ + // if (!Disposed) + // return InvokeAsync(StateHasChanged); + // else + // return Task.CompletedTask; + //} protected override void OnParametersSet() { @@ -51,32 +47,6 @@ public partial class VariableRow : IDisposable CellClassStringCache?.Clear(); base.OnParametersSet(); } - /// - /// 获得 指定单元格数据方法 - /// - /// - /// - /// - protected static RenderFragment GetValue(ITableColumn col, VariableRuntime 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(); /// /// 获得指定列头固定列样式 @@ -209,7 +179,7 @@ public partial class VariableRow : IDisposable } else { - return FixedCellClassStringCache.GetOrAdd(col, col => CssBuilder.Default() + return FixedCellClassStringCache.GetOrAdd(col, col => CssBuilder.Default(col.GetFieldName()) .AddClass("fixed", col.Fixed) .AddClass("fixed-right", col.Fixed && IsTail(col)) .AddClass("fr", IsLastColumn(col)) diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRowHelpers.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRowHelpers.cs deleted file mode 100644 index c23336b79..000000000 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRowHelpers.cs +++ /dev/null @@ -1,60 +0,0 @@ -//------------------------------------------------------------------------------ -// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 -// 此代码版权(除特别声明外的代码)归作者本人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 29e0265fa..ae11f8424 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor @@ -4,10 +4,16 @@ @using ThingsGateway.Admin.Application @using ThingsGateway.Admin.Razor @using ThingsGateway.Gateway.Application -@inherits ComponentDefault +@attribute [JSModuleAutoLoader("Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor.js", AutoInvokeInit = true, AutoInvokeDispose = false, JSObjectReference = true)] +@inherits ThingsGatewayModuleComponentBase - - context.ChangeTime) Filterable=true Sortable=true Visible=false /> - context.CollectTime) Filterable=true Sortable=true Visible=true /> + context.ChangeTime) Filterable=true Sortable=true Visible=false /> + context.CollectTime) Filterable=true Sortable=true Visible=true /> context.IsOnline) Filterable=true Sortable=true Visible=true /> context.LastErrorMessage) Filterable=true Sortable=true Visible=false /> 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 90b137018..7b3754212 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 @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Components.Forms; using Microsoft.Extensions.Options; +using Microsoft.JSInterop; using ThingsGateway.Admin.Application; using ThingsGateway.Admin.Razor; @@ -19,11 +20,107 @@ using ThingsGateway.NewLife.Json.Extension; namespace ThingsGateway.Gateway.Razor; -public partial class VariableRuntimeInfo : IDisposable +public partial class VariableRuntimeInfo { - public List ColumnsFunc() + + [Inject] + [NotNull] + protected BlazorAppContext? AppContext { get; set; } + + [Inject] + [NotNull] + private NavigationManager? NavigationManager { get; set; } + + public string RouteName => NavigationManager.ToBaseRelativePath(NavigationManager.Uri); + + protected bool AuthorizeButton(string operate) { - return table?.Columns; + return AppContext.IsHasButtonWithRole(RouteName, operate); + } + [Inject] + [NotNull] + public IStringLocalizer? RazorLocalizer { get; set; } + + [Inject] + [NotNull] + public IStringLocalizer? AdminLocalizer { get; set; } + + [Inject] + [NotNull] + public DialogService? DialogService { get; set; } + + [NotNull] + public IStringLocalizer? Localizer { get; set; } + + [Inject] + [NotNull] + public IStringLocalizer? OperDescLocalizer { get; set; } + + [Inject] + [NotNull] + public ToastService? ToastService { get; set; } + + #region js + private List ColumnsFunc() + { + return table.Columns; + } + protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, new { Method = nameof(TriggerStateChanged) }); + + private Task OnColumnVisibleChanged(string name, bool visible) + { + _cachedFields = table.GetVisibleColumns.ToArray(); + return Task.CompletedTask; + } + private Task OnColumnCreating(List columns) + { + foreach (var column in columns) + { + column.OnCellRender = a => + { + a.Class = $"{column.GetFieldName()}"; + }; + } + return Task.CompletedTask; + } + + private ITableColumn[] _cachedFields = Array.Empty(); + + [JSInvokable] + public List TriggerStateChanged(int rowIndex) + { + try + { + + if (table == null) return null; + var row = table.Rows[rowIndex]; + if (_cachedFields.Length == 0) _cachedFields = table.GetVisibleColumns.ToArray(); + var list = new List(_cachedFields.Length); + foreach (var col in _cachedFields) + { + var fieldName = col.GetFieldName(); + list.Add(new(fieldName, VariableModelUtils.GetValue(row,fieldName))); + } + + return list; + + } + catch (Exception) + { + return new(); + } + } + + + #endregion + + protected override void OnAfterRender(bool firstRender) + { + if (firstRender || _cachedFields.Length == 0) + { + _cachedFields = table.GetVisibleColumns.ToArray(); + } + base.OnAfterRender(firstRender); } #if !Management @@ -43,17 +140,16 @@ public partial class VariableRuntimeInfo : IDisposable [Inject] private IOptions? WebsiteOption { get; set; } public bool Disposed { get; set; } - - - public void Dispose() + protected override ValueTask DisposeAsync(bool disposing) { Disposed = true; VariableRuntimeDispatchService.UnSubscribe(Refresh); - GC.SuppressFinalize(this); + return base.DisposeAsync(disposing); } protected override void OnInitialized() { + Localizer = App.CreateLocalizerByType(GetType()); VariableRuntimeDispatchService.Subscribe(Refresh); scheduler = new SmartTriggerScheduler(Notify, TimeSpan.FromMilliseconds(1000)); diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor.js b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor.js new file mode 100644 index 000000000..786927ec7 --- /dev/null +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Variable/VariableRuntimeInfo.razor.js @@ -0,0 +1,94 @@ +export function init(id, invoke, options) { + function getCellByClass(row, className) { + // 直接用 querySelector 精确查找 + return row.querySelector(`td.${className}`); + } + + var variableHandler = setInterval(async () => { + var admintable = document.getElementById(id); + + var tables = admintable.getElementsByTagName('table'); + + if (!tables || tables.length === 0) { + return; + } + + var table = tables[tables.length - 1]; + + if (!table) { + clearInterval(variableHandler) + return; + } + var rowCount = table.rows.length; + var { method } = options; + + for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) { + + + var row = table.rows[rowIndex]; + if (!row) continue; + + var vals = await invoke.invokeMethodAsync(method, rowIndex); + if (vals == null) continue; + + for (let i = 0; i < vals.length; i++) { + const cellName = vals[i].field; + const cellValue = vals[i].value; + if (cellValue == null) continue; + + var cell = getCellByClass(row, cellName) + + if (!cell) continue; + + // 查找 tooltip span + var cellDiv = cell.querySelector('.table-cell'); + if (cellDiv) { + var tooltipSpan = cell.querySelector('.bb-tooltip'); + if (tooltipSpan) { + + tooltipSpan.innerText = cellValue ?? ''; // 更新显示文字 + tooltipSpan.setAttribute('data-bs-original-title', cellValue ?? ''); // 同步 tooltip 提示 + continue; + + + } + else { + cellDiv.innerText = cellValue ?? ''; + } + } + + //// 查找 switch + //var switchDiv = cell.querySelector('.switch'); + //if (switchDiv) { + // if (cellValue === true || cellValue === "on" || cellValue === "True" || cellValue === "true") { + // switchDiv.classList.add('is-checked'); + // switchDiv.classList.add('enable'); + // switchDiv.classList.remove('is-unchecked'); + // switchDiv.classList.remove('disabled'); + + // switchDiv.querySelectorAll('span')[0].classList.add('border-success'); + // switchDiv.querySelectorAll('span')[0].classList.add('bg-success'); + + // } else { + // switchDiv.classList.remove('is-checked'); + // switchDiv.classList.remove('enable'); + // switchDiv.classList.add('is-unchecked'); + // switchDiv.classList.add('disabled'); + + // switchDiv.querySelectorAll('span')[0].classList.remove('border-success'); + // switchDiv.querySelectorAll('span')[0].classList.remove('bg-success'); + // } + // continue; + //} + //// 默认情况(普通单元格) + //getCellByClass(row, cellName).innerText = cellValue; + + + } + + } + } + , 500) //1000ms刷新一次 + +} +
- - @GetValue(col, RowContent.Row) - + +
+ @if(col.GetShowTips()) + { + + @VariableModelUtils.GetValue(RowContent.Row, col.GetFieldName()) + + } + else + { + @VariableModelUtils.GetValue(RowContent.Row, col.GetFieldName()) + } +