- 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)
{
-
-
- @GetValue(col, RowContent.Row)
-
+ |
+
+ @if(col.GetShowTips())
+ {
+
+ @VariableModelUtils.GetValue(RowContent.Row, col.GetFieldName())
+
+ }
+ else
+ {
+ @VariableModelUtils.GetValue(RowContent.Row, col.GetFieldName())
+ }
+
|
}
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刷新一次
+
+}
+