网关监控树节点添加右侧常用操作按钮

This commit is contained in:
Diego
2025-07-04 12:50:07 +08:00
parent e270b0c4f6
commit a825ca5f6f
6 changed files with 146 additions and 49 deletions

View File

@@ -107,7 +107,7 @@
"DeleteCurrentDevice": "Delete Current Device",
"DeleteDevice": "Delete Device",
"DeviceInformation": "Device Information",
"DeviceList": "Device List",
"DeviceList": "Tip: Right click to operate tree nodes",
"DeviceRedundantThread": "Switching redundancy",
"ExcelChannel": "ExcelChannel",
"ExcelDevice": "ExcelDevice",

View File

@@ -105,7 +105,7 @@
"DeleteCurrentDevice": "删除当前节点下的设备",
"DeleteDevice": "删除设备",
"DeviceInformation": "设备信息",
"DeviceList": "设备节点",
"DeviceList": "提示:右键操作树节点",
"DeviceRedundantThread": "切换冗余",
"ExcelChannel": "在线excel编辑通道",
"ExcelDevice": "在线excel编辑设备",

View File

@@ -13,10 +13,66 @@
<SpinnerComponent @ref=Spinner></SpinnerComponent>
</div>
<span style="color:var(--bs-body-color)" class="text-h6 mb-2">@GatewayLocalizer["DeviceList"]</span>
<span style="color:var(--bs-body-color);opacity: 0.5;" class="text-h6 mb-2">@GatewayLocalizer["DeviceList"]</span>
<ContextMenuZone title="Right click operation">
<TreeView TItem="ChannelDeviceTreeItem" Items="Items" ShowIcon="false" ShowSearch IsAccordion=false IsVirtualize="true" OnTreeItemClick="OnTreeItemClick" OnSearchAsync="OnClickSearch" ModelEqualityComparer=ModelEqualityComparer>
<ContextMenuZone>
<TreeView TItem="ChannelDeviceTreeItem" Items="Items" ShowIcon="false" ShowSearch IsAccordion=false IsVirtualize="true" OnTreeItemClick="OnTreeItemClick" OnSearchAsync="OnClickSearch" ModelEqualityComparer=ModelEqualityComparer ShowToolbar="true">
<ToolbarTemplate>
<div class="tree-node-toolbar-edit" @onclick:preventDefault @onclick:stopPropagation>
@if (context.ChannelDevicePluginType < ChannelDevicePluginTypeEnum.Channel)
{
@* <Tooltip Title=@(GatewayLocalizer["AddChannel"]) Placement="Placement.Bottom"> *@
<Button Size=Size.ExtraSmall Icon="fa-solid fa-plus" Color=Color.None OnClickWithoutRender=@(() => EditChannel(GatewayLocalizer["AddChannel"], context, ItemChangedType.Add))>
</Button>
@* </Tooltip> *@
@* <Tooltip Title=@(GatewayLocalizer["DeleteChannel"]) Placement="Placement.Bottom"> *@
<Button Size=Size.ExtraSmall Icon="fa-solid fa-xmark" Color=Color.None OnClickWithoutRender=@(() => DeleteCurrentChannel(context))>
</Button>
@* </Tooltip> *@
}
@* //更新通道 *@
@if (context.ChannelDevicePluginType == ChannelDevicePluginTypeEnum.Channel)
{
@* <Tooltip Title=@(GatewayLocalizer["AddDevice"]) Placement="Placement.Bottom"> *@
<Button Size=Size.ExtraSmall Icon="fa-solid fa-plus" Color=Color.None OnClickWithoutRender=@(() => EditDevice(GatewayLocalizer["AddDevice"], context, ItemChangedType.Add))>
</Button>
@* </Tooltip> *@
@* <Tooltip Title=@(GatewayLocalizer["UpdateChannel"]) Placement="Placement.Bottom"> *@
<Button Size=Size.ExtraSmall Icon="fa-regular fa-pen-to-square" Color=Color.None OnClickWithoutRender=@(() => EditChannel(GatewayLocalizer["UpdateChannel"], context, ItemChangedType.Update))>
</Button>
@* </Tooltip> *@
@* <Tooltip Title=@(GatewayLocalizer["DeleteChannel"]) Placement="Placement.Bottom"> *@
<Button Size=Size.ExtraSmall Icon="fa-solid fa-xmark" Color=Color.None OnClickWithoutRender=@(() => DeleteCurrentChannel(context))>
</Button>
@* </Tooltip> *@
}
@* //更新设备 *@
@if (context.ChannelDevicePluginType == ChannelDevicePluginTypeEnum.Device)
{
@* <Tooltip Title=@(GatewayLocalizer["UpdateDevice"]) Placement="Placement.Bottom"> *@
<Button Size=Size.ExtraSmall Icon="fa-regular fa-pen-to-square" Color=Color.None OnClickWithoutRender=@(() => EditDevice(GatewayLocalizer["UpdateDevice"], context, ItemChangedType.Update))>
</Button>
@* </Tooltip> *@
@* <Tooltip Title=@(GatewayLocalizer["DeleteDevice"]) Placement="Placement.Bottom"> *@
<Button Size=Size.ExtraSmall Icon="fa-solid fa-xmark" Color=Color.None OnClickWithoutRender=@(() => DeleteCurrentDevice(context))>
</Button>
@* </Tooltip> *@
}
</div>
</ToolbarTemplate>
</TreeView>

