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

news2025/9/21 12:03:31

文章目录

    • 获取资源
      • 从源码构建
      • 从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.coloradmin.cn/o/1592622.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【opencv】示例-npr_demo.cpp 非真实感渲染:边缘保留平滑、细节增强、铅笔素描/彩色铅笔绘图和风格化处理...

Edge Preserve Smoothing- Using Normalized convolution Filter Edge Preserve Smoothing-Using Recursive Filter Detail Enhancement Pencil sketch/Color Pencil Drawing Stylization /* * npr_demo.cpp * * 作者: * Siddharth Kherada <siddharthkherada27[at]gmail[do…

最优算法100例之46-单链表逆置-头插法

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 单链表逆置-头插法 题解报告 1)带头结点 带头结点的反转链表,头结点一直不动,后移head->next。不带头结点的反转,后移…

【vue】defineEmits 传值 子传父

先行知识 【vue】导入组件【vue】defineProps 传数据 父传子 传值流程 App.vue <template><Header getWeb"emitsGetWeb" userAdd"emitsUserAdd"/><hr /><p>web.name: {{ web.name }}</p><p>web.url: {{ web.url }}&…

HarmonyOS实战开发-横竖屏切换

介绍 本实例展示如何使用媒体查询&#xff0c;通过ohos.mediaquery 接口完成在不同设备上显示不同的界面效果。 效果预览 使用说明 1.在竖屏设备上&#xff0c;首页展示新闻列表&#xff0c;点击新闻进入详情界面。 2.在横屏设备上&#xff0c;首页左侧展示新闻列表&#x…

MATLAB 自定义实现点云法向量和曲率计算(详细解读)(64)

MATLAB 自定义实现点云法向量和曲率计算(详细解读)(64) 一、算法介绍二、算法步骤三、算法实现1.代码 (完整,注释清晰,可直接用)2.结果一、算法介绍 首先说明: ------这里代码手动实现,不调用matlab提供的法向量计算接口,更有助于大家了解法向量和曲率的计算方法,…

【数据结构】复习题(二)

Hello&#xff01;大家好&#xff0c;这一篇数据结构复习题是我上个学期复习的时候写的&#xff08;刚刚在草稿箱发现了&#xff01;&#xff09;有一些题目过程都是配了图片的&#xff0c;希望对正在复习数据结构的宝宝们有帮助哦&#xff01;(还有一个数据结构复习题(一)可以…

从零开始写一个RTSP服务器(一)RTSP协议讲解

目录 前言一、什么是RTSP协议&#xff1f;二、RTSP协议详解2.1 RTSP数据格式2.2 RTSP请求的常用方法2.3 RTSP交互过程2.4 sdp格式 三、RTP协议3.1 RTP包格式3.2 RTP OVER TCP 四、RTCP 前言 为什么要写这个系列&#xff1f; 因为我自己在学习rtsp协议想自己从零写一个rtsp服务…

pytorch车牌识别

目录 使用pytorch库中CNN模型进行图像识别收集数据集定义CNN模型卷积层池化层全连接层 CNN模型代码使用模型 使用pytorch库中CNN模型进行图像识别 收集数据集 可以去找开源的数据集或者自己手做一个 最终整合成 类别分类的图片文件 定义CNN模型 卷积层 功能&#xff1a;提…

R:普通分组柱状图

输入文件实例&#xff08;存为csv格式&#xff09; library(ggplot2) library(ggbreak)# 从CSV文件中读取数据 setwd("C:/Users/fordata/Desktop/研究生/第二个想法(16s肠型&#xff0b;宏基因组功能)/第二篇病毒组/result/otherDB") data <- read.csv("feta…

【软件设计师知识点】一、计算机系统基础知识

文章目录 冯诺依曼计算机CPUCPU 的功能CPU 的组成 数据表示进制转换单位换算定点数浮点小数IEEE 754标准浮点数的运算 校验码奇偶校验码海明码循环冗余校验码&#xff08;CRC&#xff09; 指令系统指令格式寻址方式指令集指令流水线 存储系统存储器的层次化结构存储器的分类相联…

前端vue: 使用ElementUI适配国际化

i18n介绍 i18n&#xff08;其来源是英文单词 internationalization的首末字符i和n&#xff0c;18为中间的字符数&#xff09;是“国际化”的简称。 前端国际化步骤 1、安装i18n插件 安装插件时候&#xff0c;注意必须指定版本号&#xff0c;不然安装会报错。 npm i vue-i1…

【opencv】示例-points_classifier.cpp 使用不同机器学习算法在二维空间中对点集进行分类...

#include "opencv2/core.hpp" // 包含OpenCV核心功能的文件 #include "opencv2/imgproc.hpp" // 包含OpenCV图像处理功能的文件 #include "opencv2/ml.hpp" // 包含OpenCV机器学习模块的文件 #include "opencv2/highgui.hpp" // 包含O…

docker部署Prometheus+AlertManager实现邮件告警

文章目录 一、环境准备1、硬件准备&#xff08;虚拟机&#xff09;2、关闭防火墙&#xff0c;selinux3、所有主机安装docker 二、配置Prometheus1、docker启动Prometheus 三、添加监控节点1、docker启动node-exporter 四、Prometheus配置node-exporter1、修改prometheus.yml配置…

使用Python实现自动化网页答题功能-模拟考试篇

介绍 在驾驶员考试网站上进行模拟考试python自动答题 自动化原理 该脚本使用了自动化模块 DrissionPage 中的 ChromiumPage 类来实现网页的自动化操作。通过定位网页元素和模拟点击操作&#xff0c;完成了选择答案和提交答卷的过程。 用途与注意事项 用途&#xff1a;该脚本…

kafka学习笔记03

SpringBoot2.X项目搭建整合Kafka客户端依赖配置 用自己对应的jdk版本。 先加上我们的web依赖。 添加kafka依赖: SpringBoot2.x整合Kafka客户端adminApi单元测试 设置端口号。 新建一个kafka测试类&#xff1a; 创建一个初始化的Kafka服务。 设置kafka的名称。 测试创建kafka。…

在vue中配置样式 max-width:100px时,发现和width:100px一样没有对应的递增到最大宽度的效果?怎么回事?怎么解决?

原因&#xff1a; 可能时vue的样式大部分和display相关&#xff0c;有很多的联系&#xff0c;导致不生效 解决&#xff1a; 对设置max-width样式的元素设置display:inline-block;属性&#xff0c;即可生效&#xff0c;实现随着子元素的扩展而扩展并增加固定到最大的宽度

使用Postman发送跨域请求实验

使用Postman发送跨域请求 1 跨域是什么&#xff1f;2 何为同源呢?3 跨域请求是如何被检测到的&#xff1f;4 Postman跨域请求测试4.1 后端准备4.2 测试用例4.2.1 后端未配置跨域请求(1) 前端不跨域&#xff08;2&#xff09;前端跨域 4.2.2 后端配置跨域信息&#xff08;1&…

Springboot整合mybatis_plus + redis(使用原生的方式)

首次&#xff0c;创建一个springboot项目&#xff0c;勾选相应的依赖Lombok、Web 添加依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>…

基于Python的深度学习的中文情感分析系统(V2.0),附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

13 Php学习:面向对象

PHP 面向对象 面向对象&#xff08;Object-Oriented&#xff0c;简称 OO&#xff09;是一种编程思想和方法&#xff0c;它将程序中的数据和操作数据的方法封装在一起&#xff0c;形成"对象"&#xff0c;并通过对象之间的交互和消息传递来完成程序的功能。面向对象编…