原创 vue中使用百度ueditor,自定义图片上传
大前端

上一篇讲了vue与tinymce结合使用,点击查看,这一篇主要讲 vue使用ueditor


1、效果


新看下截图



图片上传


2、下载ueditor

下载地址:https://ueditor.baidu.com/website/download.html#ueditor

将下载下来的文件放到工程目录下的static目录下,如下图

3、安装 vue-ueditor-wrap

npm i vue-ueditor-wrap


4、封装模板

新建Ueditor.vue
<template>
  <div>
    <vue-ueditor-wrap v-model="content"
                      :config="myConfig"
                      @beforeInit="addCustomButtom"></vue-ueditor-wrap>
    <el-dialog title="图片上传"
               :visible.sync="dialogVisible"
               width="60%"
               :lock-scroll="true"
               :close-on-click-modal="false">
      <div class="image-upload-panel">
        <div class="image-item"
             v-for="(item,index) in imageList"
             :key="index">
          <img :src="item.url">
          <span class="image-del el-icon-close"
                @click="delImage(index)"></span>
        </div>
        <el-upload class="image-upload-btn"
                   :action="api.uploadImage"
                   name="upfile"
                   accept="image/png, image/jpeg"
                   with-credentials
                   :show-file-list="false"
                   :on-success="imageUploadSuccess"
                   :before-upload="imageBeforeUpload">
          <i class="el-icon-plus"></i>
        </el-upload>
      </div>
      <span slot="footer"
            class="dialog-footer">
        <el-button @click="()=>{this.dialogVisible=false}">取 消</el-button>
        <el-button type="primary"
                   @click="insertImage">确 定</el-button>
      </span>
    </el-dialog>
  </div>

</template>

<script>
import VueUeditorWrap from 'vue-ueditor-wrap'
let loading
export default {
  components: {
    VueUeditorWrap,
  },
  props: {
    value: {
      type: String,
      default: ''
    },
    myConfig: {
      type: Object,
      default: () => ({
        // 编辑器不自动被内容撑高
        autoHeightEnabled: false,
        // 初始容器高度
        initialFrameHeight: 400,
        // 初始容器宽度
        initialFrameWidth: '100%',
        // UEditor 资源文件的存放路径,如果你使用的是 vue-cli 生成的项目,通常不需要设置该选项,vue-ueditor-wrap 会自动处理常见的情况,如果需要特殊配置,参考下方的常见问题2
        UEDITOR_HOME_URL: '/static/plugin/UEditor/'
      }
      )
    }
  },
  data () {
    return {
      loading: undefined,
      api: {
        /**
         * 图片上传地址
         */
        uploadImage: "/api/imageUpload"
      },
      dialogVisible: false,
      imageList: [],
      editorHandler: null,
      content: this.value,
    }
  },
  methods: {
    //删除图片
    delImage (index) {
      let imageList = this.imageList;
      imageList.splice(index, 1);
    },
    //图片开始上传
    imageBeforeUpload () {
      this.loading = this.$loading({
        lock: true,
        text: '加载中......',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })
    },
    //图片上传成功
    imageUploadSuccess (response, file) {
      if (this.loading != undefined) {
        this.loading.close();
      }
      let imageList = this.imageList;
      let item = response.data;
      imageList.push(item);
      this.imageList = imageList;
    },
    insertImage () {
      let imageList = this.imageList;
      let imageHtml = "";
      (imageList || []).map(item => {
        imageHtml = imageHtml + "<p><img src=\"" + item.url + "\"/></p>";
      })
      if (imageHtml != "") {
        this.editorHandler.execCommand('inserthtml', imageHtml);
      }
      this.dialogVisible = false;
    },
    addCustomButtom (editorId) {
      let _this = this;
      window.UE.registerUI('test-button', function (editor, uiName) {
        // 注册按钮执行时的 command 命令,使用命令默认就会带有回退操作
        editor.registerCommand(uiName, {
          execCommand: () => {
            _this.imageList = [];
            _this.dialogVisible = true;
            _this.editorHandler = editor;
            //editor.execCommand('inserthtml', `<span>这是一段由自定义按钮添加的文字</span>`)
          }
        })

        // 创建一个 button
        var btn = new window.UE.ui.Button({
          // 按钮的名字
          name: uiName,
          // 提示
          title: '鼠标悬停时的提示文字',
          // 需要添加的额外样式,可指定 icon 图标,图标路径参考常见问题 2
          cssRules: "background-position: -380px 0;",
          // 点击时执行的命令
          onclick: function () {
            // 这里可以不用执行命令,做你自己的操作也可
            editor.execCommand(uiName)
          }
        })

        // 当点到编辑内容上时,按钮要做的状态反射
        editor.addListener('selectionchange', function () {
          var state = editor.queryCommandState(uiName)
          if (state === -1) {
            btn.setDisabled(true)
            btn.setChecked(false)
          } else {
            btn.setDisabled(false)
            btn.setChecked(state)
          }
        })

        // 因为你是添加 button,所以需要返回这个 button
        return btn
      }, 47 /* 指定添加到工具栏上的哪个位置,默认时追加到最后 */, editorId /* 指定这个 UI 是哪个编辑器实例上的,默认是页面上所有的编辑器都会添加这个按钮 */)
    }
  },
  watch: {
    value (newValue) {
      this.content = newValue
    },
    content (newValue) {
      this.$emit('input', newValue)
    }
  }
}
</script>
<style scoped>
.image-upload-panel {
  overflow: hidden;
}
.image-upload-btn {
  float: left;
  width: 150px;
  height: 150px;
  border: 1px dashed #c0ccda;
  border-radius: 6px;
  line-height: 150px;
  text-align: center;
}

.image-upload-btn div {
  width: 150px;
}

.image-upload-btn i {
  font-size: 30px;
  font-weight: 400;
}
.image-item {
  float: left;
  width: 150px;
  height: 150px;
  background: #ddd;
  margin-right: 10px;
  border-radius: 6px;
  position: relative;
}
.image-item img {
  max-width: 150px;
  border-radius: 6px;
}
.image-del {
  position: absolute;
  top: 0px;
  right: 0px;
  color: red;
  font-weight: bold;
  font-size: 20px;
  cursor: pointer;
}
</style>

注意,这里使用了 element-ui 请自行安装

图片上传地址在 api.uploadImage 中定义,可自行修改,图片上传返回json为
{"code":200,"data":{"url":"http://www.xxxx.com/images/xxxx.jpg"},"info":"请求成功"}
可自行修改,同时需要修改 imageUploadSuccess  方法,这里图片上传大家可自行扩展,主要用到了element-ui 的一些组件


5、使用模板

新建Editor.vue
<template>
  <div>
    <uEditor v-model="content"></uEditor>
  </div>

</template>

<script>
import uEditor from './common/Ueditor'
export default {
  components: {
    uEditor,
  },
  data () {
    return {
      content: "",
    }
  },
  methods: {

  }
}
</script>
content就是编辑的内容,可以实现数据双向绑定