View File

@@ -101,19 +101,22 @@ public partial class ChannelDeviceTree
#region
async Task EditChannel(ContextMenuItem item, object value, ItemChangedType itemChangedType)
Task EditChannel(ContextMenuItem item, object value, ItemChangedType itemChangedType)
{
return EditChannel(item.Text, value as ChannelDeviceTreeItem, itemChangedType);
}
async Task EditChannel(string text, ChannelDeviceTreeItem channelDeviceTreeItem, ItemChangedType itemChangedType)
{
var op = new DialogOption()
{
IsScrolling = false,
ShowMaximizeButton = true,
Size = Size.ExtraLarge,
Title = item.Text,
Title = text,
ShowFooter = false,
ShowCloseButton = false,
};
if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
PluginTypeEnum? pluginTypeEnum = ChannelDeviceHelpers.GetPluginType(channelDeviceTreeItem);
var oneModel = ChannelDeviceHelpers.GetChannelModel(itemChangedType, channelDeviceTreeItem);
@@ -134,12 +137,16 @@ public partial class ChannelDeviceTree
}
async Task CopyChannel(ContextMenuItem item, object value)
Task CopyChannel(ContextMenuItem item, object value)
{
return CopyChannel(item.Text, value as ChannelDeviceTreeItem);
}
async Task CopyChannel(string text, ChannelDeviceTreeItem channelDeviceTreeItem)
{
Channel oneModel = null;
Dictionary<Device, List<Variable>> deviceDict = new();
if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
{
@@ -159,7 +166,7 @@ public partial class ChannelDeviceTree
IsScrolling = false,
ShowMaximizeButton = true,
Size = Size.ExtraLarge,
Title = item.Text,
Title = text,
ShowFooter = false,
ShowCloseButton = false,
};
@@ -182,22 +189,21 @@ public partial class ChannelDeviceTree
}
async Task BatchEditChannel(ContextMenuItem item, object value)
Task BatchEditChannel(ContextMenuItem item, object value)
{
return BatchEditChannel(item.Text, value as ChannelDeviceTreeItem);
}
async Task BatchEditChannel(string text, ChannelDeviceTreeItem channelDeviceTreeItem)
{
Channel oldModel = null;
Channel oneModel = null;
IEnumerable<Channel>? changedModels = null;
IEnumerable<Channel>? models = null;
if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
{
await EditChannel(item, value, ItemChangedType.Update);
await EditChannel(text, channelDeviceTreeItem, ItemChangedType.Update);
return;
}
//批量编辑只有分类和插件名称节点
@@ -235,7 +241,7 @@ public partial class ChannelDeviceTree
IsScrolling = false,
ShowMaximizeButton = true,
Size = Size.ExtraLarge,
Title = item.Text,
Title = text,
ShowFooter = false,
ShowCloseButton = false,
};
@@ -265,8 +271,11 @@ public partial class ChannelDeviceTree
}
async Task ExcelChannel(ContextMenuItem item, object value)
Task ExcelChannel(ContextMenuItem item, object value)
{
return ExcelChannel(item.Text);
}
async Task ExcelChannel(string text)
{
var op = new DialogOption()
@@ -274,7 +283,7 @@ public partial class ChannelDeviceTree
IsScrolling = false,
ShowMaximizeButton = true,
Size = Size.ExtraExtraLarge,
Title = item.Text,
Title = text,
ShowFooter = false,
ShowCloseButton = false,
};
@@ -316,8 +325,11 @@ finally
}
async Task DeleteChannel(ContextMenuItem item, object value)
Task DeleteChannel(ContextMenuItem item, object value)
{
return DeleteChannel(value as ChannelDeviceTreeItem);
}
async Task DeleteChannel(ChannelDeviceTreeItem channelDeviceTreeItem)
{
var op = new DialogOption();
{
@@ -336,7 +348,7 @@ finally
EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
{
await op.CloseDialogAsync();
await DeleteCurrentChannel(item, value);
await DeleteCurrentChannel(channelDeviceTreeItem);
}));
builder.AddComponentParameter(5, nameof(Button.Text), GatewayLocalizer["DeleteCurrentChannel"].Value);
@@ -348,7 +360,7 @@ finally
builder.AddComponentParameter(15, nameof(Button.OnClick), EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
{
await op.CloseDialogAsync();
await DeleteAllChannel(item, value);
await DeleteAllChannel();
}));
builder.AddComponentParameter(16, nameof(Button.Text), GatewayLocalizer["DeleteAllChannel"].Value);
@@ -361,10 +373,9 @@ finally
;
await DialogService.Show(op);
}
async Task DeleteCurrentChannel(ContextMenuItem item, object value)
async Task DeleteCurrentChannel(ChannelDeviceTreeItem channelDeviceTreeItem)
{
IEnumerable<ChannelRuntime> modelIds = null;
if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
{
@@ -395,6 +406,7 @@ finally
{
var op = new SwalOption()
{
Title = GatewayLocalizer["DeleteConfirmTitle"],
BodyTemplate = (__builder) =>
{
@@ -434,7 +446,7 @@ finally
}
}
async Task DeleteAllChannel(ContextMenuItem item, object value)
async Task DeleteAllChannel()
{
try
{
@@ -479,7 +491,11 @@ finally
}
async Task ExportChannel(ContextMenuItem item, object value)
Task ExportChannel(ContextMenuItem item, object value)
{
return ExportChannel(value as ChannelDeviceTreeItem);
}
async Task ExportChannel(ChannelDeviceTreeItem channelDeviceTreeItem)
{
var op = new DialogOption();
{
@@ -498,7 +514,7 @@ finally
EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
{
await op.CloseDialogAsync();
await ExportCurrentChannel(item, value);
await ExportCurrentChannel(channelDeviceTreeItem);
}));
builder.AddComponentParameter(5, nameof(Button.Text), GatewayLocalizer["ExportCurrentChannel"].Value);
@@ -510,7 +526,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
builder.AddComponentParameter(15, nameof(Button.OnClick), EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
{
await op.CloseDialogAsync();
await ExportAllChannel(item, value);
await ExportAllChannel();
}));
builder.AddComponentParameter(16, nameof(Button.Text), GatewayLocalizer["ExportAllChannel"].Value);
@@ -523,10 +539,9 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
;
await DialogService.Show(op);
}
async Task ExportCurrentChannel(ContextMenuItem item, object value)
async Task ExportCurrentChannel(ChannelDeviceTreeItem channelDeviceTreeItem)
{
bool ret;
if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
{
@@ -550,7 +565,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
if (ret)
await ToastService.Default();
}
async Task ExportAllChannel(ContextMenuItem item, object value)
async Task ExportAllChannel()
{
bool ret;
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
@@ -561,14 +576,18 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
}
async Task ImportChannel(ContextMenuItem item, object value)
Task ImportChannel(ContextMenuItem item, object value)
{
return ImportChannel(item.Text);
}
async Task ImportChannel(string text)
{
var op = new DialogOption()
{
IsScrolling = true,
ShowMaximizeButton = true,
Size = Size.ExtraLarge,
Title = item.Text,
Title = text,
ShowFooter = false,
ShowCloseButton = false,
OnCloseAsync = async () =>
@@ -654,19 +673,22 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
}
async Task EditDevice(ContextMenuItem item, object value, ItemChangedType itemChangedType)
Task EditDevice(ContextMenuItem item, object value, ItemChangedType itemChangedType)
{
return EditDevice(item.Text, value as ChannelDeviceTreeItem, itemChangedType);
}
async Task EditDevice(string text, ChannelDeviceTreeItem channelDeviceTreeItem, ItemChangedType itemChangedType)
{
var op = new DialogOption()
{
IsScrolling = true,
ShowMaximizeButton = true,
Size = Size.ExtraLarge,
Title = item.Text,
Title = text,
ShowFooter = false,
ShowCloseButton = false,
};
Device oneModel = null;
if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
if (channelDeviceTreeItem.TryGetDeviceRuntime(out var deviceRuntime))
{
@@ -845,7 +867,12 @@ finally
}
async Task DeleteDevice(ContextMenuItem item, object value)
Task DeleteDevice(ContextMenuItem item, object value)
{
return DeleteDevice(value as ChannelDeviceTreeItem);
}
async Task DeleteDevice(ChannelDeviceTreeItem channelDeviceTreeItem)
{
var op = new DialogOption();
{
@@ -864,7 +891,7 @@ finally
EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
{
await op.CloseDialogAsync();
await DeleteCurrentDevice(item, value);
await DeleteCurrentDevice(channelDeviceTreeItem);
}));
builder.AddComponentParameter(5, nameof(Button.Text), GatewayLocalizer["DeleteCurrentDevice"].Value);
@@ -876,7 +903,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
builder.AddComponentParameter(15, nameof(Button.OnClick), EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
{
await op.CloseDialogAsync();
await DeleteAllDevice(item, value);
await DeleteAllDevice();
}));
builder.AddComponentParameter(16, nameof(Button.Text), GatewayLocalizer["DeleteAllDevice"].Value);
@@ -886,15 +913,14 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
});
}
;
;
await DialogService.Show(op);
}
async Task DeleteCurrentDevice(ContextMenuItem item, object value)
async Task DeleteCurrentDevice(ChannelDeviceTreeItem channelDeviceTreeItem)
{
IEnumerable<DeviceRuntime> modelIds = null;
if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
if (channelDeviceTreeItem.TryGetDeviceRuntime(out var deviceRuntime))
{
@@ -968,7 +994,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
}
async Task DeleteAllDevice(ContextMenuItem item, object value)
async Task DeleteAllDevice()
{
try
{
@@ -1511,4 +1537,8 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
base.OnAfterRender(firstRender);
}
private void OnUpdateCallbackAsync()
{
throw new NotImplementedException();
}
}

View File

@@ -2,15 +2,26 @@
.listtree-view {
height: 100%;
}
.listtree-view ::deep .bb-cm-zone {
height: calc(100% - 60px);
height: calc(100% - 60px);
/*margin-bottom: 2px;*/
}
.listtree-view ::deep .tree-view {
--bb-tree-search-height: 32px;
min-height: 300px;
height: 100%;
}
.listtree-view ::deep .tree-view .tree-root {
padding-right: 0px;
}
.deviceEnable--text {
color: var(--bs-body-color);
}
.tree-node-toolbar-edit {
background-color: white;
}

View File

@@ -7,7 +7,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj">
</ProjectReference>
<PackageReference Include="Confluent.Kafka" Version="2.10.1" GeneratePathProperty="true">
<PackageReference Include="Confluent.Kafka" Version="2.11.0" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
</ItemGroup>