feat: js刷新变量表

This commit is contained in:
2248356998 qq.com
2025-10-16 10:52:50 +08:00
committed by Diego
parent 35edd7dc43
commit b22e90609b
12 changed files with 620 additions and 325 deletions

View File

@@ -4,8 +4,8 @@
<div class="tg-table h-100">
<Table TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate"
DataService="DataService" CreateItemCallback="CreateItemCallback!" RenderMode=RenderMode
<Table Id=@Id TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate"
DataService="DataService" CreateItemCallback="CreateItemCallback!" RenderMode=RenderMode OnColumnCreating=OnColumnCreating
IsPagination="IsPagination" PageItemsSource="PageItemsSource" IsFixedHeader="IsFixedHeader" IndentSize=24 RowHeight=RowHeight ShowSearchText="ShowSearchText" ShowSearchButton="ShowSearchButton" DisableEditButtonCallback="DisableEditButtonCallback" DisableDeleteButtonCallback="DisableDeleteButtonCallback" BeforeShowEditDialogCallback=" BeforeShowEditDialogCallback!"
IsTree="IsTree" OnTreeExpand="OnTreeExpand!" TreeNodeConverter="TreeNodeConverter!" TreeIcon="fa-solid fa-circle-chevron-right" TreeExpandIcon="fa-solid fa-circle-chevron-right fa-rotate-90" IsAutoQueryFirstRender=IsAutoQueryFirstRender
ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton
@@ -14,7 +14,7 @@
ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch" SearchModel=@SearchModel ShowLineNo
SearchMode=SearchMode ShowExportPdfButton=ShowExportPdfButton ExportButtonText=@ExportButtonText
ShowExportButton=@ShowExportButton Items=Items ClickToSelect=ClickToSelect ScrollMode=ScrollMode
ShowExportCsvButton=@ShowExportCsvButton ShowCardView=ShowCardView
ShowExportCsvButton=@ShowExportCsvButton ShowCardView=ShowCardView OnColumnVisibleChanged=OnColumnVisibleChanged
FixedExtendButtonsColumn=FixedExtendButtonsColumn FixedMultipleColumn=FixedMultipleColumn FixedDetailRowHeaderColumn=FixedDetailRowHeaderColumn FixedLineNoColumn=FixedLineNoColumn
IsAutoRefresh=IsAutoRefresh AutoRefreshInterval=AutoRefreshInterval
AllowDragColumn=@AllowDragColumn Height=@Height ShowRefresh=ShowRefresh

View File

@@ -13,13 +13,22 @@ namespace ThingsGateway.Admin.Razor;
[CascadingTypeParameter(nameof(TItem))]
public partial class AdminTable<TItem> where TItem : class, new()
{
/// <inheritdoc cref="Table{TItem}.OnColumnVisibleChanged"/>
[Parameter]
public Func<string,bool, Task> OnColumnVisibleChanged { get; set; }
/// <inheritdoc cref="Table{TItem}.OnColumnCreating"/>
[Parameter]
public Func<List<ITableColumn>,Task> OnColumnCreating { get; set; }
/// <inheritdoc cref="Table{TItem}.RenderMode"/>
[Parameter]
public TableRenderMode RenderMode { get; set; }
public List<ITableColumn> Columns => Instance?.Columns;
public IEnumerable<ITableColumn> GetVisibleColumns => Instance?.GetVisibleColumns();
public List<TItem> Rows => Instance?.Rows;
/// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/>
[Parameter]
@@ -158,6 +167,9 @@ public partial class AdminTable<TItem> where TItem : class, new()
[Parameter]
public IDataService<TItem> DataService { get; set; }
[Parameter]
public string? Id { get; set; }
/// <inheritdoc cref="Table{TItem}.CreateItemCallback"/>
[Parameter]
public Func<TItem> CreateItemCallback { get; set; }

View File

@@ -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);

View File

@@ -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
<div id="@Id">
@@ -8,12 +8,12 @@
<div class="quickactions-list">
<div class="quickactions-header">
<div class="flex-fill">@HeaderText</div>
<button class="btn-close btn-close-white" type="button" aria-label="Close" onclick=@(async ()=>await ToggleOpen())></button>
<button class="btn-close btn-close-white" type="button" aria-label="Close" onclick=@(async () => await ToggleOpen())></button>
</div>
<div class="mx-2 row g-0">
<div class="col-12 my-1">
<RadioList ShowLabel="true" TValue="bool" IsButton ValueExpression=@(()=> AutoRestartThread ) Value="AutoRestartThread" ValueChanged="OnAutoRestartThreadChanged" Items="AutoRestartThreadBoolItems" />
<RadioList ShowLabel="true" TValue="bool" IsButton ValueExpression=@(() => AutoRestartThread) Value="AutoRestartThread" ValueChanged="OnAutoRestartThreadChanged" Items="AutoRestartThreadBoolItems" />
</div>
</div>

