zoukankan      html  css  js  c++  java
  • 工作笔记四——vueJS在移动端使用富文本编辑器

    本文主要介绍vue移动端使用富文本编辑器的使用及常见问题处理。参考组件vue-html5-editor。

    本例主要基于vue-cli脚手架创建。更多vue相关应用请参考:https://github.com/JerryYuanJ/a-vue-app-template

    1.项目创建与初始化

    创建一个vue-cli项目,建议在安装的时候不要使用ESLINT做代码检查,练习的项目不需要这种检查机制,会很浪费时间。还有一些自动化测试的插件也最好不要装,影响效率。

    在安装好脚手架的依赖后,要执行 npm install vue-html5-editor -S 来安装这个富文本插件,由于这个富文本插件的图标是依赖font-awesome.css的,所以要npm install font-awesome.css 安装这个css然后在main.js中引入这个css   import   "font-awesome/css/font-awesome.css"

    2.使用vue-html5-editor富文本编辑器

               新建一个common文件夹用于存放我们的工具类js文件,然后将下面的代码copy进去:

    /**
     * author: Joker
     * creationDate: 2018/1/22
     * usage:
     */
    import Vue from 'vue'
    import VueHtml5Editor from 'vue-html5-editor'
    export default function () {
      let opt = {
        // 全局组件名称,使用new VueHtml5Editor(options)时该选项无效
        name: "vue-html5-editor",
        // 是否显示模块名称,开启的话会在工具栏的图标后台直接显示名称
        showModuleName: true,
        // 自定义各个图标的class,默认使用的是font-awesome提供的图标
        icons: {
          text: "fa fa-pencil",
          color: "fa fa-paint-brush",
          font: "fa fa-font",
          align: "fa fa-align-justify",
          list: "fa fa-list",
          link: "fa fa-chain",
          unlink: "fa fa-chain-broken",
          tabulation: "fa fa-table",
          image: "fa fa-file-image-o",
          hr: "fa fa-minus",
          eraser: "fa fa-eraser",
          undo: "fa-undo fa",
          "full-screen": "fa fa-arrows-alt",
          info: "fa fa-info",
        },
        // 配置图片模块
        image: {
          // 文件最大体积,单位字节  
          sizeLimit: 512 * 1024 * 10,
          // 上传参数,默认把图片转为base64而不上传
          // upload config,default null and convert image to base64
          upload: {
            url: null,
            headers: {},
            params: {},
            fieldName: {}
          },
          // 压缩参数,默认使用localResizeIMG进行压缩,设置为null禁止压缩
          // width和height是文件的最大宽高
          compress: {
             600,
            height: 600,
            quality: 80
          },
          // 响应数据处理,最终返回图片链接
          uploadHandler(responseText){
            //default accept json data like  {ok:false,msg:"unexpected"} or {ok:true,data:"image url"}
            var json = JSON.parse(responseText);
            console.info(json);
            if (!json.ok) {
              alert(json.msg)
            } else {
              return json.data
            }
          }
        },
        // 语言,内建的有英文(en-us)和中文(zh-cn)
        language: "zh-cn",
        // 自定义语言
        i18n: {
          "zh-cn": {
            "align": "对齐方式",
            "image": "图片",
            "list": "列表",
            "link": "链接",
            "unlink": "去除链接",
            "table": "表格",
            "font": "文字",
            "full screen": "全屏",
            "text": "排版",
            "eraser": "格式清除",
            "info": "关于",
            "color": "颜色",
            "please enter a url": "请输入地址",
            "create link": "创建链接",
            "bold": "加粗",
            "italic": "倾斜",
            "underline": "下划线",
            "strike through": "删除线",
            "subscript": "上标",
            "superscript": "下标",
            "heading": "标题",
            "font name": "字体",
            "font size": "文字大小",
            "left justify": "左对齐",
            "center justify": "居中",
            "right justify": "右对齐",
            "ordered list": "有序列表",
            "unordered list": "无序列表",
            "fore color": "前景色",
            "background color": "背景色",
            "row count": "行数",
            "column count": "列数",
            "save": "确定",
            "upload": "上传",
            "progress": "进度",
            "unknown": "未知",
            "please wait": "请稍等",
            "error": "错误",
            "abort": "中断",
            "reset": "重置"
          }
        },
        // 隐藏不想要显示出来的模块
        hiddenModules: [],
        // 自定义要显示的模块,并控制顺序
        visibleModules: [
          "text",
          "color",
          "font",
          "align",
          "list",
          "link",
          "unlink",
          "tabulation",
          "image",
          "hr",
          "eraser",
          "undo",
          "full-screen",
          "info",
        ],
        // 扩展模块,具体可以参考examples或查看源码
        // extended modules
        modules: {
          //omit,reference to source code of build-in modules
        }
      };
      Vue.use(VueHtml5Editor, opt);
    }
    

    接着在main.js中引入这个初始化的函数:

    import initRichText from './common/initHTMLEditor';  
    initRichText();

    准备工作已经完成了,我们可以使用这个组件了:

        <template>  
          <div class="content">  
            <vue-html5-editor :content="content" :height="400"  
                              @change="updateData"></vue-html5-editor>  
          
          </div>  
        </template>  
        <style scoped>  
          
        </style>  
        <script>  
          export default {  
            data(){  
              return {content: '请输入文章内容'}  
            },  
            methods: {  
              updateData(e = ''){  
                this.content = e;  
                console.info(e);  
              }  
            }  
          }  
        </script>  

    这个height属性是设置内容区的高度,content是内容区的数据内容,@change事件是内容区的监听事件,会在发生变化时触发,该函数接收一个参数,表示当前编辑器中的内容。运行结果如下(这里对图片的操作是转成base64的字符串):


     

    3.常见问题解决

    a.自定义工具栏的模块

    如果不想要显示这么多的工具,则只要配置visibleModules即可:

    b.工具栏的样式修改

    在移动端我们通常希望工具栏可以固定不动,并且显示在页面最下方,这时候我们要修改该组件的核心js里面的样式代码:

    主要是将

        var template$9 = "<div class="vue-html5-editor" :class="{'full-screen':fullScreen}".......省略很长的代码....)  

        __$styleInject(`.vue-html5-editor,.vue-html5-editor *{box-sizing:border-box}.....省略很长的代码......)  

    分别用下面两个代替:

    1.组件的字符串模板

        var  temlate$9=`  
         <div class="vue-html5-editor" :class="{'full-screen':fullScreen}" :style="{'z-index':zIndex}">  
               
             <div class="content" ref="content" :style="contentStyle" contenteditable  
                  @click="toggleDashboard(dashboard)"></div>  
                    
             <div class="toolbar" :style="{'z-index':zIndex+1}" ref="toolbar">  
               <ul>  
                 <template v-for="module in modules">  
                   <li :title="locale[module.i18n]" @click="activeModule(module)"><span class="icon"  
                                                                                        :class="module.icon"></span>  
                     <template v-if="showModuleName === undefined ? defaultShowModuleName : showModuleName">  
                        {{locale[module.i18n]}}   
                     </template>  
                   </li>  
                 </template>  
               </ul>  
               <div class="dashboard" v-show="dashboard" ref="dashboard">  
                 <keep-alive>  
                   <div v-show="dashboard" :is="dashboard"></div>  
                 </keep-alive>  
               </div>  
             </div>  
               
           </div>  
         `  

    和  2.主要的样式字符串

     `
      .vue-html5-editor, .vue-html5-editor * {
        box-sizing: border-box
      }
    
      .vue-html5-editor {
        font-size: 14px;
        line-height: 1.5;
        background-color: #fff;
        color: #333;
        border: 1px solid #ddd;
        text-align: left;
        border-radius: 5px;
        overflow: hidden
      }
    
      .vue-html5-editor.full-screen {
        position: fixed !important;
        top: 0 !important;
        left: 0 !important;
        bottom: 0 !important;
        right: 0 !important;
        border-radius: 0
      }
    
      .vue-html5-editor > .toolbar {
        position: relative;
        background-color: inherit;
        border-top:1px solid #ccc
      }
    
      .vue-html5-editor > .toolbar > ul {
        list-style: none;
        padding: 0;
        margin: 0;
        border-bottom: 1px solid #ddd
      }
    
      .vue-html5-editor > .toolbar > ul > li {
        display: inline-block;
        cursor: pointer;
        text-align: center;
        line-height: 36px;
        padding: 0 10px
      }
    
      .vue-html5-editor > .toolbar > ul > li .icon {
        height: 16px;
         16px;
        display: inline-block;
        vertical-align: middle
      }
    
      .vue-html5-editor > .toolbar > .dashboard {
        background-color: inherit;
        border-top: 1px solid #ddd;
        padding: 10px;
        position: absolute;
        bottom: 100%;
        left: 0;
        right: 0;
        overflow: auto
      }
    
      .vue-html5-editor > .toolbar > .dashboard input[type=text], .vue-html5-editor > .toolbar > .dashboard input[type=number], .vue-html5-editor > .toolbar > .dashboard select {
        padding: 6px 12px;
        color: inherit;
        background-color: transparent;
        border: 1px solid #ddd;
        border-radius: 5px
      }
    
      .vue-html5-editor > .toolbar > .dashboard input[type=text]:hover, .vue-html5-editor > .toolbar > .dashboard input[type=number]:hover, .vue-html5-editor > .toolbar > .dashboard select:hover {
        border-color: #bebebe
      }
    
      .vue-html5-editor > .toolbar > .dashboard input[type=text][disabled], .vue-html5-editor > .toolbar > .dashboard input[type=text][readonly], .vue-html5-editor > .toolbar > .dashboard input[type=number][disabled], .vue-html5-editor > .toolbar > .dashboard input[type=number][readonly], .vue-html5-editor > .toolbar > .dashboard select[disabled], .vue-html5-editor > .toolbar > .dashboard select[readonly] {
        background-color: #eee;
        opacity: 1
      }
    
      .vue-html5-editor > .toolbar > .dashboard input[type=text][disabled], .vue-html5-editor > .toolbar > .dashboard input[type=number][disabled], .vue-html5-editor > .toolbar > .dashboard select[disabled] {
        cursor: not-allowed
      }
    
      .vue-html5-editor > .toolbar > .dashboard button {
        color: inherit;
        background-color: inherit;
        padding: 6px 12px;
        white-space: nowrap;
        vertical-align: middle;
        cursor: pointer;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        border: 1px solid #ddd;
        border-radius: 5px;
        margin-right: 4px;
        margin-bottom: 4px
      }
    
      .vue-html5-editor > .toolbar > .dashboard button:hover {
        border-color: #bebebe
      }
    
      .vue-html5-editor > .toolbar > .dashboard button[disabled] {
        cursor: not-allowed;
        opacity: .68
      }
    
      .vue-html5-editor > .toolbar > .dashboard button:last-child {
        margin-right: 0
      }
    
      .vue-html5-editor > .toolbar > .dashboard label {
        font-weight: bolder
      }
    
      .vue-html5-editor > .content {
        overflow: scroll;
        padding: 10px;
        max-height:500px
      }
    
      .vue-html5-editor > .content:focus {
        outline: 0
      }`

    修改的地方不多,请参照具体的样式自己配置自定义的模板。

    我这个修改完以后显示如下。而且输入很长的数据后,只是内容区变成上下滚动的,工具栏不动。

    c.移动端图片上传的处理

    这里我没有配置服务端的上传文件的接口,所以我就直接将图片转成base64的来处理。但是这样会有问题,在PC端图片是可以修改大小的,但是在移动端上传的图片上是原图,也就是很大,图文混排的时候非常不好看,也不好编辑。这时候我做的处理挺投机取巧的,但是也是可行的方法。我们知道change函数是在内容发生变化时触发的,这时候我们只要将获取到的内容做一下修改即可,看代码:

        updateData(e = ''){  
           let c1 = e.replace(/<img width="100%"/g, '<img');  
           let c2 = c1.replace(/<img/g, '<img width="100%"');  
           this.content = c2;  
         }  

    这样就OK了。

    看移动端的效果图:图片是按宽100%自适应缩放的。效果达到。

    本项目的git地址:https://github.com/JerryYuanJ/a-vue-app-template

    主要参考  /src/pages/tool/RichTextTest.vue (使用),/src/init-plugins.js(配置) 

    如果有bug欢迎指正,不胜感激;如果对您有帮助,给个star,谢谢~~

  • 相关阅读:
    【修订】为什么OO方法是有本质缺陷的?
    jQuery到底有什么好?
    qsort还是std::sort,一个排序引发的血案
    小小的吹一下集结号~
    谈谈学习与讨论风格:信任、质疑与交流
    职场中的我们,应该有多和谐?
    我到底是在干嘛?
    最近一些想法
    老饭新炒:object based、object oriented与弱类语言
    Flash性能分析与调试工具
  • 原文地址:https://www.cnblogs.com/jerryyj/p/9621555.html
Copyright © 2011-2022 走看看