[MAUI]集成富文本编辑器Editor.js至.NET MAUI Blazor项目

文章目录

    • 获取资源
      • 从源码构建
      • 从CDN获取
      • 获取扩展插件
    • 创建项目
      • 创建控件
      • 创建Blazor组件
      • 初始化
      • 保存
      • 销毁
      • 编写渲染逻辑
    • 实现只读/编辑功能
      • 切换模式
      • 获取只读模式状态
      • 响应切换事件
    • 实现明/暗主题切换
    • 项目地址

Editor.js 是一个基于 Web 的所见即所得富文本编辑器,它由CodeX团队开发。之前写过一篇博文专门介绍过这个编辑器,可以回看: 开源好用的所见即所得(WYSIWYG)编辑器:Editor.js。

.NET MAUI Blazor允许使用 Web UI 生成跨平台本机应用。 组件在 .NET 进程中以本机方式运行,并使用本地互操作通道将 Web UI 呈现到嵌入式 Web 视图控件(BlazorWebView)。

这次我们将Editor.js集成到.NET MAUI应用中。并实现只读切换,明/暗主题切换等功能。

在这里插入图片描述

使用.NET MAUI实现跨平台支持,本项目可运行于Android、iOS平台。

获取资源

我们先要获取web应用的资源文件(js,css等),以便MAUI的视图呈现标准的Web UI。有两种方式可以获取:

  1. 从源码构建
  2. 从CDN获取

从源码构建

此方法需要首先安装nodejs

克隆Editorjs项目到本地

git clone https://github.com/codex-team/editor.js.git

运行

npm i

以及

npm run build

等待nodejs构建完成,在项目根目录找到dist/editorjs.umd.js这个就是我们需要的js文件

在这里插入图片描述

从CDN获取

从官方CDN获取:

https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest

获取扩展插件

Editor.js中的每个块都由插件提供。有简单的外部脚本,有自己的逻辑。默认Editor.js项目中已包含唯一的 Paragraph 块。其它的工具插件可以单独获取。

同样我们可以找到这些插件的源码编译,或通过CDN获取:

  1. Header
  2. 链接
  3. HTML块
  4. 简单图片(无后端要求)
  5. 图片
  6. 清单
  7. 列表
  8. 嵌入
  9. 引用

创建项目

新建.NET MAUI Blazor项目,命名Editorjs

将editorjs.umd.js和各插件js文件拷贝至项目根目录下wwwroot文件夹,文件结构如下:

在这里插入图片描述

在wwwroot创建editorjs_index.html文件,并在body中引入editorjs.umd.js和各插件js文件

<body>...<script src="lib/editorjs/editorjs.umd.js"></script><script src="lib/editorjs/tools/checklist@latest.js"></script><script src="lib/editorjs/tools/code@latest.js"></script><script src="lib/editorjs/tools/delimiter@latest.js"></script><script src="lib/editorjs/tools/embed@latest.js"></script><script src="lib/editorjs/tools/header@latest.js"></script><script src="lib/editorjs/tools/image@latest.js"></script><script src="lib/editorjs/tools/inline-code@latest.js"></script><script src="lib/editorjs/tools/link@latest.js"></script><script src="lib/editorjs/tools/nested-list@latest.js"></script><script src="lib/editorjs/tools/marker@latest.js"></script><script src="lib/editorjs/tools/quote@latest.js"></script><script src="lib/editorjs/tools/table@latest.js"></script>
</body>

创建控件