View File

@@ -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; }
}

View File

@@ -0,0 +1,28 @@
using ThingsGateway.NewLife.Caching;
namespace ThingsGateway.NewLife;
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<VariableRuntime, object> data))
{
return data(model);
}
else
{
var ret = MemoryCache.GetOrAdd(fieldName, (fieldName) =>
{
return LambdaExtensions.GetPropertyValueLambda<VariableRuntime, object?>(model, fieldName).Compile();
})(model);
return ret;
}
}
}

View File

@@ -1,4 +1,4 @@
@namespace ThingsGateway.Gateway.Razor
@* @namespace ThingsGateway.Gateway.Razor
@using System.Text.Json.Nodes
@using Microsoft.Extensions.Hosting
@using ThingsGateway.Admin.Application
@@ -17,3 +17,4 @@
</DynamicElement>
</td>
}
*@

View File

@@ -1,279 +1,279 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
////------------------------------------------------------------------------------
//// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
//// 此代码版权除特别声明外的代码归作者本人Diego所有
//// 源代码使用协议遵循本仓库的开源协议及附加协议
//// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
//// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
//// 使用文档https://thingsgateway.cn/
//// QQ群605534569
////------------------------------------------------------------------------------
using System.Collections.Concurrent;
//using System.Collections.Concurrent;
using ThingsGateway.NewLife.Threading;
//using ThingsGateway.NewLife.Threading;
using TouchSocket.Core;
//using TouchSocket.Core;
namespace ThingsGateway.Gateway.Razor;
//namespace ThingsGateway.Gateway.Razor;
public partial class VariableRow : IDisposable
{
[Parameter]
public TableRowContext<VariableRuntime>? 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);
}
//public partial class VariableRow : IDisposable
//{
// [Parameter]
// public TableRowContext<VariableRuntime>? 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 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()
{
FixedCellClassStringCache?.Clear();
CellClassStringCache?.Clear();
base.OnParametersSet();
}
/// <summary>
/// 获得 指定单元格数据方法
/// </summary>
/// <param name="col"></param>
/// <param name="item"></param>
/// <returns></returns>
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));
}
};
// protected override void OnParametersSet()
// {
// FixedCellClassStringCache?.Clear();
// CellClassStringCache?.Clear();
// base.OnParametersSet();
// }
// /// <summary>
// /// 获得 指定单元格数据方法
// /// </summary>
// /// <param name="col"></param>
// /// <param name="item"></param>
// /// <returns></returns>
// 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();
// // internal static string? GetDoubleClickCellClassString(bool trigger) => CssBuilder.Default()
// //.AddClass("is-dbcell", trigger)
// //.Build();
/// <summary>
/// 获得指定列头固定列样式
/// </summary>
/// <param name="col"></param>
/// <param name="margin"></param>
/// <returns></returns>
protected string? GetFixedCellStyleString(ITableColumn col, int margin = 0)
{
string? ret = null;
if (col.Fixed)
{
ret = IsTail(col) ? GetRightStyle(col, margin) : GetLeftStyle(col);
}
return ret;
}
// /// <summary>
// /// 获得指定列头固定列样式
// /// </summary>
// /// <param name="col"></param>
// /// <param name="margin"></param>
// /// <returns></returns>
// 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? 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);
// 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;
}
// // 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;
// // 如果是固定表头时增加滚动条位置
// 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;
// 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 NonBlockingDictionary<ITableColumn, string> CellClassStringCache { get; } = new(ReferenceEqualityComparer.Instance);
// private bool IsTail(ITableColumn col)
// {
// var middle = Math.Floor(RowContent.Columns.Count() * 1.0 / 2);
// var index = Columns.IndexOf(col);
// return middle < index;
// }
// private NonBlockingDictionary<ITableColumn, string> CellClassStringCache { get; } = new(ReferenceEqualityComparer.Instance);
/// <summary>
/// 获得 Cell 文字样式
/// </summary>
/// <param name="col"></param>
/// <param name="hasChildren"></param>
/// <param name="inCell"></param>
/// <returns></returns>
protected string? GetCellClassString(ITableColumn col, bool hasChildren, bool inCell)
{
if (CellClassStringCache.TryGetValue(col, out var cached))
{
return cached;
}
else
{
bool trigger = false;
return 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("is-dbcell", trigger)
.AddClass(col.CssClass)
.Build());
}
// /// <summary>
// /// 获得 Cell 文字样式
// /// </summary>
// /// <param name="col"></param>
// /// <param name="hasChildren"></param>
// /// <param name="inCell"></param>
// /// <returns></returns>
// protected string? GetCellClassString(ITableColumn col, bool hasChildren, bool inCell)
// {
// if (CellClassStringCache.TryGetValue(col, out var cached))
// {
// return cached;
// }
// else
// {
// bool trigger = false;
// return 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("is-dbcell", trigger)
// .AddClass(col.CssClass)
// .Build());
// }
}
// }
private bool AllowResizing = true;
private bool IsTree = false;
// private bool AllowResizing = true;
// private bool IsTree = false;
private NonBlockingDictionary<ITableColumn, string> FixedCellClassStringCache { get; } = new(ReferenceEqualityComparer.Instance);
/// <summary>
/// 获得指定列头固定列样式
/// </summary>
/// <param name="col"></param>
/// <returns></returns>
protected string? GetFixedCellClassString(ITableColumn col)
{
if (FixedCellClassStringCache.TryGetValue(col, out var cached))
{
return cached;
}
else
{
return 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());
// private NonBlockingDictionary<ITableColumn, string> FixedCellClassStringCache { get; } = new(ReferenceEqualityComparer.Instance);
// /// <summary>
// /// 获得指定列头固定列样式
// /// </summary>
// /// <param name="col"></param>
// /// <returns></returns>
// protected string? GetFixedCellClassString(ITableColumn col)
// {
// if (FixedCellClassStringCache.TryGetValue(col, out var cached))
// {
// return cached;
// }
// else
// {
// return 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<List<ITableColumn>> ColumnsFunc { get; set; }
public List<ITableColumn> Columns => ColumnsFunc();
// [Parameter]
// public Func<List<ITableColumn>> ColumnsFunc { get; set; }
// public List<ITableColumn> Columns => ColumnsFunc();
private NonBlockingDictionary<ITableColumn, bool> LastFixedColumnCache { get; } = new(ReferenceEqualityComparer.Instance);
private bool IsLastColumn(ITableColumn col)
{
if (LastFixedColumnCache.TryGetValue(col, out var cached))
{
return cached;
}
else
{
return 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 NonBlockingDictionary<ITableColumn, bool> LastFixedColumnCache { get; } = new(ReferenceEqualityComparer.Instance);
// private bool IsLastColumn(ITableColumn col)
// {
// if (LastFixedColumnCache.TryGetValue(col, out var cached))
// {
// return cached;
// }
// else
// {
// return 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 NonBlockingDictionary<ITableColumn, bool> FirstFixedColumnCache { get; } = new(ReferenceEqualityComparer.Instance);
private bool IsFirstColumn(ITableColumn col)
{
if (FirstFixedColumnCache.TryGetValue(col, out var cached))
{
return cached;
}
else
{
return 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;
});
// }
// }
// private NonBlockingDictionary<ITableColumn, bool> FirstFixedColumnCache { get; } = new(ReferenceEqualityComparer.Instance);
// private bool IsFirstColumn(ITableColumn col)
// {
// if (FirstFixedColumnCache.TryGetValue(col, out var cached))
// {
// return cached;
// }
// else
// {
// return 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;
// });
}
}
// }
// }
}
//}

