diff --git a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj index 2e72107f001..0c1ae893386 100644 --- a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj +++ b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj @@ -77,6 +77,7 @@ + diff --git a/src/BootstrapBlazor.Server/Components/Samples/Terms.razor b/src/BootstrapBlazor.Server/Components/Samples/Terms.razor new file mode 100644 index 00000000000..b8b952775c8 --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/Terms.razor @@ -0,0 +1,32 @@ +@page "/term" +@using Microsoft.AspNetCore.Components.Sections +@inject IStringLocalizer Localizer + +

@Localizer["Title"]

+

@Localizer["SubTitle"]

+ + +

@((MarkupString)Localizer["Tips"].Value)

+
+ + +
+
@((MarkupString)Localizer["TermDesc"].Value)
+
+ +
+
+ + + +
+
+
+ + + + + + + + diff --git a/src/BootstrapBlazor.Server/Components/Samples/Terms.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Terms.razor.cs new file mode 100644 index 00000000000..2f26c0971fb --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/Terms.razor.cs @@ -0,0 +1,89 @@ +using System.Text; + +namespace BootstrapBlazor.Server.Components.Samples; + +/// +/// Term 示例 +/// +public partial class Terms : IDisposable +{ + private Term _term = default!; + private Term _termStream = default!; + private MemoryStream _ms = new MemoryStream(); + private CancellationTokenSource? _cancellationTokenSource; + + private async Task OnReceivedAsync(byte[] data) + { + var str = System.Text.Encoding.UTF8.GetString(data); + await _term.Write(str.Replace("\r", "\r\n")); + } + + private async Task OnCheck() + { + await _term.WriteLine($"Check\r\n{DateTime.Now}"); + } + + private async Task OnTest() + { + await _term.WriteLine($"Test\r\n{DateTime.Now}"); + } + + private async Task OnClean() + { + await _term.Clear(); + } + + /// + /// + /// + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await _termStream.Open(_ms); + + _ = Task.Run(async () => + { + _cancellationTokenSource ??= new(); + while (_cancellationTokenSource is { IsCancellationRequested: false }) + { + try + { + // 模拟流内数据变化 + _ms.SetLength(0); + await _ms.WriteAsync(Encoding.UTF8.GetBytes($"{DateTime.Now}\r\n"), _cancellationTokenSource.Token); + _ms.Seek(0, SeekOrigin.Begin); + await Task.Delay(2000); + } + catch { } + } + }); + } + } + + private void Dispose(bool disposing) + { + if (disposing) + { + if (_cancellationTokenSource is { IsCancellationRequested: false }) + { + _cancellationTokenSource.Cancel(); + _cancellationTokenSource.Dispose(); + _cancellationTokenSource = null; + } + + _ms.Close(); + _ms.Dispose(); + } + } + + /// + /// + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } +} diff --git a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs index 6cf7e180172..d385e27d484 100644 --- a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs @@ -1406,6 +1406,11 @@ void AddNotice(DemoMenuItem item) Url = "sweet-alert" }, new() + { + Text = Localizer["Terms"], + Url = "term" + }, + new() { Text = Localizer["Timer"], Url = "timer" diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 4191e75d328..175480e25da 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -428,6 +428,7 @@ "Tag": "Tag", "TaskDashBoard": "TaskDashBoard", "TcpSocketFactory": "ITcpSocketFactory", + "Terms": "Terminal", "Textarea": "Textarea", "Theme": "Theme", "ThemeProvider": "IThemeProvider", @@ -5306,6 +5307,19 @@ "TaskBoardNormalTitle": "Basic usage", "TaskBoardTitle": "Task DashBoard" }, + "BootstrapBlazor.Server.Components.Samples.Terms": { + "BasicUsageIntro": "Output text by calling the Write method", + "BasicUsageTitle": "Basic Usage", + "CheckText": "Check", + "CleanText": "Clean", + "StreamUsageIntro": "Connect various streams by calling the Open method, this example simulates Shell and verifies input characters", + "StreamUsageTitle": "Stream Usage", + "SubTitle": "Terminal Component", + "TermDesc": "The Term component is a terminal component that establishes a connection with the stream via C# code. The Term component is then responsible for displaying interactive content. In this example, after establishing a connection with a simulated Stream instance in the backend, data is automatically displayed within the component when it is written. When the user enters a command within the component and presses Enter, the backend processes it and displays the result in the component.", + "TestText": "Test", + "Tips": "This component depends on the BootstrapBlazor.Term extension package. You need to reference it as follows", + "Title": "Terminal" + }, "BootstrapBlazor.Server.Components.Samples.TextAreas": { "TextAreaAutoScroll": "Automatic scrolling", "TextAreaBindWayBindValue": "The binding value", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index bcc9036b00e..972c2fdd5a5 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -428,6 +428,7 @@ "Tag": "标签 Tag", "TaskDashBoard": "任务管理器 TaskDashBoard", "TcpSocketFactory": "套接字服务 ITcpSocketFactory", + "Terms": "终端 Terminal", "Textarea": "多行文本框 Textarea", "Theme": "组件主题", "ThemeProvider": "主题服务 IThemeProvider", @@ -5306,6 +5307,19 @@ "TaskBoardNormalTitle": "基本用法", "TaskBoardTitle": "Task DashBoard 任务管理器" }, + "BootstrapBlazor.Server.Components.Samples.Terms": { + "BasicUsageIntro": "通过调用 Write 方法输出文本", + "BasicUsageTitle": "基础用法", + "CheckText": "查询命令(Check)", + "CleanText": "清空", + "StreamUsageIntro": "通过调用 Open 方法连接各种流,本例模拟 Shell 并回显输入字符", + "StreamUsageTitle": "流式处理", + "SubTitle": "命令行终端显示组件", + "TermDesc": "Term 组件是一个终端组件,通过 C# 代码与流建立联系,然后组件 Term 负责显示交互内容;本例中,与后台一个模拟 Stream 实例建立联系后,当有数据写入时自动显示在组件内,用户在组件内录入命令回车后,由后端处理回显在组件中", + "TestText": "测试命令 (Test)", + "Tips": "本组件依赖 BootstrapBlazor.Term 扩展包,需要依照下面步骤进行引用", + "Title": "Term 命令行" + }, "BootstrapBlazor.Server.Components.Samples.TextAreas": { "TextAreaAutoScroll": "自动滚屏", "TextAreaBindWayBindValue": "绑定值", diff --git a/src/BootstrapBlazor.Server/docs.json b/src/BootstrapBlazor.Server/docs.json index 5db8b80bd08..b13020255f9 100644 --- a/src/BootstrapBlazor.Server/docs.json +++ b/src/BootstrapBlazor.Server/docs.json @@ -260,7 +260,8 @@ "select-city": "SelectCities", "select-province": "SelectProvinces", "hik-vision": "HikVisions", - "embed-pdf": "EmbedPDFs" + "embed-pdf": "EmbedPDFs", + "term": "Terms" }, "video": { "table": "BV1ap4y1x7Qn?p=1",