mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-20 10:50:48 +08:00
fix: 变量实时值偶发错误
feat: 优化内存占用
This commit is contained in:
@@ -21,6 +21,7 @@ public class TestController : ControllerBase
|
||||
[HttpPost]
|
||||
public async Task Test(string data)
|
||||
{
|
||||
GC.Collect();
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
@@ -22,89 +22,87 @@
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
|
||||
@inject NavigationManager NavigationManager
|
||||
@if (AppContext.CurrentUser != null && AppContext.OwnMenus != null)
|
||||
{
|
||||
|
||||
<LoginConnectionHub />
|
||||
<CascadingValue Value="ReloadMenu" Name="ReloadMenu" IsFixed="true">
|
||||
<CascadingValue Value="ReloadUser" Name="ReloadUser" IsFixed="true">
|
||||
|
||||
<div class="mainlayout">
|
||||
<LoginConnectionHub />
|
||||
<CascadingValue Value="ReloadMenu" Name="ReloadMenu" IsFixed="true">
|
||||
<CascadingValue Value="ReloadUser" Name="ReloadUser" IsFixed="true">
|
||||
|
||||
<Layout SideWidth="0" IsPage="true" IsFullSide="true" IsFixedHeader="true"
|
||||
ShowGotoTop="true" ShowCollapseBar="true" Menus="@MenuService.MenuItems"
|
||||
AdditionalAssemblies=App.RazorAssemblies AllowDragTab=true
|
||||
UseTabSet="false" TabDefaultUrl="/">
|
||||
<Header>
|
||||
<div class="ms-4"></div>
|
||||
<ChoiceModuleComponent Value=@(AppContext.CurrentModuleId) ModuleList=@(AppContext.CurrentUser.ModuleList) OnClick=@(ChoiceModule) />
|
||||
<div class="mainlayout">
|
||||
|
||||
<div class="flex-fill"></div>
|
||||
<Layout SideWidth="0" IsPage="true" IsFullSide="true" IsFixedHeader="true"
|
||||
ShowGotoTop="true" ShowCollapseBar="true" Menus="@MenuService.MenuItems"
|
||||
AdditionalAssemblies=App.RazorAssemblies AllowDragTab=true
|
||||
UseTabSet="false" TabDefaultUrl="/">
|
||||
<Header>
|
||||
<div class="ms-4"></div>
|
||||
<ChoiceModuleComponent Value=@(AppContext.CurrentModuleId) ModuleList=@(AppContext.CurrentUser.ModuleList) OnClick=@(ChoiceModule) />
|
||||
|
||||
@* 搜索框 *@
|
||||
<GlobalSearch Menus=@(MenuService.SameLevelMenuItems) />
|
||||
<div class="flex-fill"></div>
|
||||
|
||||
@* 语言选择 *@
|
||||
<div class="d-none d-xl-flex ">
|
||||
<CultureChooser />
|
||||
@* 搜索框 *@
|
||||
<GlobalSearch Menus=@(MenuService.SameLevelMenuItems) />
|
||||
|
||||
@* 语言选择 *@
|
||||
<div class="d-none d-xl-flex ">
|
||||
<CultureChooser />
|
||||
</div>
|
||||
|
||||
<Logout ImageUrl="@(AppContext.CurrentUser.Avatar??$"{WebsiteConst.DefaultResourceUrl}images/defaultUser.svg")" ShowUserName=false DisplayName="@UserManager.UserAccount" UserName="@UserManager.VerificatId.ToString()" PrefixUserNameText=@AdminLocalizer["CurrentVerificat"]>
|
||||
<LinkTemplate>
|
||||
<a href=@("/") class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["系统首页"]</a>
|
||||
|
||||
<a @onclick="@OnUserInfoDialog" class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["UserCenter"]</a>
|
||||
<a @onclick="@LogoutAsync" class="h6"><i class="fa-solid fa-key me-2"></i>@Localizer["Logout"]</a>
|
||||
|
||||
|
||||
</LinkTemplate>
|
||||
</Logout>
|
||||
|
||||
@* 全屏按钮 *@
|
||||
<FullScreenButton class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-arrows-alt"
|
||||
TooltipPlacement=Placement.Bottom TooltipText="@Localizer[nameof(FullScreenButton)]" />
|
||||
|
||||
@if (WebsiteOption.Value.IsShowAbout)
|
||||
{
|
||||
<Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
|
||||
}
|
||||
@* 版本号 *@
|
||||
<div class="px-1 navbar-header-text d-none d-lg-block">@_versionString</div>
|
||||
|
||||
@* 主题切换 *@
|
||||
@* <ThemeToggle /> *@
|
||||
<ThemeProvider class="layout-header-bar d-none d-lg-flex px-0"></ThemeProvider>
|
||||
|
||||
</Header>
|
||||
<Side>
|
||||
<div class="layout-banner">
|
||||
<span class="avatar">
|
||||
@WebsiteOption.Value.Title?.GetNameLen2()
|
||||
</span>
|
||||
|
||||
<div class="layout-title d-flex align-items-center justify-content-center">
|
||||
<span>@WebsiteOption.Value.Title</span>
|
||||
</div>
|
||||
|
||||
<Logout ImageUrl="@(AppContext.CurrentUser.Avatar??$"{WebsiteConst.DefaultResourceUrl}images/defaultUser.svg")" ShowUserName=false DisplayName="@UserManager.UserAccount" UserName="@UserManager.VerificatId.ToString()" PrefixUserNameText=@AdminLocalizer["CurrentVerificat"]>
|
||||
<LinkTemplate>
|
||||
<a href=@("/") class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["系统首页"]</a>
|
||||
|
||||
<a @onclick="@OnUserInfoDialog" class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["UserCenter"]</a>
|
||||
<a @onclick="@LogoutAsync" class="h6"><i class="fa-solid fa-key me-2"></i>@Localizer["Logout"]</a>
|
||||
|
||||
|
||||
</LinkTemplate>
|
||||
</Logout>
|
||||
|
||||
@* 全屏按钮 *@
|
||||
<FullScreenButton class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-arrows-alt"
|
||||
TooltipPlacement=Placement.Bottom TooltipText="@Localizer[nameof(FullScreenButton)]" />
|
||||
|
||||
@if (WebsiteOption.Value.IsShowAbout)
|
||||
</div>
|
||||
</Side>
|
||||
<Main>
|
||||
<Tab @ref=Tab ClickTabToNavigation="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
|
||||
AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.MenuItems"
|
||||
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
|
||||
{
|
||||
<Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
|
||||
}
|
||||
@* 版本号 *@
|
||||
<div class="px-1 navbar-header-text d-none d-lg-block">@_versionString</div>
|
||||
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));
|
||||
})>
|
||||
</Tab>
|
||||
|
||||
@* 主题切换 *@
|
||||
@* <ThemeToggle /> *@
|
||||
<ThemeProvider class="layout-header-bar d-none d-lg-flex px-0"></ThemeProvider>
|
||||
</Main>
|
||||
<NotAuthorized>
|
||||
<Redirect Url="/Account/Login" />
|
||||
</NotAuthorized>
|
||||
</Layout>
|
||||
|
||||
</Header>
|
||||
<Side>
|
||||
<div class="layout-banner">
|
||||
<span class="avatar">
|
||||
@WebsiteOption.Value.Title?.GetNameLen2()
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="layout-title d-flex align-items-center justify-content-center">
|
||||
<span>@WebsiteOption.Value.Title</span>
|
||||
</div>
|
||||
</div>
|
||||
</Side>
|
||||
<Main>
|
||||
<Tab @ref=Tab ClickTabToNavigation="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
|
||||
AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.MenuItems"
|
||||
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
|
||||
{
|
||||
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));
|
||||
})>
|
||||
</Tab>
|
||||
|
||||
</Main>
|
||||
<NotAuthorized>
|
||||
<Redirect Url="/Account/Login" />
|
||||
</NotAuthorized>
|
||||
</Layout>
|
||||
|
||||
</div>
|
||||
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
|
||||
}
|
||||
|
@@ -166,7 +166,6 @@ public partial class MainLayout : IDisposable
|
||||
DispatchService.Subscribe(Dispatch);
|
||||
await AppContext.InitUserAsync();
|
||||
await AppContext.InitMenus(NavigationManager.ToBaseRelativePath(NavigationManager.Uri));
|
||||
StateHasChanged();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
private Tab Tab { get; set; }
|
||||
|
@@ -349,7 +349,7 @@ public sealed class HttpContextForwardBuilder
|
||||
// 初始化 MultipartReader 实例
|
||||
var multipartReader = new MultipartReader(boundary, bodyStream);
|
||||
|
||||
while ((await multipartReader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false))is { } multipartSection)
|
||||
while ((await multipartReader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false)) is { } multipartSection)
|
||||
{
|
||||
// 检查当前节是否为文件节
|
||||
if (multipartSection.AsFileSection() is not null)
|
||||
@@ -364,7 +364,7 @@ public sealed class HttpContextForwardBuilder
|
||||
await CopyTextMultipartSectionAsync(multipartSection, httpMultipartFormDataBuilder, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 设置多部分表单内容
|
||||
|
@@ -23,6 +23,7 @@
|
||||
"False": "No",
|
||||
"Save": "Save",
|
||||
"Fail": "Fail {0}",
|
||||
"Clear": "Clear",
|
||||
"Success": "Success",
|
||||
"PleaseSelect": "Please select the data",
|
||||
"TablesExportButtonExcelText": "Export Excel",
|
||||
|
@@ -23,6 +23,7 @@
|
||||
"False": "否",
|
||||
"Save": "保存",
|
||||
"Fail": "失败 {0}",
|
||||
"Clear": "清空",
|
||||
"Success": "成功",
|
||||
"PleaseSelect": "请选择需要操作的数据",
|
||||
"TablesExportButtonExcelText": "导出Excel",
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.0.0.18</PluginVersion>
|
||||
<ProPluginVersion>10.0.0.18</ProPluginVersion>
|
||||
<PluginVersion>10.0.0.19</PluginVersion>
|
||||
<ProPluginVersion>10.0.0.19</ProPluginVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@@ -13,7 +13,6 @@ using Newtonsoft.Json.Linq;
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
using ThingsGateway.NewLife.Caching;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
@@ -22,6 +21,8 @@ namespace ThingsGateway.Foundation;
|
||||
/// </summary>
|
||||
public static class ThingsGatewayBitConverterExtension
|
||||
{
|
||||
|
||||
//private static MemoryCache MemoryCache = new() { Capacity = 10000000 };
|
||||
/// <summary>
|
||||
/// 从设备地址中解析附加信息
|
||||
/// 这个方法获取<see cref="IThingsGatewayBitConverter"/>
|
||||
@@ -36,18 +37,18 @@ public static class ThingsGatewayBitConverterExtension
|
||||
|
||||
var type = defaultBitConverter.GetType();
|
||||
// 尝试从缓存中获取解析结果
|
||||
var cacheKey = $"{nameof(ThingsGatewayBitConverterExtension)}_{nameof(GetTransByAddress)}_{type.FullName}_{type.TypeHandle.Value}_{defaultBitConverter.ToJsonString()}_{registerAddress}";
|
||||
if (MemoryCache.Instance.TryGetValue(cacheKey, out IThingsGatewayBitConverter cachedConverter))
|
||||
{
|
||||
if (cachedConverter.Equals(defaultBitConverter))
|
||||
{
|
||||
return defaultBitConverter;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (IThingsGatewayBitConverter)cachedConverter.Map(type);
|
||||
}
|
||||
}
|
||||
//var cacheKey = $"{nameof(ThingsGatewayBitConverterExtension)}_{nameof(GetTransByAddress)}_{type.FullName}_{type.TypeHandle.Value}_{defaultBitConverter.ToJsonString()}_{registerAddress}_{defaultBitConverter.GetHashCode()}";
|
||||
//if (MemoryCache.TryGetValue(cacheKey, out IThingsGatewayBitConverter cachedConverter))
|
||||
//{
|
||||
// if (cachedConverter.Equals(defaultBitConverter))
|
||||
// {
|
||||
// return defaultBitConverter;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return (IThingsGatewayBitConverter)cachedConverter.Map(type);
|
||||
// }
|
||||
//}
|
||||
|
||||
// 去除设备地址两端的空格
|
||||
registerAddress = registerAddress.Trim();
|
||||
@@ -109,7 +110,7 @@ public static class ThingsGatewayBitConverterExtension
|
||||
// 如果没有解析出任何附加信息,则直接返回默认的数据转换器
|
||||
if (bcdFormat == null && stringlength == null && encoding == null && dataFormat == null && wstring == null)
|
||||
{
|
||||
MemoryCache.Instance.Set(cacheKey, defaultBitConverter!, 3600);
|
||||
//MemoryCache.Set(cacheKey, defaultBitConverter!, 3600);
|
||||
return defaultBitConverter;
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ public static class ThingsGatewayBitConverterExtension
|
||||
}
|
||||
|
||||
// 将解析结果添加到缓存中,缓存有效期为3600秒
|
||||
MemoryCache.Instance.Set(cacheKey, converter!, 3600);
|
||||
//MemoryCache.Set(cacheKey, converter!, 3600);
|
||||
return converter;
|
||||
}
|
||||
|
||||
|
@@ -85,7 +85,7 @@ public class RuntimeInfoController : ControllerBase
|
||||
.WhereIF(!input.RegisterAddress.IsNullOrEmpty(), a => a.RegisterAddress == input.RegisterAddress)
|
||||
.WhereIF(!input.Name.IsNullOrEmpty(), a => a.Name == input.Name)
|
||||
.WhereIF(!input.DeviceName.IsNullOrEmpty(), a => a.DeviceName == input.DeviceName)
|
||||
.WhereIF(input.BusinessDeviceId > 0, a => a.VariablePropertys.ContainsKey(input.BusinessDeviceId))
|
||||
.WhereIF(input.BusinessDeviceId > 0, a => a.VariablePropertys?.ContainsKey(input.BusinessDeviceId) == true)
|
||||
.ToPagedList(input);
|
||||
return data.Adapt<SqlSugarPagedList<AlarmVariable>>();
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public class RuntimeInfoController : ControllerBase
|
||||
.WhereIF(!input.Name.IsNullOrWhiteSpace(), a => a.Name == input.Name)
|
||||
.WhereIF(!input.DeviceName.IsNullOrEmpty(), a => a.DeviceName == input.DeviceName)
|
||||
.WhereIF(!input.RegisterAddress.IsNullOrWhiteSpace(), a => a.RegisterAddress == input.RegisterAddress)
|
||||
.WhereIF(input.BusinessDeviceId > 0, a => a.VariablePropertys.ContainsKey(input.BusinessDeviceId))
|
||||
.WhereIF(input.BusinessDeviceId > 0, a => a.VariablePropertys?.ContainsKey(input.BusinessDeviceId) == true)
|
||||
|
||||
.ToPagedList(input);
|
||||
return data;
|
||||
|
@@ -71,7 +71,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
|
||||
|
||||
|
||||
// 触发一次设备状态变化和变量值变化事件
|
||||
CollectDevices.ForEach(a =>
|
||||
CollectDevices?.ForEach(a =>
|
||||
{
|
||||
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine)
|
||||
DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>());
|
||||
@@ -162,10 +162,14 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
|
||||
{
|
||||
if (_exT2TimerTick.IsTickHappen())
|
||||
{
|
||||
// 间隔推送全部设备
|
||||
foreach (var deviceRuntime in CollectDevices.Select(a => a.Value))
|
||||
if (CollectDevices != null)
|
||||
{
|
||||
DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>());
|
||||
|
||||
// 间隔推送全部设备
|
||||
foreach (var deviceRuntime in CollectDevices.Select(a => a.Value))
|
||||
{
|
||||
DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,7 +241,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
// 检查当前设备的设备列表是否包含此设备,如果包含,则触发设备的状态变化处理方法
|
||||
if (CollectDevices.ContainsKey(deviceData.Id))
|
||||
if (CollectDevices?.ContainsKey(deviceData.Id) == true)
|
||||
DeviceChange(deviceRuntime, deviceData);
|
||||
}
|
||||
}
|
||||
|
@@ -75,7 +75,7 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
|
||||
}
|
||||
|
||||
|
||||
CollectDevices.ForEach(a =>
|
||||
CollectDevices?.ForEach(a =>
|
||||
{
|
||||
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine)
|
||||
DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>());
|
||||
@@ -159,10 +159,13 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
|
||||
{
|
||||
if (_exT2TimerTick.IsTickHappen())
|
||||
{
|
||||
// 上传所有设备信息
|
||||
foreach (var deviceRuntime in CollectDevices.Select(a => a.Value))
|
||||
if (CollectDevices != null)
|
||||
{
|
||||
DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>());
|
||||
// 上传所有设备信息
|
||||
foreach (var deviceRuntime in CollectDevices.Select(a => a.Value))
|
||||
{
|
||||
DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,7 +222,7 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
// 检查当前设备集合中是否包含该设备,并进行相应处理
|
||||
if (CollectDevices.ContainsKey(deviceRuntime.Id))
|
||||
if (CollectDevices?.ContainsKey(deviceRuntime.Id) == true)
|
||||
DeviceChange(deviceRuntime, deviceData);
|
||||
}
|
||||
}
|
||||
|
@@ -167,7 +167,7 @@ public class Variable : BaseDataEntity, IValidatableObject
|
||||
[SugarColumn(IsJson = true, ColumnDataType = StaticConfig.CodeFirst_BigString, ColumnDescription = "变量属性Json", IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public ConcurrentDictionary<long, Dictionary<string, string>>? VariablePropertys { get; set; } = new();
|
||||
public Dictionary<long, Dictionary<string, string>>? VariablePropertys { get; set; }
|
||||
|
||||
#region 报警
|
||||
/// <summary>
|
||||
|
@@ -35,6 +35,11 @@ public delegate void VariableChangeEventHandler(VariableRuntime variableRuntime,
|
||||
/// <param name="variableRuntime">变量运行时对象</param>
|
||||
public delegate void VariableCollectEventHandler(VariableRuntime variableRuntime);
|
||||
|
||||
/// <summary>
|
||||
/// 变量报警事件委托
|
||||
/// </summary>
|
||||
public delegate void VariableAlarmEventHandler(AlarmVariable alarmVariable);
|
||||
|
||||
/// <summary>
|
||||
/// 采集设备值与状态全局提供类,用于提供全局的设备状态和变量数据的管理
|
||||
/// </summary>
|
||||
@@ -57,7 +62,7 @@ public static class GlobalData
|
||||
/// <summary>
|
||||
/// 报警变化事件
|
||||
/// </summary>
|
||||
public static event VariableAlarmEventHandler AlarmChangedEvent;
|
||||
public static event VariableAlarmEventHandler? AlarmChangedEvent;
|
||||
|
||||
/// <summary>
|
||||
/// 只读的通道字典,提供对通道的只读访问
|
||||
|
@@ -19,11 +19,6 @@ using ThingsGateway.NewLife.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 变量报警事件委托
|
||||
/// </summary>
|
||||
public delegate void VariableAlarmEventHandler(AlarmVariable alarmVariable);
|
||||
|
||||
/// <summary>
|
||||
/// 设备采集报警后台服务
|
||||
/// </summary>
|
||||
|
@@ -832,13 +832,14 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
|
||||
Disposed = true;
|
||||
try
|
||||
{
|
||||
Channel?.SafeDispose();
|
||||
|
||||
LogMessage?.LogInformation(Localizer["ChannelDispose", CurrentChannel?.Name ?? string.Empty]);
|
||||
|
||||
await NewDeviceLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
await PrivateRemoveDevicesAsync(Drivers.Keys).ConfigureAwait(false);
|
||||
Channel?.SafeDispose();
|
||||
|
||||
LogMessage?.LogInformation(Localizer["ChannelDispose", CurrentChannel?.Name ?? string.Empty]);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@@ -138,9 +138,9 @@ public static class PluginServiceUtil
|
||||
/// <summary>
|
||||
/// 通过实体赋值到字典中
|
||||
/// </summary>
|
||||
public static ConcurrentDictionary<long, Dictionary<string, string>> SetDict(ConcurrentDictionary<long, ModelValueValidateForm>? models)
|
||||
public static Dictionary<long, Dictionary<string, string>> SetDict(ConcurrentDictionary<long, ModelValueValidateForm>? models)
|
||||
{
|
||||
ConcurrentDictionary<long, Dictionary<string, string>> results = new();
|
||||
Dictionary<long, Dictionary<string, string>> results = new();
|
||||
models ??= new();
|
||||
foreach (var model in models)
|
||||
{
|
||||
|
@@ -43,7 +43,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
foreach (var item in group)
|
||||
{
|
||||
//需要重启业务线程
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => item.Value.VariablePropertys.ContainsKey(a.Key)).Select(a => a.Value);
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => item.Value.VariablePropertys?.ContainsKey(a.Key) == true).Select(a => a.Value);
|
||||
foreach (var deviceRuntime in deviceRuntimes)
|
||||
{
|
||||
if (deviceRuntime.Driver != null)
|
||||
@@ -116,7 +116,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
foreach (var item in group)
|
||||
{
|
||||
//需要重启业务线程
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => item.Value.VariablePropertys.ContainsKey(a.Key)).Select(a => a.Value);
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => item.Value.VariablePropertys?.ContainsKey(a.Key) == true).Select(a => a.Value);
|
||||
foreach (var deviceRuntime in deviceRuntimes)
|
||||
{
|
||||
if (deviceRuntime.Driver != null)
|
||||
@@ -192,7 +192,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
foreach (var item in group)
|
||||
{
|
||||
//需要重启业务线程
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => item.VariablePropertys.ContainsKey(a.Key)).Select(a => a.Value);
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => item.VariablePropertys?.ContainsKey(a.Key) == true).Select(a => a.Value);
|
||||
foreach (var deviceRuntime in deviceRuntimes)
|
||||
{
|
||||
if (deviceRuntime.Driver != null)
|
||||
@@ -254,7 +254,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
foreach (var item in group)
|
||||
{
|
||||
//需要重启业务线程
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => item.Value.VariablePropertys.ContainsKey(a.Key)).Select(a => a.Value);
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => item.Value.VariablePropertys?.ContainsKey(a.Key) == true).Select(a => a.Value);
|
||||
foreach (var deviceRuntime in deviceRuntimes)
|
||||
{
|
||||
if (deviceRuntime.Driver != null)
|
||||
@@ -437,7 +437,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
}
|
||||
|
||||
//需要重启业务线程
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => newVariableRuntime.VariablePropertys.ContainsKey(a.Key)).Select(a => a.Value);
|
||||
var deviceRuntimes = GlobalData.Devices.Where(a => newVariableRuntime.VariablePropertys?.ContainsKey(a.Key) == true).Select(a => a.Value);
|
||||
foreach (var businessDeviceRuntime in deviceRuntimes)
|
||||
{
|
||||
if (businessDeviceRuntime.Driver != null)
|
||||
|
@@ -443,90 +443,93 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
variableExports.Add(varExport);
|
||||
|
||||
#region 插件sheet
|
||||
|
||||
foreach (var item in variable.VariablePropertys ?? new())
|
||||
if (variable.VariablePropertys != null)
|
||||
{
|
||||
//插件属性
|
||||
//单个设备的行数据
|
||||
Dictionary<string, object> driverInfo = new();
|
||||
var has = deviceDicts.TryGetValue(item.Key, out var businessDevice);
|
||||
if (!has)
|
||||
continue;
|
||||
|
||||
channelDicts.TryGetValue(businessDevice.ChannelId, out var channel);
|
||||
|
||||
//没有包含设备名称,手动插入
|
||||
driverInfo.TryAdd(ExportString.DeviceName, businessDevice.Name);
|
||||
driverInfo.TryAdd(ExportString.VariableName, variable.Name);
|
||||
|
||||
var propDict = item.Value;
|
||||
|
||||
if (propertysDict.TryGetValue(channel.PluginName, out var propertys))
|
||||
foreach (var item in variable.VariablePropertys)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
//插件属性
|
||||
//单个设备的行数据
|
||||
Dictionary<string, object> driverInfo = new();
|
||||
var has = deviceDicts.TryGetValue(item.Key, out var businessDevice);
|
||||
if (!has)
|
||||
continue;
|
||||
|
||||
channelDicts.TryGetValue(businessDevice.ChannelId, out var channel);
|
||||
|
||||
//没有包含设备名称,手动插入
|
||||
driverInfo.TryAdd(ExportString.DeviceName, businessDevice.Name);
|
||||
driverInfo.TryAdd(ExportString.VariableName, variable.Name);
|
||||
|
||||
var propDict = item.Value;
|
||||
|
||||
if (propertysDict.TryGetValue(channel.PluginName, out var propertys))
|
||||
{
|
||||
|
||||
var variableProperty = ((BusinessBase)_pluginService.GetDriver(channel.PluginName))?.VariablePropertys;
|
||||
propertys.Item1 = variableProperty;
|
||||
var variablePropertyType = variableProperty.GetType();
|
||||
propertys.Item2 = variablePropertyType.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<DynamicPropertyAttribute>() != null)
|
||||
.ToDictionary(a => variablePropertyType.GetPropertyDisplayName(a.Name, a => a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description));
|
||||
propertysDict.TryAdd(channel.PluginName, propertys);
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
if (propertys.Item2?.Count == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//根据插件的配置属性项生成列,从数据库中获取值或者获取属性默认值
|
||||
foreach (var item1 in propertys.Item2)
|
||||
{
|
||||
if (propDict.TryGetValue(item1.Value.Name, out var dependencyProperty))
|
||||
{
|
||||
driverInfo.TryAdd(item1.Key, dependencyProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
//添加对应属性数据
|
||||
driverInfo.TryAdd(item1.Key, ThingsGatewayStringConverter.Default.Serialize(null, item1.Value.GetValue(propertys.Item1)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!driverPluginDicts.ContainsKey(channel.PluginName))
|
||||
continue;
|
||||
|
||||
var pluginName = PluginServiceUtil.GetFileNameAndTypeName(channel.PluginName);
|
||||
//lock (devicePropertys)
|
||||
{
|
||||
if (devicePropertys.ContainsKey(pluginName.Item2))
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys[pluginName.Item2].Add(driverInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (devicePropertys)
|
||||
try
|
||||
{
|
||||
if (devicePropertys.ContainsKey(pluginName.Item2))
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys[pluginName.Item2].Add(driverInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys.TryAdd(pluginName.Item2, new() { driverInfo });
|
||||
}
|
||||
|
||||
var variableProperty = ((BusinessBase)_pluginService.GetDriver(channel.PluginName))?.VariablePropertys;
|
||||
propertys.Item1 = variableProperty;
|
||||
var variablePropertyType = variableProperty.GetType();
|
||||
propertys.Item2 = variablePropertyType.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<DynamicPropertyAttribute>() != null)
|
||||
.ToDictionary(a => variablePropertyType.GetPropertyDisplayName(a.Name, a => a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description));
|
||||
propertysDict.TryAdd(channel.PluginName, propertys);
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
if (propertys.Item2?.Count == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//根据插件的配置属性项生成列,从数据库中获取值或者获取属性默认值
|
||||
foreach (var item1 in propertys.Item2)
|
||||
{
|
||||
if (propDict.TryGetValue(item1.Value.Name, out var dependencyProperty))
|
||||
{
|
||||
driverInfo.TryAdd(item1.Key, dependencyProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
//添加对应属性数据
|
||||
driverInfo.TryAdd(item1.Key, ThingsGatewayStringConverter.Default.Serialize(null, item1.Value.GetValue(propertys.Item1)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!driverPluginDicts.ContainsKey(channel.PluginName))
|
||||
continue;
|
||||
|
||||
var pluginName = PluginServiceUtil.GetFileNameAndTypeName(channel.PluginName);
|
||||
//lock (devicePropertys)
|
||||
{
|
||||
if (devicePropertys.ContainsKey(pluginName.Item2))
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys[pluginName.Item2].Add(driverInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (devicePropertys)
|
||||
{
|
||||
if (devicePropertys.ContainsKey(pluginName.Item2))
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys[pluginName.Item2].Add(driverInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (driverInfo.Count > 0)
|
||||
devicePropertys.TryAdd(pluginName.Item2, new() { driverInfo });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -190,7 +190,7 @@ public partial class VariableEditComponent
|
||||
VariablePropertyRenderFragments.AddOrUpdate(id, component.Render());
|
||||
}
|
||||
|
||||
if (Model.VariablePropertys.TryGetValue(id, out var dict))
|
||||
if (Model.VariablePropertys?.TryGetValue(id, out var dict) == true)
|
||||
{
|
||||
PluginServiceUtil.SetModel(data.Model, dict);
|
||||
}
|
||||
|
@@ -60,7 +60,7 @@ public class Dlt645_2007Master : CollectBase
|
||||
_plc.CheckClearTime = _driverPropertys.CheckClearTime;
|
||||
_plc.Station = _driverPropertys.Station;
|
||||
_plc.Heartbeat = _driverPropertys.Heartbeat;
|
||||
_plc.InitChannel(channel,LogMessage);
|
||||
_plc.InitChannel(channel, LogMessage);
|
||||
|
||||
base.InitChannel(channel);
|
||||
}
|
||||
|
@@ -128,7 +128,7 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
|
||||
//保留消息
|
||||
//分解List,避免超出字节大小限制
|
||||
var varData = VariableRuntimes.Select(a => a.Value).Adapt<List<VariableData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices.Select(a => a.Value).Adapt<List<DeviceData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
foreach (var item in varData)
|
||||
{
|
||||
@@ -136,12 +136,14 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
|
||||
break;
|
||||
await UpdateVarModel(item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
foreach (var item in devData)
|
||||
if (devData != null)
|
||||
{
|
||||
if (!success)
|
||||
break;
|
||||
await UpdateDevModel(item, cancellationToken).ConfigureAwait(false);
|
||||
foreach (var item in devData)
|
||||
{
|
||||
if (!success)
|
||||
break;
|
||||
await UpdateDevModel(item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
foreach (var item in alramData)
|
||||
{
|
||||
|
@@ -60,7 +60,7 @@ public class ModbusMaster : CollectBase
|
||||
_plc.CheckClearTime = _driverPropertys.CheckClearTime;
|
||||
_plc.ModbusType = _driverPropertys.ModbusType;
|
||||
_plc.Heartbeat = _driverPropertys.Heartbeat;
|
||||
_plc.InitChannel(channel,LogMessage);
|
||||
_plc.InitChannel(channel, LogMessage);
|
||||
base.InitChannel(channel);
|
||||
}
|
||||
|
||||
|
@@ -85,7 +85,7 @@ public class ModbusSlave : BusinessBase
|
||||
_plc.DtuId = _driverPropertys.DtuId;
|
||||
_plc.HeartbeatTime = _driverPropertys.HeartbeatTime;
|
||||
_plc.Heartbeat = _driverPropertys.Heartbeat;
|
||||
_plc.InitChannel(channel,LogMessage);
|
||||
_plc.InitChannel(channel, LogMessage);
|
||||
base.InitChannel(channel);
|
||||
|
||||
_plc.WriteData -= OnWriteData;
|
||||
@@ -147,10 +147,13 @@ public class ModbusSlave : BusinessBase
|
||||
CurrentDevice.SetDeviceStatus(TimerX.Now, true);
|
||||
try
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
await FoundationDevice.Channel.CloseAsync().ConfigureAwait(false);
|
||||
await FoundationDevice.Channel.ConnectAsync(3000, cancellationToken).ConfigureAwait(false);
|
||||
success = true;
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (success)
|
||||
|
@@ -199,7 +199,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
//保留消息
|
||||
//分解List,避免超出mqtt字节大小限制
|
||||
var varData = VariableRuntimes.Select(a => a.Value).Adapt<List<VariableData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices.Select(a => a.Value).Adapt<List<DeviceBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
foreach (var item in varData)
|
||||
{
|
||||
@@ -207,12 +207,14 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
break;
|
||||
await UpdateVarModel(item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
foreach (var item in devData)
|
||||
if (devData != null)
|
||||
{
|
||||
if (!success)
|
||||
break;
|
||||
await UpdateDevModel(item, cancellationToken).ConfigureAwait(false);
|
||||
foreach (var item in devData)
|
||||
{
|
||||
if (!success)
|
||||
break;
|
||||
await UpdateDevModel(item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in alramData)
|
||||
|
@@ -66,7 +66,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async void Dispose(bool disposing)
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (_mqttServer != null)
|
||||
@@ -79,7 +79,6 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
}
|
||||
if (_webHost != null)
|
||||
{
|
||||
await _webHost.StopAsync().ConfigureAwait(false);
|
||||
_webHost.SafeDispose();
|
||||
}
|
||||
|
||||
|
@@ -173,7 +173,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
//首次连接时的保留消息
|
||||
//分解List,避免超出mqtt字节大小限制
|
||||
var varData = VariableRuntimes.Select(a => a.Value).Adapt<List<VariableData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices.Select(a => a.Value).Adapt<List<DeviceData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
List<MqttApplicationMessage> Messages = new();
|
||||
|
||||
@@ -192,14 +192,17 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
}
|
||||
if (!_businessPropertyWithCacheIntervalScript.DeviceTopic.IsNullOrEmpty())
|
||||
{
|
||||
foreach (var item in devData)
|
||||
if (devData != null)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetDeviceData(item);
|
||||
foreach (var topicJson in topicJsonList)
|
||||
foreach (var item in devData)
|
||||
{
|
||||
Messages.Add(new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topicJson.Topic)
|
||||
.WithPayload(topicJson.Json).Build());
|
||||
List<TopicJson> topicJsonList = GetDeviceData(item);
|
||||
foreach (var topicJson in topicJsonList)
|
||||
{
|
||||
Messages.Add(new MqttApplicationMessageBuilder()
|
||||
.WithTopic(topicJson.Topic)
|
||||
.WithPayload(topicJson.Json).Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -131,7 +131,7 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari
|
||||
//保留消息
|
||||
//分解List,避免超出字节大小限制
|
||||
var varData = VariableRuntimes.Select(a => a.Value).Adapt<List<VariableData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices.Select(a => a.Value).Adapt<List<DeviceData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
foreach (var item in varData)
|
||||
{
|
||||
@@ -139,12 +139,14 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari
|
||||
break;
|
||||
await UpdateVarModel(item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
foreach (var item in devData)
|
||||
if (devData != null)
|
||||
{
|
||||
if (!success)
|
||||
break;
|
||||
await UpdateDevModel(item, cancellationToken).ConfigureAwait(false);
|
||||
foreach (var item in devData)
|
||||
{
|
||||
if (!success)
|
||||
break;
|
||||
await UpdateDevModel(item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in alramData)
|
||||
|
@@ -64,7 +64,7 @@ public class SiemensS7Master : CollectBase
|
||||
_plc.LocalTSAP = _driverPropertys.LocalTSAP;
|
||||
_plc.Rack = _driverPropertys.Rack;
|
||||
_plc.Slot = _driverPropertys.Slot;
|
||||
_plc.InitChannel(channel,LogMessage);
|
||||
_plc.InitChannel(channel, LogMessage);
|
||||
base.InitChannel(channel);
|
||||
}
|
||||
|
||||
|
@@ -121,7 +121,7 @@ internal sealed class Program
|
||||
return false;
|
||||
};
|
||||
hybridApp.Run();
|
||||
Thread.Sleep(5000);
|
||||
Thread.Sleep(3000);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,9 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<title>ThingsGateway.Debug</title>
|
||||
<title>ThingsGateway.Hybrid</title>
|
||||
<base href="/" />
|
||||
|
||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||
@@ -15,7 +16,6 @@
|
||||
<script src="/_content/ThingsGateway.Razor/js/theme.js" type="module"></script><!-- 初始主题 -->
|
||||
<script src="/_content/ThingsGateway.Razor/js/culture.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@@ -22,89 +22,86 @@
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
|
||||
@inject NavigationManager NavigationManager
|
||||
@if (AppContext.CurrentUser != null && AppContext.OwnMenus != null)
|
||||
{
|
||||
|
||||
<LoginConnectionHub />
|
||||
<CascadingValue Value="ReloadMenu" Name="ReloadMenu" IsFixed="true">
|
||||
<CascadingValue Value="ReloadUser" Name="ReloadUser" IsFixed="true">
|
||||
<LoginConnectionHub />
|
||||
<CascadingValue Value="ReloadMenu" Name="ReloadMenu" IsFixed="true">
|
||||
<CascadingValue Value="ReloadUser" Name="ReloadUser" IsFixed="true">
|
||||
|
||||
<div class="mainlayout">
|
||||
<div class="mainlayout">
|
||||
|
||||
<Layout SideWidth="0" IsPage="true" IsFullSide="true" IsFixedHeader="true"
|
||||
ShowGotoTop="true" ShowCollapseBar="true" Menus="@MenuService.MenuItems"
|
||||
AdditionalAssemblies=App.RazorAssemblies AllowDragTab=true
|
||||
UseTabSet="false" TabDefaultUrl="/">
|
||||
<Header>
|
||||
<div class="ms-4"></div>
|
||||
<ChoiceModuleComponent Value=@(AppContext.CurrentModuleId) ModuleList=@(AppContext.CurrentUser.ModuleList) OnClick=@(ChoiceModule) />
|
||||
<Layout SideWidth="0" IsPage="true" IsFullSide="true" IsFixedHeader="true"
|
||||
ShowGotoTop="true" ShowCollapseBar="true" Menus="@MenuService.MenuItems"
|
||||
AdditionalAssemblies=App.RazorAssemblies AllowDragTab=true
|
||||
UseTabSet="false" TabDefaultUrl="/">
|
||||
<Header>
|
||||
<div class="ms-4"></div>
|
||||
<ChoiceModuleComponent Value=@(AppContext.CurrentModuleId) ModuleList=@(AppContext.CurrentUser.ModuleList) OnClick=@(ChoiceModule) />
|
||||
|
||||
<div class="flex-fill"></div>
|
||||
<div class="flex-fill"></div>
|
||||
|
||||
@* 搜索框 *@
|
||||
<GlobalSearch Menus=@(MenuService.SameLevelMenuItems) />
|
||||
@* 搜索框 *@
|
||||
<GlobalSearch Menus=@(MenuService.SameLevelMenuItems) />
|
||||
|
||||
@* 语言选择 *@
|
||||
<div class="d-none d-xl-flex ">
|
||||
<CultureChooser />
|
||||
@* 语言选择 *@
|
||||
<div class="d-none d-xl-flex ">
|
||||
<CultureChooser />
|
||||
</div>
|
||||
|
||||
<Logout ImageUrl="@(AppContext.CurrentUser.Avatar??$"{WebsiteConst.DefaultResourceUrl}images/defaultUser.svg")" ShowUserName=false DisplayName="@UserManager.UserAccount" UserName="@UserManager.VerificatId.ToString()" PrefixUserNameText=@AdminLocalizer["CurrentVerificat"]>
|
||||
<LinkTemplate>
|
||||
<a href=@("/") class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["系统首页"]</a>
|
||||
|
||||
<a @onclick="@OnUserInfoDialog" class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["UserCenter"]</a>
|
||||
<a @onclick="@LogoutAsync" class="h6"><i class="fa-solid fa-key me-2"></i>@Localizer["Logout"]</a>
|
||||
|
||||
|
||||
</LinkTemplate>
|
||||
</Logout>
|
||||
|
||||
@* 全屏按钮 *@
|
||||
<FullScreenButton class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-arrows-alt"
|
||||
TooltipPlacement=Placement.Bottom TooltipText="@Localizer[nameof(FullScreenButton)]" />
|
||||
|
||||
@if (WebsiteOption.Value.IsShowAbout)
|
||||
{
|
||||
<Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
|
||||
}
|
||||
@* 版本号 *@
|
||||
<div class="px-1 navbar-header-text d-none d-lg-block">@_versionString</div>
|
||||
|
||||
@* 主题切换 *@
|
||||
@* <ThemeToggle /> *@
|
||||
<ThemeProvider class="layout-header-bar d-none d-lg-flex px-0"></ThemeProvider>
|
||||
|
||||
</Header>
|
||||
<Side>
|
||||
<div class="layout-banner">
|
||||
<span class="avatar">
|
||||
@WebsiteOption.Value.Title?.GetNameLen2()
|
||||
</span>
|
||||
|
||||
<div class="layout-title d-flex align-items-center justify-content-center">
|
||||
<span>@WebsiteOption.Value.Title</span>
|
||||
</div>
|
||||
|
||||
<Logout ImageUrl="@(AppContext.CurrentUser.Avatar??$"{WebsiteConst.DefaultResourceUrl}images/defaultUser.svg")" ShowUserName=false DisplayName="@UserManager.UserAccount" UserName="@UserManager.VerificatId.ToString()" PrefixUserNameText=@AdminLocalizer["CurrentVerificat"]>
|
||||
<LinkTemplate>
|
||||
<a href=@("/") class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["系统首页"]</a>
|
||||
|
||||
<a @onclick="@OnUserInfoDialog" class="h6"><i class="fa-solid fa-suitcase me-2"></i>@Localizer["UserCenter"]</a>
|
||||
<a @onclick="@LogoutAsync" class="h6"><i class="fa-solid fa-key me-2"></i>@Localizer["Logout"]</a>
|
||||
|
||||
|
||||
</LinkTemplate>
|
||||
</Logout>
|
||||
|
||||
@* 全屏按钮 *@
|
||||
<FullScreenButton class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-arrows-alt"
|
||||
TooltipPlacement=Placement.Bottom TooltipText="@Localizer[nameof(FullScreenButton)]" />
|
||||
|
||||
@if (WebsiteOption.Value.IsShowAbout)
|
||||
</div>
|
||||
</Side>
|
||||
<Main>
|
||||
<Tab @ref=Tab ClickTabToNavigation="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
|
||||
AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
|
||||
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
|
||||
{
|
||||
<Button @onclick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
|
||||
}
|
||||
@* 版本号 *@
|
||||
<div class="px-1 navbar-header-text d-none d-lg-block">@_versionString</div>
|
||||
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));
|
||||
})>
|
||||
</Tab>
|
||||
|
||||
@* 主题切换 *@
|
||||
@* <ThemeToggle /> *@
|
||||
<ThemeProvider class="layout-header-bar d-none d-lg-flex px-0"></ThemeProvider>
|
||||
</Main>
|
||||
<NotAuthorized>
|
||||
<Redirect Url="/Account/Login" />
|
||||
</NotAuthorized>
|
||||
</Layout>
|
||||
|
||||
</Header>
|
||||
<Side>
|
||||
<div class="layout-banner">
|
||||
<span class="avatar">
|
||||
@WebsiteOption.Value.Title?.GetNameLen2()
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="layout-title d-flex align-items-center justify-content-center">
|
||||
<span>@WebsiteOption.Value.Title</span>
|
||||
</div>
|
||||
</div>
|
||||
</Side>
|
||||
<Main>
|
||||
<Tab @ref=Tab ClickTabToNavigation="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
|
||||
AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
|
||||
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
|
||||
{
|
||||
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));
|
||||
})>
|
||||
</Tab>
|
||||
|
||||
</Main>
|
||||
<NotAuthorized>
|
||||
<Redirect Url="/Account/Login" />
|
||||
</NotAuthorized>
|
||||
</Layout>
|
||||
|
||||
</div>
|
||||
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
|
||||
}
|
||||
|
@@ -166,7 +166,6 @@ public partial class MainLayout : IDisposable
|
||||
DispatchService.Subscribe(Dispatch);
|
||||
await AppContext.InitUserAsync();
|
||||
await AppContext.InitMenus(NavigationManager.ToBaseRelativePath(NavigationManager.Uri));
|
||||
StateHasChanged();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
private Tab Tab { get; set; }
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>10.0.0.18</Version>
|
||||
<Version>10.0.0.19</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Reference in New Issue
Block a user