创建 EditNotePage.xaml ,EditNotePage类作为视图控件,继承于ContentView,EditNotePage.xaml的完整代码如下:

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:mato="clr-namespace:Editorjs;assembly=Editorjs"xmlns:service="clr-namespace:Editorjs.ViewModels;assembly=Editorjs"xmlns:xct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"x:Name="MainPage"x:Class="Editorjs.Controls.EditNotePage"><Grid BackgroundColor="{AppThemeBinding Light={StaticResource LightPageBackgroundColor}, Dark={StaticResource DarkPageBackgroundColor}}"RowDefinitions="Auto, *, Auto"Padding="20, 10, 20, 0"><Grid Grid.Row="0"Margin="0, 0, 0, 10"><Grid.ColumnDefinitions><ColumnDefinition Width="auto"></ColumnDefinition><ColumnDefinition></ColumnDefinition><ColumnDefinition></ColumnDefinition></Grid.ColumnDefinitions><Entry Grid.Column="1"Placeholder="请输入标题"Margin="10, 0, 0, 0"VerticalOptions="Center"Text="{Binding Title}"
></Entry><HorizontalStackLayout Grid.Column="2"HeightRequest="60"VerticalOptions="Center"HorizontalOptions="End"Margin="0, 0, 10, 0"><StackLayout RadioButtonGroup.GroupName="State"RadioButtonGroup.SelectedValue="{Binding NoteSegmentState,Mode=TwoWay}"Orientation="Horizontal"><RadioButton Value="{x:Static service:NoteSegmentState.Edit}"Content="编辑"></RadioButton><RadioButton Value="{x:Static service:NoteSegmentState.PreView}"Content="预览"></RadioButton></StackLayout></HorizontalStackLayout></Grid><BlazorWebView Grid.Row="1"Margin="-10, 0"x:Name="mainMapBlazorWebView"HostPage="wwwroot/editorjs_index.html"><BlazorWebView.RootComponents><RootComponent Selector="#app"x:Name="rootComponent"ComponentType="{x:Type mato:EditorjsPage}" /></BlazorWebView.RootComponents></BlazorWebView><ActivityIndicator Grid.RowSpan="4"IsRunning="{Binding Loading}"></ActivityIndicator></Grid>
</ContentView>

创建一个EditNotePageViewModel的ViewModel类,用于处理页面逻辑。代码如下:

