• 从零开始一起学Blazor WebAssembly 开发(5) 富文本框


    今天用到富文本框了,ant-design提供的是vditor的控件。但是感觉功能有点少,而且扩展的不太好。先后对比了下vditor、quill、ckeditor

    vditor 主要优势在markdown的编写上,我这项目用户群体不适合用markdown,quill是轻量级的,利于扩展,ckeditor重量级,功能很多。但是就是太重了。最终选择了这个quill。

    网上走到一个结合quill 富文本框做的一个开源的组件,但是这个组件有几个缺点

    1、没有实现双向绑定

    2、图片上传采用的base64存储

    于是对这个组件做了一次大手术,修改了不少地方。代码改动量几乎相当于重新做了遍了。

    先看下效果

    从零开始一起学Blazor WebAssembly 开发(5) 富文本框

     

    接下来就给看下每个细节地方的代码

    QuillEditor.razor

    @inject IJSRuntime JSRuntime
        <div @ref="@ToolBarContainer">
            @if (ToolBarMode == "basic")
            {
                <span class="ql-formats">
                    <select class="ql-font"></select>
                    <select class="ql-size"></select>
                </span>
                <span class="ql-formats">
                    <button class="ql-bold"></button>
                    <button class="ql-italic"></button>
                    <button class="ql-underline"></button>
                    <button class="ql-strike"></button>
                </span>
                <span class="ql-formats">
                    <select class="ql-color"></select>
                    <select class="ql-background"></select>
                </span>
                <span class="ql-formats">
                    <button class="ql-header" value="1"></button>
                    <button class="ql-header" value="2"></button>
                    <button class="ql-blockquote"></button>
                    <button class="ql-code-block"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-list" value="ordered"></button>
                    <button class="ql-list" value="bullet"></button>
                    <button class="ql-indent" value="-1"></button>
                    <button class="ql-indent" value="+1"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-link"></button>
                    <button class="ql-image"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-clean"></button>
                </span>
            }
            else if (ToolBarMode == "full")
            {
                <span class="ql-formats">
                    <select class="ql-font"></select>
                    <select class="ql-size"></select>
                </span>
                <span class="ql-formats">
                    <button class="ql-bold"></button>
                    <button class="ql-italic"></button>
                    <button class="ql-underline"></button>
                    <button class="ql-strike"></button>
                </span>
                <span class="ql-formats">
                    <select class="ql-color"></select>
                    <select class="ql-background"></select>
                </span>
                <span class="ql-formats">
                    <button class="ql-script" value="sub"></button>
                    <button class="ql-script" value="super"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-header" value="1"></button>
                    <button class="ql-header" value="2"></button>
                    <button class="ql-blockquote"></button>
                    <button class="ql-code-block"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-list" value="ordered"></button>
                    <button class="ql-list" value="bullet"></button>
                    <button class="ql-indent" value="-1"></button>
                    <button class="ql-indent" value="+1"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-direction" value="rtl"></button>
                    <select class="ql-align"></select>
                </span>
                <span class="ql-formats">
                    <button class="ql-link"></button>
                    <button class="ql-image"></button>
                    <button class="ql-video"></button>
                    <button class="ql-formula"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-clean"></button>
                </span>
            }
            else if (ToolBarMode == "custom")
            {
                @ToolBarContent
            }
        </div>
        <div @ref="@ElementContentContainer" style="height:@ContainerHeight">
            @EditorContent
        </div>

    QuillEditor.razor.cs

    public partial class QuillEditor : ComponentBase
        {
            [Parameter]
            public string Value
            {
                get => _value??"";
                set
                {
                    if (_value != value)
                    {
                        _value = value;
                        _wattingUpdate = true;
                    }
                }
            }
            private bool _wattingUpdate = false;
            private string _value;
            [Parameter] public EventCallback<string> ValueChanged { get; set; }
            [Inject] private QuillEditorService EditorService { get; set; }
            [Parameter]
            public RenderFragment EditorContent { get; set; }
    
            [Parameter]
            public RenderFragment ToolBarContent { get; set; }
    
            [Parameter]
            public bool ReadOnly { get; set; }= false;
    
            [Parameter]
            public string Placeholder { get; set; }= "";
    
            [Parameter]
            public string Theme { get; set; }= "snow";
    
            [Parameter]
            public string DebugLevel { get; set; }= "info";
            [Parameter]
            public string ToolBarMode { get; set; } = "basic";//full
            [Parameter]
            public string ContainerHeight { get; set; } = "600px";
            [Parameter]
            public string UploadImageUrl { get; set; }
    
            private ElementReference ElementContentContainer;
            private ElementReference ToolBarContainer;
    
            protected override async Task OnAfterRenderAsync(bool firstRender)
            {
                if (firstRender)
                {
                    await EditorService.CreateQuill(
                            ElementContentContainer,
                            ToolBarContainer,
                            ReadOnly,
                            Placeholder,
                            Theme,
                            DebugLevel,
                            UploadImageUrl,
                            this);
                }
            }
    
            protected override async Task OnParametersSetAsync()
            {
                await base.OnParametersSetAsync();
            }
            [JSInvokable]
            public void OnInput(string value)
            {
                _value = value;
                _wattingUpdate = false;
    
                if (ValueChanged.HasDelegate)
                {
                    ValueChanged.InvokeAsync(value);
                }
            }
            public async Task<string> GetText()
            {
                return await EditorService.GetText(ElementContentContainer);
            }
    
            public async Task<string> GetHTML()
            {
                return await EditorService.GetHTML(ElementContentContainer);
            }
    
            public async Task<string> GetContent()
            {
                return await EditorService.GetContent(ElementContentContainer);
            }
    
            public async Task LoadContent(string Content)
            {
                var QuillDelta =
                    await EditorService.LoadQuillContent(ElementContentContainer, Content);
            }
    
            public async Task LoadHTMLContent(string quillHTMLContent)
            {
                var QuillDelta =
                    await EditorService.LoadQuillHTMLContent(ElementContentContainer, quillHTMLContent);
            }
    
            public async Task InsertImage(string ImageURL)
            {
                var QuillDelta =
                    await EditorService.InsertQuillImage(ElementContentContainer, ImageURL);
            }
    
            public async Task EnableEditor(bool mode)
            {
                var QuillDelta =
                    await EditorService.EnableQuillEditor(ElementContentContainer, mode);
            }
        }

    QuillEditorService.cs

    public class QuillEditorOption
        {
            public string UploadBaseUrl { get; set; }
        }
        public class QuillEditorService
        {
            private readonly IJSRuntime jsRuntime;
            private readonly QuillEditorOption _option;
            public QuillEditorService(IJSRuntime js, IOptions<QuillEditorOption> option)
            {
                jsRuntime = js;
                _option = option.Value;
            }
            
            public async ValueTask<object> CreateQuill(
                    ElementReference quillElement,
                    ElementReference toolbar,
                    bool readOnly,
                    string placeholder,
                    string theme,
                    string debugLevel,
                    string uploadImageUrl,
                    QuillEditor editor)
            {
                return await jsRuntime.InvokeAsync<object>(
                    "QuillFunctions.createQuill",
                    quillElement, toolbar, readOnly,
                    placeholder, theme, debugLevel, DotNetObjectReference.Create(editor),editor.Value, _option.UploadBaseUrl+uploadImageUrl);
            }
    
            public async ValueTask<string> GetText(
                ElementReference quillElement)
            {
                return await jsRuntime.InvokeAsync<string>(
                    "QuillFunctions.getQuillText",
                    quillElement);
            }
    
            public async ValueTask<string> GetHTML(
                ElementReference quillElement)
            {
                return await jsRuntime.InvokeAsync<string>(
                    "QuillFunctions.getQuillHTML",
                    quillElement);
            }
    
            public async ValueTask<string> GetContent(
                ElementReference quillElement)
            {
                return await jsRuntime.InvokeAsync<string>(
                    "QuillFunctions.getQuillContent",
                    quillElement);
            }
    
            public async ValueTask<object> LoadQuillContent(
                ElementReference quillElement,
                string Content)
            {
                return await jsRuntime.InvokeAsync<object>(
                    "QuillFunctions.loadQuillContent",
                    quillElement, Content);
            }
    
            public async ValueTask<object> LoadQuillHTMLContent(
                ElementReference quillElement,
                string quillHTMLContent)
            {
                return await jsRuntime.InvokeAsync<object>(
                    "QuillFunctions.loadQuillHTMLContent",
                    quillElement, quillHTMLContent);
            }
    
            public async ValueTask<object> EnableQuillEditor(
                    ElementReference quillElement,
                    bool mode)
            {
                return await jsRuntime.InvokeAsync<object>(
                        "QuillFunctions.enableQuillEditor",
                        quillElement, mode);
            }
    
            public async ValueTask<object> InsertQuillImage(
                    ElementReference quillElement,
                    string imageURL)
            {
                return await jsRuntime.InvokeAsync<object>(
                    "QuillFunctions.insertQuillImage",
                    quillElement, imageURL);
            }
        }

    blazorquill.js

    (function () {
        window.QuillFunctions = {
            createQuill: function (
                quillElement, toolBar, readOnly,
                placeholder, theme, debugLevel,editor,value,uploadurl) {
    
                Quill.register('modules/blotFormatter', QuillBlotFormatter.default);
    
                var options = {
                    debug: debugLevel,
                    modules: {
                        toolbar: toolBar,
                        blotFormatter: {}
                    },
                    placeholder: placeholder,
                    readOnly: readOnly,
                    theme: theme
                };
    
                quillElement.__quill = new Quill(quillElement, options);
                quillElement.__quill.root.innerHTML = value;//初始化时设置初始值 HTML
                quillElement.__quill.on('text-change', function () {
                    var cotent = quillElement.__quill.root.innerHTML;
                    editor.invokeMethodAsync('OnInput', cotent);
                });
                let toolbar = quillElement.__quill.getModule('toolbar');
                toolbar.addHandler('image', () => {
                    var fileInput = toolbar.container.querySelector('input.ql-image[type=file]');
                    if (fileInput == null) {
                        fileInput = document.createElement('input');
                        fileInput.setAttribute('type', 'file');
                        fileInput.setAttribute('accept', 'image/*');
                        fileInput.classList.add('ql-image');
                        fileInput.addEventListener('change', function () {
                            if (fileInput.files != null && fileInput.files[0] != null) {
                                var formData = new FormData();
                                formData.append('file', fileInput.files[0]);
                                var xhr = new XMLHttpRequest();
                                // 调用xhr.open()函数
                                xhr.open("POST", uploadurl);
                                //调用xhr.send()函数,发送请求,这一步是异步操作
                                xhr.send(formData);
                                //监听 xhr.onreadystatechange  事件
                                xhr.onreadystatechange = function () {
                                    if (xhr.readyState === 4 && xhr.status === 200) {
                                        var filejson = eval("(" + xhr.responseText + ")");
                                        var range = quillElement.__quill.getSelection();
                                       //插入图片
                                        quillElement.__quill.insertEmbed(range.index, 'image', filejson.fileUrl);
                                    }
                                }
                            }
                        });
                        toolbar.container.appendChild(fileInput);
                    }
                    fileInput.click();
                });
            },
            getQuillContent: function (quillElement) {
                return JSON.stringify(quillElement.__quill.getContents());
            },
            getQuillText: function (quillElement) {
                return quillElement.__quill.getText();
            },
            getQuillHTML: function (quillElement) {
                return quillElement.__quill.root.innerHTML;
            },
            loadQuillContent: function (quillElement, quillContent) {
                content = JSON.parse(quillContent);
                return quillElement.__quill.setContents(content, 'api');
            },
            loadQuillHTMLContent: function (quillElement, quillHTMLContent) {
                return quillElement.__quill.root.innerHTML = quillHTMLContent;
            },
            enableQuillEditor: function (quillElement, mode) {
                quillElement.__quill.enable(mode);
            },
            insertQuillImage: function (quillElement, imageURL) {
                var Delta = Quill.import('delta');
                editorIndex = 0;
    
                if (quillElement.__quill.getSelection() !== null) {
                    editorIndex = quillElement.__quill.getSelection().index;
                }
    
                return quillElement.__quill.updateContents(
                    new Delta()
                        .retain(editorIndex)
                        .insert({ image: imageURL },
                            { alt: imageURL }));
            }
        };
    })();

     

     
  • 相关阅读:
    2020软件工程02
    自我介绍
    2019年春总结作业
    第十二周作业
    第十一周作业
    第十周作业
    第九周作业
    第八周作业
    第七周学习总结
    第六周学习总结
  • 原文地址:https://www.cnblogs.com/wcoolly/p/14692533.html
Copyright © 2020-2023  润新知