美文网首页
vue使用wangEditor自定义上传音频的新功能

vue使用wangEditor自定义上传音频的新功能

作者: Goorln | 来源:发表于2023-12-13 16:40 被阅读0次

    image.png
    wangEditor官网 开源 Web 富文本编辑器,开箱即用,配置简单

    要实现一个完整的富文本编辑器功能,你可能还需要以下功能:

    Vue2

    安装

    npm install @wangeditor/editor --save
    # 或者 yarn add @wangeditor/editor
    
    npm install @wangeditor/editor-for-vue --save
    # 或者 yarn add @wangeditor/editor-for-vue
    

    使用

    <template>
        <div style="border: 1px solid #ccc;">
            <Toolbar
                style="border-bottom: 1px solid #ccc"
                :editor="editor"
                :defaultConfig="toolbarConfig"
                :mode="mode"
            />
            <Editor
                style="height: 500px; overflow-y: hidden;"
                v-model="html"
                :defaultConfig="editorConfig"
                :mode="mode"
                @onCreated="onCreated"
            />
        </div>
    </template>
    <script>
    import Vue from 'vue'
    import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
    
    export default Vue.extend({
        components: { Editor, Toolbar },
        data() {
            return {
                editor: null,
                html: '<p>hello</p>',
                toolbarConfig: { },
                editorConfig: { placeholder: '请输入内容...' },
                mode: 'default', // or 'simple'
            }
        },
        methods: {
            onCreated(editor) {
                this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
            },
        },
        mounted() {
            // 模拟 ajax 请求,异步渲染编辑器
            setTimeout(() => {
                this.html = '<p>模拟 Ajax 异步设置内容 HTML</p>'
            }, 1500)
        },
        beforeDestroy() {
            const editor = this.editor
            if (editor == null) return
            editor.destroy() // 组件销毁时,及时销毁编辑器
        }
    })
    </script>
    

    TIP:
    赋值 this.editor 时要用 Object.seal()
    组件销毁时,要及时销毁编辑器

    wangEditor富文本编辑框中,有上传图片和上传视频的功能,但是没有上传音频的功能,所以就需要使用wangEditor的自动以扩展新功能来实现上传音频的功能

    自定义扩展新功能

    文件目录为:


    image.png

    1. 注册新菜单

    import { IDomEditor, IDropPanelMenu } from "@wangeditor/editor";
    
    // class MyDropPanelMenu implements IDropPanelMenu {
    // TS 语法
    export default class AudioMenu {
      // JS 语法
    
      constructor() {
        this.title = "上传音频";
        this.iconSvg =
          '<svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#FF7F7F" d="M849.408 6.656L411.648 140.8c-53.248 15.36-95.744 70.656-95.744 123.392v461.312S284.16 704 213.504 714.24C109.568 729.088 25.6 808.448 25.6 891.904s83.968 134.656 187.904 119.808c103.936-14.848 179.712-91.648 179.712-175.104v-445.44c0-36.864 44.544-52.736 44.544-52.736l387.072-121.344s43.008-14.336 43.008 25.088v367.616s-39.424-22.528-110.08-14.336c-103.936 12.8-187.904 90.624-187.904 174.08S653.824 905.728 757.76 893.44c103.936-12.8 187.904-90.624 187.904-174.08V74.752c-0.512-52.224-43.52-82.944-96.256-68.096z"  /></svg>';
        this.tag = "button";
        this.showDropPanel = true;
      }
    
      // 菜单是否需要激活(如选中加粗文本,“加粗”菜单会激活),用不到则返回 false
      // isActive(editor: IDomEditor): boolean {
      // TS 语法
      isActive(editor) {
        // JS 语法
        return false;
      }
    
      // 获取菜单执行时的 value ,用不到则返回空 字符串或 false
      // getValue(editor: IDomEditor): string | boolean {
      // TS 语法
      getValue(editor) {
        // JS 语法
        return "";
      }
    
      // 菜单是否需要禁用(如选中 H1 ,“引用”菜单被禁用),用不到则返回 false
      // isDisabled(editor: IDomEditor): boolean {
      // TS 语法
      isDisabled(editor) {
        // JS 语法
        return false;
      }
    
      // 点击菜单时触发的函数
      // exec(editor: IDomEditor, value: string | boolean) {
      // TS 语法
      exec(editor, value) {
        // JS 语法
        // DropPanel menu ,这个函数不用写,空着即可
        if (this.isDisabled(editor)) {
          return;
        }
        editor.emit("AudioMenuClick");
      }
    }
    
    
    // export const menu1Conf = {
    //   key: "uploadAudio", // 定义 menu key :要保证唯一、不重复(重要)
    //   factory() {
    //     return new AudioMenu(); // 把 `YourMenuClass` 替换为你菜单的 class
    //   }
    // };
    
    

    2. 注册菜单到wangEditor,并插入菜单到工具栏 --- index.vue

    onCreated(editor) {
          this.editorRef = Object.seal(editor); // 一定要用 Object.seal() ,否则会报错
          this.toolbarConfig.insertKeys = {
            index: 24, // 插入的位置,基于当前的 toolbarKeys
            keys: ["menu1"]
          };
          // 注册菜单
          Boot.registerMenu(menu1Conf);
          module();
    
          // 事件监听
          const initMediaMenuEvent = () => {
            const editor = this.editorRef;
            // 在点击事件中,根据具体菜单,可以触发响应的功能,这里可以为每个事件创建一个el-dialog弹窗。我们就可以完全按照自己的需求开发后续功能
            editor.on("AudioMenuClick", () => {
              // 你点击了音频菜单
              console.log("123");
              editor.insertNode({
                type: "audio",
                src: "http://music.163.com/song/media/outer/url?id=1908673805.mp3",
                children: [{ text: "aaa" }]
              });
            });
          };
          initMediaMenuEvent(); // 注册自定义菜单点击事件
        }
    

    3. 定义节点数据结构 --- plugin.js

    import { DomEditor, IDomEditor } from "@wangeditor/editor";
    import { Transforms } from "slate";
    
    function withAudio(editor) {
      const { isVoid, normalizeNode } = editor;
      const newEditor = editor;
    
      // 重写 isVoid
    
      // @ts-ignore
      newEditor.isVoid = elem => {
        const { type } = elem;
    
        if (type === "audio") {
          return true;
        }
    
        return isVoid(elem);
      };
    
      // 重写 normalizeNode
      newEditor.normalizeNode = ([node, path]) => {
        const type = DomEditor.getNodeType(node);
    
        // ----------------- audio 后面必须跟一个 p header blockquote -----------------
        if (type === "audio") {
          // -------------- audio 是 editor 最后一个节点,需要后面插入 p --------------
          const isLast = DomEditor.isLastNode(newEditor, node);
          if (isLast) {
            Transforms.insertNodes(newEditor, DomEditor.genEmptyParagraph(), {
              at: [path[0] + 1]
            });
          }
        }
    
        // 执行默认的 normalizeNode ,重要!!!
        return normalizeNode([node, path]);
      };
    
      // 返回 editor ,重要!
      return newEditor;
    }
    export default withAudio;
    
    

    4. 在编辑器中渲染新元素 --- render-elem.js

    必须安装 snabbdom.js

    yarn add snabbdom --peer
    ## 安装到 package.json 的 peerDependencies 中即可
    
    import { DomEditor, IDomEditor, SlateElement } from "@wangeditor/editor";
    import { h, VNode } from "snabbdom";
    
    function renderAudioElement(elemNode, children, editor) {
      const { src = "", width = "300", height = "54" } = elemNode;
      const selected = DomEditor.isNodeSelected(editor, elemNode);
    
      const audioVnode = h(
        "audio", // html标签
        {
          props: {
            src: src,
            contentEditable: false,
            controls: true
          },
          style: {
            width: width + "px",
            height: height + "px",
            "max-width": "100%" // 这里之所以要写死,是为了实现宽度自适应的。如果直接设置width:100%,会触发报错。所以想要实现width:100%效果,需要先设置max-width,然后在给width设置一个离谱的值,比如说100000.
          }
        }
      );
      const vnode = h(
        "div",
        {
          props: {
            className: "w-e-textarea-video-container", // 这里直接复用video的效果
            "data-selected": selected ? "true" : ""
          }
        },
        audioVnode
      );
      const containerVnode = h(
        "div",
        {
          props: {
            contentEditable: false
          },
          on: {
            mousedown: e => e.preventDefault()
          }
        },
        vnode
      );
      return containerVnode;
    }
    const renderAudioConf = {
      type: "audio", // 新元素 type ,重要!!!即custom-type中定义的type
      renderElem: renderAudioElement
    };
    
    export { renderAudioConf };
    
    

    5. 把新元素转换为 HTML --- elem-to-html.js

    import { SlateElement } from "@wangeditor/editor";
    
    function audioElemtToHtml(elem, childrenHtml) {
      const { src, width = 300, height = 54 } = elem;
      // 通过data-w-e开头的data数据,存放一些必要的信息,到时候通过setHtml将富文本信息还原回编辑器的时候,才能使编辑器正常识别
      const html = `<div data-w-e-type="audio" data-w-e-is-void data-w-e-type="audio" data-w-e-width="${width}" data-w-e-height="${height}" data-src="${src}" data-width="${width}" data-height="${height}">
                            <audio poster="" controls style="width:${width};height:${height};max-width:100%" src="${src}"><source src="${src}" type="audio/mpeg"/></audio>
                    </div>`;
      return html;
    }
    
    const audioToHtmlConf = {
      type: "audio",
      elemToHtml: audioElemtToHtml
    };
    
    export { audioToHtmlConf };
    
    

    6. 解析新元素 HTML 到编辑器 --- parse-elem-html.js

    import { IDomEditor, SlateDescendant, SlateElement } from "@wangeditor/editor";
    
    function parseAudioElementHtml(domElem,children,editor) {
      const src = domElem.getAttribute("data-src"); // 这些就是elem-html.ts自定义扩展存放的地方,可以根据需要自行扩展
      const height = domElem.getAttribute("data-height");
      const width = domElem.getAttribute("data-width");
      const myAudio = {
        // 这里的信息要和custom-types.ts一致
        type: "audio",
        src,
        width,
        height,
        children: [{ text: "" }]
      };
      return myAudio;
    }
    const parseAudioHtmlConf = {
      selector: 'div[data-w-e-type="audio"]', // 这个就是elem-html.ts中第一个div里包含的信息
      parseElemHtml: parseAudioElementHtml
    };
    
    export { parseAudioHtmlConf };
    
    

    7. 注册插件到 wangEditor --- index.js

    import { IModuleConf } from "@wangeditor/editor";
    import { Boot } from "@wangeditor/editor";
    import { renderAudioConf } from "./render-elem";
    import { audioToHtmlConf } from "./elem-to-html";
    import { parseAudioHtmlConf } from "./parse-elem-html";
    import withAudio from "./plugin";
    
    function module() {
      Boot.registerRenderElem(renderAudioConf);
      Boot.registerElemToHtml(audioToHtmlConf);
      Boot.registerParseElemHtml(parseAudioHtmlConf);
      Boot.registerPlugin(withAudio);
    }
    
    export default module;
    
    

    8. 在index.vue中导入并使用

    import module from '../../../plugins/module'
    
    
    onCreated(editor) {
      module()
    }
    

    以上就是今天的全部内容啦。遇到问题就留言。

    相关文章

      网友评论

          本文标题:vue使用wangEditor自定义上传音频的新功能

          本文链接:https://www.haomeiwen.com/subject/nulmgdtx.html