Compare commits

...

2 Commits

Author SHA1 Message Date
2248356998 qq.com
1bad65378f build: 10.11.101 2025-10-14 11:46:26 +08:00
2248356998 qq.com
db3affc67e build: 10.11.100
feat(VariablePage): 优化UI性能
2025-10-14 10:18:09 +08:00
15 changed files with 396 additions and 18 deletions

View File

@@ -5,7 +5,7 @@
<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!"
DataService="DataService" CreateItemCallback="CreateItemCallback!" RenderMode=RenderMode
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
@@ -41,6 +41,7 @@
DoubleClickToEdit="DoubleClickToEdit"
OnDoubleClickCellCallback="OnDoubleClickCellCallback"
OnDoubleClickRowCallback="OnDoubleClickRowCallback"
RowContentTemplate="RowContentTemplate"
OnClickRowCallback="OnClickRowCallback">
</Table>
</div>

View File

@@ -13,6 +13,14 @@ namespace ThingsGateway.Admin.Razor;
[CascadingTypeParameter(nameof(TItem))]
public partial class AdminTable<TItem> where TItem : class, new()
{
/// <inheritdoc cref="Table{TItem}.RenderMode"/>
[Parameter]
public TableRenderMode RenderMode { get; set; }
public List<ITableColumn> Columns => Instance?.Columns;
/// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/>
[Parameter]
public EventCallback<List<TItem>> SelectedRowsChanged { get; set; }
@@ -40,6 +48,10 @@ public partial class AdminTable<TItem> where TItem : class, new()
/// <inheritdoc cref="Table{TItem}.OnDoubleClickRowCallback"/>
[Parameter]
public Func<TItem, Task>? OnDoubleClickRowCallback { get; set; }
/// <inheritdoc cref="Table{TItem}.RowContentTemplate"/>
[Parameter]
public RenderFragment<TableRowContext<TItem>>? RowContentTemplate { get; set; }
/// <inheritdoc cref="Table{TItem}.OnClickRowCallback"/>
[Parameter]
public Func<TItem, Task>? OnClickRowCallback { get; set; }

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.NewLife.Log;
namespace ThingsGateway.NewLife;
/// <summary>

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.NewLife;
namespace ThingsGateway;
/// <inheritdoc/>

View File

@@ -1,7 +1,6 @@
using System.Reflection;
using ThingsGateway.NewLife.Log;
using ThingsGateway.NewLife.Reflection;
namespace ThingsGateway.NewLife.Threading;

View File

@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<PluginVersion>10.11.99</PluginVersion>
<ProPluginVersion>10.11.99</ProPluginVersion>
<DefaultVersion>10.11.99</DefaultVersion>
<PluginVersion>10.11.101</PluginVersion>
<ProPluginVersion>10.11.101</ProPluginVersion>
<DefaultVersion>10.11.101</DefaultVersion>
<AuthenticationVersion>10.11.6</AuthenticationVersion>
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
<NET8Version>8.0.20</NET8Version>

View File

@@ -73,7 +73,7 @@ public interface IChannelOptions
bool RtsEnable { get; set; }
bool StreamAsync { get; set; }
Handshake Handshake { get; set; }
#endregion
/// <summary>

View File

@@ -100,7 +100,7 @@ public static class LoggerExtensions
/// </summary>
public static string GetDeviceLogBasePath()
{
return PathExtensions.CombinePathWithOs("Logs","DeviceLog");
return PathExtensions.CombinePathWithOs("Logs", "DeviceLog");
}
/// <summary>

View File

@@ -143,7 +143,7 @@ public class Channel : ChannelOptionsBase, IPrimaryIdEntity, IBaseDataEntity, IB
[SugarColumn(ColumnDescription = "StreamAsync", IsNullable = true)]
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public override bool StreamAsync { get; set; }
/// <summary>
/// Handshake
/// </summary>

View File

@@ -0,0 +1,19 @@
@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
@foreach (var col in RowContent.Columns)
{
<td class="@GetFixedCellClassString(col)" style="@GetFixedCellStyleString(col)">
<DynamicElement TagName="div" TriggerClick="@false"
StopPropagation="false"
class="@GetCellClassString(col, false, false)">
@GetValue(col, RowContent.Row)
</DynamicElement>
</td>
}

View File

@@ -0,0 +1,279 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人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<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;
}
protected override void OnParametersSet()
{
FixedCellClassStringCache?.Clear();
CellClassStringCache?.Clear();
base.OnParametersSet();
}
/// <summary>
/// 获得 指定单元格数据方法
/// </summary>
/// <param name="col"></param>
/// <param name="item"></param>
/// <returns></returns>
protected 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();
/// <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? 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<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());
}
}
private bool AllowResizing = true;
private bool IsTree = false;
private ConcurrentDictionary<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();
private ConcurrentDictionary<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 ConcurrentDictionary<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

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

View File

@@ -15,13 +15,13 @@
AllowResizing="true"
OnAdd="OnAdd"
IsFixedHeader=true
ShowCardView=false
IsMultipleSelect=true
SearchMode=SearchMode.Top
ShowExtendButtons=true
ShowToolbar="true"
ShowExportButton
IsAutoRefresh
AutoRefreshInterval="1000"
RenderMode="TableRenderMode.Table"
ShowDefaultButtons=true
ShowSearch=false
ExtendButtonColumnWidth=220
@@ -85,6 +85,11 @@
<VariableEditComponent Model=@(context) AutoRestartThread="AutoRestartThread"></VariableEditComponent>
</EditTemplate>
<RowContentTemplate Context="context">
<VariableRow RowContent="@context" ColumnsFunc="ColumnsFunc"></VariableRow>
</RowContentTemplate>
<ExportButtonDropdownTemplate Context="ExportContext">
@if ((AuthorizeButton("导出")))

View File

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

View File

@@ -15,9 +15,6 @@ using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using ThingsGateway.Gateway.Application;
namespace ThingsGateway.Server;