mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-11-04 09:33:58 +08:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			5ee8b50a92
			...
			10.11.104.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8a95f48f5a | ||
| 
						 | 
					14f3c31265 | ||
| 
						 | 
					1bad65378f | ||
| 
						 | 
					db3affc67e | 
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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; }
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.7" />
 | 
			
		||||
		<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor" Version="9.11.1" />
 | 
			
		||||
		<PackageReference Include="BootstrapBlazor" Version="9.11.2" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.NewLife.Threading;
 | 
			
		||||
namespace ThingsGateway.NewLife;
 | 
			
		||||
@@ -51,18 +52,20 @@ public class ExpiringDictionary<TKey, TValue> : IDisposable
 | 
			
		||||
            return rs;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private ConcurrentDictionary<TKey, CacheItem> _dict;
 | 
			
		||||
 | 
			
		||||
    private ConcurrentDictionary<TKey, CacheItem> _dict = new();
 | 
			
		||||
    private readonly TimerX _cleanupTimer;
 | 
			
		||||
    private int defaultExpire = 60;
 | 
			
		||||
    public ExpiringDictionary(int expire = 60)
 | 
			
		||||
    IEqualityComparer<TKey>? comparer;
 | 
			
		||||
    public ExpiringDictionary(int expire = 60, IEqualityComparer<TKey>? comparer = null)
 | 
			
		||||
    {
 | 
			
		||||
        defaultExpire = expire;
 | 
			
		||||
        this.comparer = comparer;
 | 
			
		||||
        _dict = new ConcurrentDictionary<TKey, CacheItem>(comparer);
 | 
			
		||||
 | 
			
		||||
        _cleanupTimer = new TimerX(TimerClear, null, 10000, 10000) { Async = true };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public bool TryAdd(TKey key, TValue value)
 | 
			
		||||
    {
 | 
			
		||||
        if (_dict.TryGetValue(key, out var item))
 | 
			
		||||
@@ -109,7 +112,7 @@ public class ExpiringDictionary<TKey, TValue> : IDisposable
 | 
			
		||||
    private void Clear(object? state)
 | 
			
		||||
    {
 | 
			
		||||
        var data = _dict;
 | 
			
		||||
        _dict = new();
 | 
			
		||||
        _dict = new(comparer);
 | 
			
		||||
        data.Clear();
 | 
			
		||||
    }
 | 
			
		||||
    private void TimerClear(object? state)
 | 
			
		||||
@@ -143,3 +146,4 @@ public class ExpiringDictionary<TKey, TValue> : IDisposable
 | 
			
		||||
        _cleanupTimer.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,6 @@
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.NewLife.Log;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.NewLife;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,6 @@
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.NewLife;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
	<Import Project="..\..\PackNuget.props" />
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<TargetFrameworks>net462;netstandard2.0;net6.0;net6.0-windows;net8.0;$(OtherTargetFrameworks);net8.0-windows;</TargetFrameworks>
 | 
			
		||||
		<TargetFrameworks>net47;netstandard2.0;net6.0;net6.0-windows;net8.0;$(OtherTargetFrameworks);net8.0-windows;</TargetFrameworks>
 | 
			
		||||
		<AssemblyName>ThingsGateway.NewLife.X</AssemblyName>
 | 
			
		||||
		<RootNamespace>ThingsGateway.NewLife</RootNamespace>
 | 
			
		||||
		<AssemblyTitle>工具核心库</AssemblyTitle>
 | 
			
		||||
@@ -35,12 +35,11 @@
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
 | 
			
		||||
		<PackageReference Include="System.Memory" Version="4.6.3" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='net462'">
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='net47'">
 | 
			
		||||
		<PackageReference Include="System.Memory" Version="4.6.3" />
 | 
			
		||||
		<PackageReference Include="System.ValueTuple" Version="4.6.1" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='net462'">
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='net47'">
 | 
			
		||||
		<Using Include="System.Net.Http" />
 | 
			
		||||
		<Reference Include="Microsoft.VisualBasic" />
 | 
			
		||||
		<Reference Include="System.Management" />
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.NewLife.Log;
 | 
			
		||||
using ThingsGateway.NewLife.Reflection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.NewLife.Threading;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@
 | 
			
		||||
		<PackageReference Include="Npgsql" Version="9.0.4" />
 | 
			
		||||
		<PackageReference Include="CsvHelper" Version="33.1.0" />
 | 
			
		||||
		<PackageReference Include="TDengine.Connector" Version="3.1.9" />
 | 
			
		||||
		<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.9.1" />
 | 
			
		||||
		<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.26.0" />
 | 
			
		||||
		<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.28" />
 | 
			
		||||
		<PackageReference Include="System.Data.Common" Version="4.3.0" />
 | 
			
		||||
		<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.2" />
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
<Project>
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<PluginVersion>10.11.99</PluginVersion>
 | 
			
		||||
		<ProPluginVersion>10.11.99</ProPluginVersion>
 | 
			
		||||
		<DefaultVersion>10.11.99</DefaultVersion>
 | 
			
		||||
		<PluginVersion>10.11.104</PluginVersion>
 | 
			
		||||
		<ProPluginVersion>10.11.104</ProPluginVersion>
 | 
			
		||||
		<DefaultVersion>10.11.104</DefaultVersion>
 | 
			
		||||
		<AuthenticationVersion>10.11.6</AuthenticationVersion>
 | 
			
		||||
		<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
 | 
			
		||||
		<NET8Version>8.0.20</NET8Version>
 | 
			
		||||
		<NET10Version>10.0.0-rc.1.25451.107</NET10Version>
 | 
			
		||||
		<NET8Version>8.0.21</NET8Version>
 | 
			
		||||
		<NET10Version>10.0.0-rc.2.25502.107</NET10Version>
 | 
			
		||||
		<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
 | 
			
		||||
		<IsTrimmable>false</IsTrimmable>
 | 
			
		||||
		<ManagementProPluginVersion>10.11.87</ManagementProPluginVersion>
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ public interface IChannelOptions
 | 
			
		||||
    bool RtsEnable { get; set; }
 | 
			
		||||
 | 
			
		||||
    bool StreamAsync { get; set; }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    Handshake Handshake { get; set; }
 | 
			
		||||
    #endregion
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,7 @@ public static class LoggerExtensions
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static string GetDeviceLogBasePath()
 | 
			
		||||
    {
 | 
			
		||||
        return PathExtensions.CombinePathWithOs("Logs","DeviceLog");
 | 
			
		||||
        return PathExtensions.CombinePathWithOs("Logs", "DeviceLog");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
}
 | 
			
		||||
@@ -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 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();
 | 
			
		||||
 | 
			
		||||
    /// <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;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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("导出")))
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user