View File

@@ -1,60 +1,60 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
////------------------------------------------------------------------------------
//// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
//// 此代码版权除特别声明外的代码归作者本人Diego所有
//// 源代码使用协议遵循本仓库的开源协议及附加协议
//// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
//// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
//// 使用文档https://thingsgateway.cn/
//// QQ群605534569
////------------------------------------------------------------------------------
namespace ThingsGateway.Gateway.Razor;
//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 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<TItem>(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<TItem>(this ITableColumn col, TItem item)
{
var fieldName = col.GetFieldName();
object? ret;
if (item is IDynamicObject dynamicObject)
{
ret = dynamicObject.GetValue(fieldName);
}
else
{
ret = Utility.GetPropertyValue<TItem, object?>(item, fieldName);
// internal static RenderFragment RenderColor<TItem>(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<TItem>(this ITableColumn col, TItem item)
// {
// var fieldName = col.GetFieldName();
// object? ret;
// if (item is IDynamicObject dynamicObject)
// {
// ret = dynamicObject.GetValue(fieldName);
// }
// else
// {
// ret = Utility.GetPropertyValue<TItem, object?>(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;
// 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;
}
//}

View File

@@ -4,9 +4,13 @@
@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
<AdminTable @ref=table BeforeShowEditDialogCallback="BeforeShowEditDialogCallback"
@* RenderMode="TableRenderMode.Table"
ShowCardView=false *@
<AdminTable Id=@Id @ref=table BeforeShowEditDialogCallback="BeforeShowEditDialogCallback"
TItem="VariableRuntime"
EditDialogSize="Size.ExtraLarge"
AutoGenerateColumns="true"
@@ -15,13 +19,13 @@
AllowResizing="true"
OnAdd="OnAdd"
IsFixedHeader=true
ShowCardView=false
IsMultipleSelect=true
SearchMode=SearchMode.Top
ShowExtendButtons=true
ShowToolbar="true"
ShowExportButton
RenderMode="TableRenderMode.Table"
OnColumnCreating="OnColumnCreating"
OnColumnVisibleChanged=OnColumnVisibleChanged
ShowDefaultButtons=true
ShowSearch=false
ExtendButtonColumnWidth=220
@@ -85,11 +89,11 @@
<VariableEditComponent Model=@(context) AutoRestartThread="AutoRestartThread"></VariableEditComponent>
</EditTemplate>
<RowContentTemplate Context="context">
@* <RowContentTemplate Context="context">
<VariableRow RowContent="@context" ColumnsFunc="ColumnsFunc"></VariableRow>
</RowContentTemplate>
</RowContentTemplate> *@
<ExportButtonDropdownTemplate Context="ExportContext">
@if ((AuthorizeButton("导出")))

View File

@@ -10,20 +10,168 @@
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
using ThingsGateway.Admin.Application;
using ThingsGateway.Admin.Razor;
using ThingsGateway.DB;
using ThingsGateway.Extension.Generic;
using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Json.Extension;
namespace ThingsGateway.Gateway.Razor;
public partial class VariableRuntimeInfo : IDisposable
public partial class VariableRuntimeInfo
{
public List<ITableColumn> 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<ThingsGateway.Razor._Imports>? RazorLocalizer { get; set; }
[Inject]
[NotNull]
public IStringLocalizer<ThingsGateway.Admin.Razor._Imports>? AdminLocalizer { get; set; }
[Inject]
[NotNull]
public DialogService? DialogService { get; set; }
[NotNull]
public IStringLocalizer? Localizer { get; set; }
[Inject]
[NotNull]
public IStringLocalizer<OperDescAttribute>? OperDescLocalizer { get; set; }
[Inject]
[NotNull]
public ToastService? ToastService { get; set; }
#region js
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<ITableColumn> columns)
{
foreach (var column in columns)
{
column.OnCellRender = a =>
{
a.Class = $"{column.GetFieldName()}";
};
}
return Task.CompletedTask;
}
private ITableColumn[] _cachedFields = Array.Empty<ITableColumn>();
[JSInvokable]
public List<CellValue> 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<CellValue>(_cachedFields.Length);
foreach (var col in _cachedFields)
{
var fieldName = col.GetFieldName();
switch (fieldName)
{
case nameof(VariableRuntime.Value):
list.Add(new(fieldName, row.Value?.ToSystemTextJsonString() ?? string.Empty));
break;
case nameof(VariableRuntime.RawValue):
list.Add(new(fieldName, row.RawValue?.ToSystemTextJsonString() ?? string.Empty));
break;
case nameof(VariableRuntime.LastSetValue):
list.Add(new(fieldName, row.LastSetValue?.ToSystemTextJsonString() ?? string.Empty));
break;
case nameof(VariableRuntime.ChangeTime):
list.Add(new(fieldName, row.ChangeTime.ToString("yyyy-MM-dd HH:mm:ss.fff")));
break;
case nameof(VariableRuntime.CollectTime):
list.Add(new(fieldName, row.CollectTime.ToString("yyyy-MM-dd HH:mm:ss.fff")));
break;
case nameof(VariableRuntime.IsOnline):
list.Add(new(fieldName, row.IsOnline.ToString()));
break;
case nameof(VariableRuntime.LastErrorMessage):
list.Add(new(fieldName, row.LastErrorMessage));
break;
case nameof(VariableRuntime.RuntimeType):
list.Add(new(fieldName, row.RuntimeType));
break;
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);
}
}
}
list.Add(new(fieldName, ret is string str ? str : ret?.ToString() ?? string.Empty));
break;
}
}
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 +191,16 @@ public partial class VariableRuntimeInfo : IDisposable
[Inject]
private IOptions<WebsiteOptions>? 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));

View File

@@ -0,0 +1,85 @@
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 tooltipSpan = cell.querySelector('.bb-tooltip');
if (tooltipSpan) {
tooltipSpan.innerText = cellValue ?? ''; // 更新显示文字
tooltipSpan.setAttribute('data-bs-original-title', cellValue ?? ''); // 同步 tooltip 提示
continue;
}
// 查找 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;
}
}
}
, 1000) //1000ms刷新一次
}