public class EditNotePageViewModel : ObservableObject, IEditorViewModel
{public Func<Task<string>> OnSubmitting { get; set; }public Action<string> OnInited { get; set; }public Action OnFocus { get; set; }public EditNotePageViewModel(){Submit = new Command(SubmitAction);NoteSegmentState=NoteSegmentState.Edit;var content = "";using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Editorjs.Assets.sample1.json")){if (stream != null){using (StreamReader reader = new StreamReader(stream)){content = reader.ReadToEnd();                     }}}Init(new Note(){Title = "sample",Content=content});}private void Init(Note note){if (note != null){Title = note.Title;Content = note.Content;}OnInited?.Invoke(this.Content);}private string _title;public string Title{get { return _title; }set{_title = value;OnPropertyChanged();}}private string _content;public string Content{get { return _content; }set{_content = value;OnPropertyChanged();}}private async void SubmitAction(object obj){var savedContent = await OnSubmitting?.Invoke();if (string.IsNullOrEmpty(savedContent)){return;}this.Content=savedContent;var note = new Note();note.Title = this.Title;note.Content = this.Content;}public Command Submit { get; set; }}

注意这里的Init方法,用于初始化内容。这里我们读取Editorjs.Assets.sample1.json资源文件作为初始内容。

在这里插入图片描述

创建Blazor组件

创建Blazor页面EditorjsPage.razor

EditorjsPage.razor页面中,我们放置一个div,用于放置编辑器,

razor页面的 @Code 代码段中,放置EditNotePageViewModel属性,以及一个DotNetObjectReference对象,用于在JS中调用C#方法。

@code {[Parameter]public IEditorViewModel EditNotePageViewModel { get; set; }private DotNetObjectReference<EditorjsPage> objRef;protected override void OnInitialized(){objRef = DotNetObjectReference.Create(this);}

初始化

在script代码段中,创建LoadContent函数,用于加载EditorJs的初始内容。

<div class="ce-main"><div id="editorjs"></div>
</div>

LoadContent中,调用函数window.editor = new window.EditorJS(config)创建一个EditorJS对象,其中config对象包括holder,tools,data等属性,关于EditorJs配置的更多说明请参考官方文档

<script type="text/javascript">window.editor = null;window.viewService = {LoadContent: function (content) {var obj = JSON.parse(content);var createEdtor = () => {window.editor = new window.EditorJS({                 holder: 'editorjs',/*** Tools list*/tools: {paragraph: {config: {placeholder: "Enter something"}},header: {class: Header,inlineToolbar: ['link'],config: {placeholder: 'Header'},shortcut: 'CMD+SHIFT+H'},/*** Or pass class directly without any configuration*/image: {class: ImageTool},list: {class: NestedList,inlineToolbar: true,shortcut: 'CMD+SHIFT+L'},checklist: {class: Checklist,inlineToolbar: true,},quote: {class: Quote,inlineToolbar: true,config: {quotePlaceholder: '输入引用内容',captionPlaceholder: '引用标题',},shortcut: 'CMD+SHIFT+O'},marker: {class: Marker,shortcut: 'CMD+SHIFT+M'},code: {class: CodeTool,shortcut: 'CMD+SHIFT+C'},delimiter: Delimiter,inlineCode: {class: InlineCode,shortcut: 'CMD+SHIFT+C'},linkTool: LinkTool,embed: Embed,table: {class: Table,inlineToolbar: true,shortcut: 'CMD+ALT+T'},},i18n: {messages: {"ui": {"blockTunes": {"toggler": {"Click to tune": "点击转换","or drag to move": "拖动调整"},},"inlineToolbar": {"converter": {"Convert to": "转换成"}},"toolbar": {"toolbox": {"Add": "添加","Filter": "过滤","Nothing found": "无内容"},"popover": {"Filter": "过滤","Nothing found": "无内容"}}},"toolNames": {"Text": "段落","Heading": "标题","List": "列表","Warning": "警告","Checklist": "清单","Quote": "引用","Code": "代码","Delimiter": "分割线","Raw HTML": "HTML片段","Table": "表格","Link": "链接","Marker": "突出显示","Bold": "加粗","Italic": "倾斜","InlineCode": "代码片段","Image": "图片"},"tools": {"link": {"Add a link": "添加链接"},"stub": {'The block can not be displayed correctly.': '该模块不能放置在这里'},"image": {"Caption": "图片说明","Select an Image": "选择图片","With border": "添加边框","Stretch image": "拉伸图像","With background": "添加背景",},"code": {"Enter a code": "输入代码",},"linkTool": {"Link": "请输入链接地址","Couldn't fetch the link data": "获取链接数据失败","Couldn't get this link data, try the other one": "该链接不能访问,请修改","Wrong response format from the server": "错误响应",},"header": {"Header": "标题","Heading 1": "一级标题","Heading 2": "二级标题","Heading 3": "三级标题","Heading 4": "四级标题","Heading 5": "五级标题","Heading 6": "六级标题",},"paragraph": {"Enter something": "请输入笔记内容",},"list": {"Ordered": "有序列表","Unordered": "无序列表",},"table": {"Heading": "标题","Add column to left": "在左侧插入列","Add column to right": "在右侧插入列","Delete column": "删除列","Add row above": "在上方插入行","Add row below": "在下方插入行","Delete row": "删除行","With headings": "有标题","Without headings": "无标题",},"quote": {"Align Left": "左对齐","Align Center": "居中对齐",}},"blockTunes": {"delete": {"Delete": "删除",'Click to delete': "点击删除"},"moveUp": {"Move up": "向上移"},"moveDown": {"Move down": "向下移"},"filter": {"Filter": "过滤"}},}},/*** Initial Editor data*/data: obj});}if (window.editor) {editor.isReady.then(() => {editor.destroy();createEdtor();});}else {createEdtor();}},DumpContent: async function () {outputData = null;if (window.editor) {if (window.editor.readOnly.isEnabled) {await window.editor.readOnly.toggle();}var outputObj = await window.editor.save();outputData = JSON.stringify(outputObj);}return outputData;},SwitchTheme: function () {document.body.classList.toggle("dark-mode");},SwitchState: async function () {state = null;if (window.editor && window.editor.readOnly) {var readOnlyState = await window.editor.readOnly.toggle();state = readOnlyState;}return state;},Focus: async function (atEnd) {if (window.editor) {await window.editor.focus(atEnd);}},GetState() {if (window.editor && window.editor.readOnly) {return window.editor.readOnly.isEnabled;}},Destroy: function () {if (window.editor) {window.editor.destroy();}},}window.initObjRef = function (objRef) {window.objRef = objRef;}</script>

在这里插入图片描述

保存

创建转存函数DumpContent

DumpContent: async function () {outputData = null;if (window.editor) {if (window.editor.readOnly.isEnabled) {await window.editor.readOnly.toggle();}var outputObj = await window.editor.save();outputData = JSON.stringify(outputObj);}return outputData;
},

销毁

创建销毁函数Destroy


Destroy: function () {if (window.editor) {window.editor.destroy();}
},

编写渲染逻辑

在OnAfterRenderAsync中调用初始化函数,并订阅OnSubmitting和OnInited事件,以便在提交事件触发时保存,以及文本状态变更时重新渲染。

 protected override async Task OnAfterRenderAsync(bool firstRender){if (!firstRender)return;if (EditNotePageViewModel != null){EditNotePageViewModel.PropertyChanged += EditNotePageViewModel_PropertyChanged;this.EditNotePageViewModel.OnSubmitting += OnSubmitting;this.EditNotePageViewModel.OnInited += OnInited;var currentContent = EditNotePageViewModel.Content;await JSRuntime.InvokeVoidAsync("viewService.LoadContent", currentContent);}await JSRuntime.InvokeVoidAsync("window.initObjRef", this.objRef);}
private async Task<string> OnSubmitting()
{var savedContent = await JSRuntime.InvokeAsync<string>("viewService.DumpContent");return savedContent;
}private async void OnInited(string content)
{await JSRuntime.InvokeVoidAsync("viewService.LoadContent", content);
}

在这里插入图片描述

实现只读/编辑功能

在.NET本机中,我们使用枚举来表示编辑状态。 并在控件上设置一个按钮来切换编辑状态。

public enum NoteSegmentState
{Edit,PreView
}

EditNotePageViewModel.cs:

...
private NoteSegmentState _noteSegmentState;public NoteSegmentState NoteSegmentState{get { return _noteSegmentState; }set{_noteSegmentState = value;OnPropertyChanged();}}

EditNotePage.xaml:

...
<StackLayout RadioButtonGroup.GroupName="State"RadioButtonGroup.SelectedValue="{Binding NoteSegmentState,Mode=TwoWay}"Orientation="Horizontal"><RadioButton Value="{x:Static service:NoteSegmentState.Edit}"Content="编辑"></RadioButton><RadioButton Value="{x:Static service:NoteSegmentState.PreView}"Content="预览"></RadioButton></StackLayout>

Editorjs官方提供了readOnly对象,通过toggle()方法,可以切换编辑模式和只读模式。

在创建Editorjs实例时,也可以通过设置readOnly属性为true即可实现只读模式。

切换模式

在razor页面中创建SwitchState函数,用来切换编辑模式和只读模式。

SwitchState: async function () {state = null;if (window.editor && window.editor.readOnly) {var readOnlyState = await window.editor.readOnly.toggle();state = readOnlyState;}return state;
},

获取只读模式状态

在razor页面中创建GetState函数,用来获取编辑模式和只读模式的状态。


GetState() {if (window.editor && window.editor.readOnly) {return window.editor.readOnly.isEnabled;}
},

响应切换事件

我们监听EditNotePageViewModel 的NoteSegmentState属性变更事件,当状态改变时,调用对应的js方法

private async void EditNotePageViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{if (e.PropertyName == nameof(EditNotePageViewModel.NoteSegmentState)){if (EditNotePageViewModel.NoteSegmentState==NoteSegmentState.PreView){var state = await JSRuntime.InvokeAsync<bool>("viewService.GetState");if (!state){await JSRuntime.InvokeAsync<bool>("viewService.SwitchState");}}else if (EditNotePageViewModel.NoteSegmentState==NoteSegmentState.Edit){var state = await JSRuntime.InvokeAsync<bool>("viewService.GetState");if (state){await JSRuntime.InvokeAsync<bool>("viewService.SwitchState");}}}
}

在这里插入图片描述

实现明/暗主题切换

lib/editorjs/css/main.css中,定义了.dark-mode类的样式表

.dark-mode {
--color-border-light: rgba(255, 255, 255,.08);
--color-bg-main: #212121;
--color-text-main: #F5F5F5;
}.dark-mode .ce-popover {--color-background: #424242;--color-text-primary: #F5F5F5;--color-text-secondary: #707684;--color-border: #424242;
}.dark-mode .ce-toolbar__settings-btn {background: #2A2A2A;border: 1px solid #424242;
}.dark-mode .ce-toolbar__plus {background: #2A2A2A;border: 1px solid #424242;
}.dark-mode .ce-popover-item__icon {background: #2A2A2A;
}.dark-mode .ce-code__textarea {color: #212121;background: #2A2A2A;
}.dark-mode .tc-popover {--color-border: #424242;--color-background: #424242;
}
.dark-mode .tc-wrap {--color-background: #424242;
}

在razor页面中添加SwitchTheme函数,用于用于切换dark-mode"的`类名,从而实现暗黑模式和正常模式之间的切换。

SwitchTheme: function () {document.body.classList.toggle("dark-mode");
},

OnInitializedAsync中,订阅Application.Current.RequestedThemeChanged事件,用于监听主题切换事件,并调用SwitchTheme函数。

protected override async Task OnInitializedAsync()
{objRef = DotNetObjectReference.Create(this);Application.Current.RequestedThemeChanged += OnRequestedThemeChanged;}
private async void OnRequestedThemeChanged(object sender, AppThemeChangedEventArgs args)
{await JSRuntime.InvokeVoidAsync("viewService.SwitchTheme");
}

在渲染页面时,也判断是否需要切换主题

protected override async Task OnAfterRenderAsync(bool firstRender)
{if (!firstRender)return;···if (Application.Current.UserAppTheme==AppTheme.Dark){await JSRuntime.InvokeVoidAsync("viewService.SwitchTheme");}}

在这里插入图片描述

项目地址

Github:maui-samples

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/308064.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自定义滚动条样式:前端实现跨浏览器兼容

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

【核心完整复现】基于目标级联法的微网群多主体分布式优化调度

1 主要内容 之前发布了华电学报的复现程序《基于目标级联法的微网群多主体分布式优化调度》&#xff0c;具体链接为【防骗版】基于目标级联法的微网群多主体分布式优化调度&#xff0c;虽然对模型及结果进行了复现&#xff0c;但是部分模型细节和参数并没有完全实现&#xff0…

MySQL单行函数

文章目录 数值函数基本函数角度与弧度互换函数三角函数指数与对数进制间的转换 字符串函数日期和时间函数获取日期、时间日期与时间戳的转换获取月份、星期、星期数、天数等函数日期的操作函数时间和秒钟转换的函数计算日期和时间的函数日期的格式化与解析 流程控制函数加密与解…

DonkeyDocker-v1-0渗透思路

MY_BLOG https://xyaxxya.github.io/2024/04/13/DonkeyDocker-v1-0%E6%B8%97%E9%80%8F%E6%80%9D%E8%B7%AF/ date: 2024-04-13 19:15:10 tags: 内网渗透Dockerfile categories: 内网渗透vulnhub 靶机下载地址 https://www.vulnhub.com/entry/donkeydocker-1,189/ 靶机IP&a…

elasticSearch从零整合springboot项目实操

type会被弃用 &#xff0c;就是说之后的elasticSearch中只会存在 索引&#xff08;indices&#xff09; 和 一行&#xff08;document&#xff09; 和字段&#xff08;fields&#xff09; elasticSearch 和solr的区别最大的就是 es对应的 是 json的格式 。 solr有xml和josn等…

React Hooks 全解: 常用 Hooks 及使用场景详解

React Hooks 是 React 16.8 版本引入的一项重要特性,它极大地简化和优化了函数组件的开发过程。 React 中常用的 10 个 Hooks,包括 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef、useLayoutEffect、useImperativeHandle 和 useDebugValue。这些…

ARM内核的CPU架构模型

1.引言 程序员在编码的时候&#xff0c;如果想有进一步的提升&#xff0c;我认为还是要深入底层理解程序运行原理才好。最近看了一些ARM架构的讲解&#xff0c;总结了如下。虽然是以ARM为原型画的图形&#xff0c;但是对于C和C的编程&#xff0c;还是有一些参考价值的。 2. AR…

云服务器租用一年、1个月优惠价格表,阿里/腾讯/京东/华为云

现在租一个服务器多少一个月&#xff1f;优惠价格低至3.8元1个月&#xff0c;租用一个月云服务器收费价格表&#xff1a;阿里云和腾讯云2核2G3M服务器优惠价格61元一年&#xff0c;折合一个月5元&#xff0c;京东云轻量云主机5.8元一个月&#xff0c;华为云服务器优惠价格3.8元…

wangeditor与deaftjs的停止维护,2024编辑器该如何做技术选型(一)

wangeditor暂停维护的声明&#xff1a; wangeditor是国内开发者开发的编辑器&#xff0c;用户也挺多&#xff0c;但是由于作者时间关系&#xff0c;暂停维护。 deaft的弃坑的声明&#xff1a; draft是Facebook开源的&#xff0c;但是也弃坑了&#xff0c;说明设计的时候存在很大…

为数据穿上安全的外衣——零售电商场景下的数据安全体系建设

在电子商务交易过程中&#xff0c;会涉及大量的个人和财务数据的传输和处理&#xff0c;随着电子商务的发展&#xff0c;数据安全风险也成为一个备受关注的问题。 而跨境电商&#xff0c;属于出海业务&#xff0c;涉及到海外不同国家的政策法规&#xff0c;且数据作为电商的业…

等保测评2.0——网络安全等级保护测评的初步了解

一、什么是网络安全等级保护测评&#xff1f; 二、网络安全等级保护&#xff0c;保护的是什么&#xff1f; 等级保护对象&#xff1a;网络安全等级保护工作直接作用的对象。&#xff08;注&#xff1a;主要包括信息系统、通信网络设施和数据资源等&#xff09; 计算机信息系统…

数据库系统概论(超详解!!!)第四节 数据库安全性

问题的提出&#xff1a; 数据库的一大特点是数据可以共享 数据共享必然带来数据库的安全性问题 数据库系统中的数据共享不能是无条件的共享。 1.数据库的安全概述 数据库的安全性是指保护数据库以防止不合法使用所造成的数据泄露、更改或破坏 。 系统安全保护措施是否有效…

Linux【实战篇】—— NFS服务搭建与配置

目录 一、介绍 1.1什么是NFS&#xff1f; 1.2客户端与服务端之间的NFS如何进行数据传输&#xff1f; 1.3RPC和NFS的启动顺序 1.4NFS服务 系统守护进程 二、安装NFS服务端 2.1安装NFS服务 2.2 创建共享目录 2.3创建共享目录首页文件 2.4关闭防火墙 2.5启动NFS服务 2.…

基于VC709构建FPGA开发学习平台(一)-系统时钟与LED

一、时钟 1. 系统时钟 板载U34可编程晶振SI570&#xff0c;为用户提供基础时钟&#xff0c;上电后&#xff0c;默认时钟频率为156.25Mhz&#xff0c;可作为free run时钟源&#xff0c;输入到PLL中&#xff0c;生成系统时钟&#xff1a; 约束为&#xff1a; set_property PACK…

Unity给地图物体添加对撞机

在项目/Assets下创建Prefabs文件夹 选择素材拖入层级下&#xff0c;注意此时地图素材有可能看不到&#xff0c;此时选择Tilemap在检查器中修改图层顺序调至最低。 添加对撞机 选择素材&#xff0c;在检查器中点击添加组件Box Collider 2D&#xff0c;将素材拖入Prefabs文件下…

SSL、TLS和HTTPS:网络安全的重要基石

随着互联网的快速发展&#xff0c;网络安全问题日益凸显。为了保护数据在传输过程中的安全&#xff0c;各种加密协议和技术应运而生。SSL&#xff08;安全套接层&#xff09;、TLS&#xff08;传输层安全&#xff09;和HTTPS&#xff08;超文本传输安全协议&#xff09;是三个最…

力扣LeetCode138. 复制带随机指针的链表 两种解法(C语言实现)

目录 题目链接 题目分析 题目定位&#xff1a; 解题思路 解题思路1&#xff08;粗暴但是复杂度高&#xff09; 解题思路2&#xff08;巧妙并且复杂度低&#xff09; 题目链接 138. 复制带随机指针的链表https://leetcode-cn.com/problems/copy-list-with-random-pointer/ …

【STL详解 —— list的介绍及使用】

STL详解 —— list的介绍及使用 list的介绍list的介绍使用list的构造list iterator的使用list capacitylist element accesslist modifiers 示例list的迭代器失效 list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭…

分类预测 | Matlab实现基于迁移学习和GASF-CNN-Mutilhead-Attention格拉姆角场和卷积网络多头注意力机制多特征分类预测/故障识别

分类预测 | Matlab实现基于迁移学习和GASF-CNN-Mutilhead-Attention格拉姆角场和卷积网络多头注意力机制多特征分类预测/故障识别 目录 分类预测 | Matlab实现基于迁移学习和GASF-CNN-Mutilhead-Attention格拉姆角场和卷积网络多头注意力机制多特征分类预测/故障识别分类效果基…

自动化运维工具Ansible

ansible 主要的意义在于可以 提高运维工作效率&#xff0c;降低成本提高准确度 资料 Ansible中文权威指南 离线安装 wget https://github.com/ansible/ansible/archive/refs/tags/v2.16.5.tar.gz tar -zxvf v2.16.5.tar.gz cd ansible-v2.16.5 python setup.py install 主要…