vue 安装ueditor

1 篇文章 0 订阅

1.安装vue-ueditor-wrap

npm install vue-ueditor-wrap

2.下载最新编译的 UEditor放到public文件, 我没有下载直接在gitee上粘贴过去的
3.在src>components 创建vue-ueditor-wrap.vue

<template>
  <div>
    <div ref="script" :name="name" type="text/plain"></div>
  </div>
</template>

<script>
import LoadEvent from '../utils/Event.js';
import Debounce from '../utils/Debounce.js';

export default {
  name: 'VueUeditorWrap',
  data () {
    return {
      status: 0,
      initValue: '',
      defaultConfig: {
        // VUE CLI 3 会添加 process.env.BASE_URL 的环境变量,而 VUE CLI 2 没有,所以借此设置 UEDITOR_HOME_URL,能涵盖大部分 Vue 开发者的使用场景
        UEDITOR_HOME_URL: process.env.BASE_URL ? process.env.BASE_URL + 'UEditor/' : '/static/UEditor/',
        enableAutoSave: false
      }
    };
  },
  props: {
    // v-model 实现方式
    mode: {
      type: String,
      default: 'observer',
      validator: function (value) {
        // 1. observer 借助 MutationObserver API https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
        // 2. listener 借助 UEditor 的 contentChange 事件 https://ueditor.baidu.com/doc/#UE.Editor:contentChange
        return ['observer', 'listener'].indexOf(value) !== -1;
      }
    },
    value: {
      type: String,
      default: ''
    },
    config: {
      type: Object,
      default: function () {
        return {};
      }
    },
    init: {
      type: Function,
      default: function () {
        return () => {};
      }
    },
    destroy: {
      type: Boolean,
      default: false
    },
    name: {
      type: String,
      default: ''
    },
    observerDebounceTime: {
      type: Number,
      default: 50,
      validator: function (value) {
        return value >= 20;
      }
    },
    observerOptions: {
      type: Object,
      default: function () {
        // https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit
        return {
          attributes: true, // 是否监听 DOM 元素的属性变化
          attributeFilter: ['src', 'style', 'type', 'name'], // 只有在该数组中的属性值的变化才会监听
          characterData: true, // 是否监听文本节点
          childList: true, // 是否监听子节点
          subtree: true // 是否监听后代元素
        };
      }
    },
    // 本组件提供对普通 Vue 项目和 Nuxt 项目开箱即用的支持,但如果是自己搭建的 Vue SSR 项目,可能需要自行区分是客户端还是服务端环境并跳过环境检测,直接初始化
    forceInit: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    mixedConfig () {
      return Object.assign({}, this.defaultConfig, this.config);
    }
  },
  methods: {
    // 添加自定义按钮(自定义按钮,自定义弹窗等操作从 2.2.0 版本开始不再考虑直接集成,这会使得组件和 UEditor 过度耦合,但为了兼容一些老版用户的写法,这个方法依然保留)
    registerButton ({ name, icon, tip, handler, index, UE = window.UE }) {
      UE.registerUI(name, (editor, name) => {
        editor.registerCommand(name, {
          execCommand: () => {
            handler(editor, name);
          }
        });
        const btn = new UE.ui.Button({
          name,
          title: tip,
          cssRules: `background-image: url(${icon}) !important;background-size: cover;`,
          onclick () {
            editor.execCommand(name);
          }
        });
        editor.addListener('selectionchange', () => {
          const state = editor.queryCommandState(name);
          if (state === -1) {
            btn.setDisabled(true);
            btn.setChecked(false);
          } else {
            btn.setDisabled(false);
            btn.setChecked(state);
          }
        });
        return btn;
      }, index, this.id);
    },
    // 实例化编辑器
    _initEditor () {
      this.$refs.script.id = this.id = 'editor_' + Math.random().toString(16).slice(-6); // 这么做是为了支持 Vue SSR,因为如果把 id 属性放在 data 里会导致服务端和客户端分别计算该属性的值,而造成 id 不匹配无法初始化的 BUG
      this.init();
      this.$emit('before-init', this.id, this.mixedConfig);
      this.$emit('beforeInit', this.id, this.mixedConfig); // 虽然这个驼峰的写法会导致使用 DOM 模版时出现监听事件自动转小写的 BUG,但如果经过编译的话并不会有这个问题,为了兼容历史版本,不做删除,参考 https://vuejs.org/v2/guide/components-custom-events.html#Event-Names
      this.editor = window.UE.getEditor(this.id, this.mixedConfig);
      this.editor.addListener('ready', () => {
        if (this.status === 2) { // 使用 keep-alive 组件会出现这种情况
          this.editor.setContent(this.value);
        } else {
          this.status = 2;
          this.$emit('ready', this.editor);
          if (this.initValue) {
            this.editor.setContent(this.initValue);
          }
        }
        if (this.mode === 'observer' && window.MutationObserver) {
          this._observerChangeListener();
        } else {
          this._normalChangeListener();
        }
      });
    },
    // 检测依赖,确保 UEditor 资源文件已加载完毕
    _checkDependencies () {
      return new Promise((resolve, reject) => {
        // 判断ueditor.config.js和ueditor.all.js是否均已加载(仅加载完ueditor.config.js时UE对象和UEDITOR_CONFIG对象存在,仅加载完ueditor.all.js时UEDITOR_CONFIG对象存在,但为空对象)
        let scriptsLoaded = !!window.UE && !!window.UEDITOR_CONFIG && Object.keys(window.UEDITOR_CONFIG).length !== 0 && !!window.UE.getEditor;
        if (scriptsLoaded) {
          resolve();
        } else if (window['$loadEnv']) { // 利用订阅发布,确保同时渲染多个组件时,不会重复创建script标签
          window['$loadEnv'].on('scriptsLoaded', () => {
            resolve();
          });
        } else {
          window['$loadEnv'] = new LoadEvent();
          // 如果在其他地方只引用ueditor.all.min.js,在加载ueditor.config.js之后仍需要重新加载ueditor.all.min.js,所以必须确保ueditor.config.js已加载
          this._loadConfig().then(() => this._loadCore()).then(() => {
            resolve();
            window['$loadEnv'].emit('scriptsLoaded');
          });
        }
      });
    },
    _loadConfig () {
      return new Promise((resolve, reject) => {
        if (window.UE && window.UEDITOR_CONFIG && Object.keys(window.UEDITOR_CONFIG).length !== 0) {
          resolve();
          return;
        }
        let configScript = document.createElement('script');
        configScript.type = 'text/javascript';
        configScript.src = this.mixedConfig.UEDITOR_HOME_URL + 'ueditor.config.js';
        document.getElementsByTagName('head')[0].appendChild(configScript);
        configScript.onload = function () {
          if (window.UE && window.UEDITOR_CONFIG && Object.keys(window.UEDITOR_CONFIG).length !== 0) {
            resolve();
          } else {
            console.error('加载ueditor.config.js失败,请检查您的配置地址UEDITOR_HOME_URL填写是否正确!\n', configScript.src);
          }
        };
      });
    },
    _loadCore () {
      return new Promise((resolve, reject) => {
        if (window.UE && window.UE.getEditor) {
          resolve();
          return;
        }
        let coreScript = document.createElement('script');
        coreScript.type = 'text/javascript';
        coreScript.src = this.mixedConfig.UEDITOR_HOME_URL + 'ueditor.all.min.js';
        document.getElementsByTagName('head')[0].appendChild(coreScript);
        coreScript.onload = function () {
          if (window.UE && window.UE.getEditor) {
            resolve();
          } else {
            console.error('加载ueditor.all.min.js失败,请检查您的配置地址UEDITOR_HOME_URL填写是否正确!\n', coreScript.src);
          }
        };
      });
    },
    // 设置内容
    _setContent (value) {
      value === this.editor.getContent() || this.editor.setContent(value);
    },
    contentChangeHandler () {
      this.$emit('input', this.editor.getContent());
    },
    // 基于 UEditor 的 contentChange 事件
    _normalChangeListener () {
      this.editor.addListener('contentChange', this.contentChangeHandler);
    },
    // 基于 MutationObserver API
    _observerChangeListener () {
      const changeHandle = (mutationsList) => {
        if (this.editor.document.getElementById('baidu_pastebin')) {
          return;
        }
        this.$emit('input', this.editor.getContent());
      };
      // 函数防抖
      this.observer = new MutationObserver(Debounce(changeHandle, this.observerDebounceTime));
      this.observer.observe(this.editor.body, this.observerOptions);
    }
  },
  deactivated () {
    this.editor && this.editor.removeListener('contentChange', this.contentChangeHandler);
    this.observer && this.observer.disconnect();
  },
  beforeDestroy () {
    if (this.destroy && this.editor && this.editor.destroy) {
      this.editor.destroy();
    }
    if (this.observer && this.observer.disconnect) {
      this.observer.disconnect();
    }
  },
  // v-model语法糖实现
  watch: {
    value: {
      handler (value) {
        // 修复值为空无法双向绑定的问题
        if (value === null) {
          value = '';
        }
        // 0: 尚未初始化 1: 开始初始化但尚未ready 2 初始化完成并已ready
        switch (this.status) {
          case 0:
            this.status = 1;
            this.initValue = value;
            // 判断执行环境是服务端还是客户端,这里的 process.client 是 Nuxt 添加的环境变量
            (this.forceInit || (typeof process !== 'undefined' && process.client) || typeof window !== 'undefined') && this._checkDependencies().then(() => {
              this.$refs.script ? this._initEditor() : this.$nextTick(() => this._initEditor());
            });
            break;
          case 1:
            this.initValue = value;
            break;
          case 2:
            this._setContent(value);
            break;
          default:
            break;
        }
      },
      immediate: true
    }
  }
};
</script>

4.在src>utils创建Event.js

// 一个简单的事件订阅发布的实现,取代原生Event对象,提升IE下的兼容性
// 富文本
function LoadEvent() {
    this.listeners = {};
    this.on = function(eventName, callback) {
        if (this.listeners[eventName] === undefined) {
            this.listeners[eventName] = [];
        }
        this.listeners[eventName].push(callback);
    };
    this.emit = function(eventName) {
        this.listeners[eventName] && this.listeners[eventName].forEach(callback => callback());
    };
}
export default LoadEvent;

5.在src>utils创建Debounce.js

/**
 * 一个简单的函数防抖
 * 富文本
 * @param {Function} fun 需要限制执行频率的函数
 * @param {Number} delay 延迟时间,这段时间过后,才可触发第二次
 */
export default function(fun, delay) {
    var timer = null;
    var debounced = function() {
        var ctx = this;
        var args = arguments;
        if (timer) clearTimeout(timer);
        timer = setTimeout(function() {
            fun.apply(ctx, args);
        }, delay);
    };
    return debounced;
}

6.调用插件

<template>
  <div class="contact">
  	<vue-ueditor-wrap v-model="msg" :config="myConfig" @before-init="addCustomButtom"></vue-ueditor-wrap>
  </div>
</template>
<script>
	import VueUeditorWrap from "vue-ueditor-wrap"; // ES6 Module
	export default {
		data() {
      		return {
      			// 富文本
		        msg: '111',
		        myConfig: {
		          autoHeightEnabled: false,
		          initialFrameHeight: 200,
		          initialFrameWidth: '100%',
		          UEDITOR_HOME_URL: '/UEditor/',
		          serverUrl: 'http://35.201.165.105:8000/controller.php',
		        },
      		}
      	},
      	// 富文本
      // 添加自定义按钮
      addCustomButtom (editorId) {
        window.UE.registerUI('test-button', function (editor, uiName) {
          // 注册按钮执行时的 command 命令,使用命令默认就会带有回退操作
          editor.registerCommand(uiName, {
            execCommand: function () {
              editor.execCommand('inserthtml', `<span>这是一段由自定义按钮添加的文字</span>`);
            }
          });

          // 创建一个 button
          var btn = new window.UE.ui.Button({
            // 按钮的名字
            name: uiName,
            // 提示
            title: '鼠标悬停时的提示文字',
            // 需要添加的额外样式,可指定 icon 图标,图标路径参考常见问题 2
            cssRules: "background-image: url('/test-button.png') !important;background-size: cover;",
            // 点击时执行的命令
            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;
        }, 
        0 /* 指定添加到工具栏上的哪个位置,默认时追加到最后 */, 
        editorId /* 指定这个 UI 是哪个编辑器实例上的,默认是页面上所有的编辑器都会添加这个按钮 */
        );
      },
	}
</script>
//和element样式冲突
<style>
  .edui-default .edui-toolbar {
    /* // 原样式不变,增加: */
    line-height: initial !important;
  }

  .edui-default .edui-toolbar .edui-combox .edui-combox-body {
    /* // 原样式不变,增加: */
    line-height: initial !important;
  }
</style>

摘取自https://gitee.com/CrazyHat/vue-ueditor-wrap.git
如有侵权请私信,马上删除

Vue-ueditor 是一个基于 Vue.js 框架的富文本编辑器,它提供了丰富的功能和良好的用户体验。以下是关于 Vue-ueditor 的简要介绍: 1. 功能特点: * 支持多语言:Vue-ueditor 支持多种语言,包括中文,方便用户进行国际化。 * 丰富的功能:Vue-ueditor 提供了诸如图片上传、剪切板图片、实时保存、撤销/重做、富文本编辑等功能。 * 高度定制化:用户可以根据自己的需求对编辑器进行高度定制,例如调整样式、添加插件等。 * 易于集成:Vue-ueditor 提供了一组简单的 API,方便开发者将其集成到自己的 Vue.js 项目中。 2. 使用方法: * 首先,需要在项目中安装 Vue-ueditor。可以通过 npm 命令进行安装:`npm install vueditor`。 * 在需要使用编辑器的 Vue 组件中,引入 Vue-ueditor 库,并使用它的 API 创建编辑器实例。 * 然后,可以通过调用编辑器的各种方法(如插入文本、图片等)来使用它的功能。 3. 示例代码: 以下是一个简单的示例代码,展示如何在 Vue 项目中使用 Vue-ueditor: ```vue <template> <div> <vueditor :editor="editor" /> </div> </template> <script> import VueEditor from 'vueditor'; export default { data() { return { editor: null, // 编辑器实例对象 }; }, mounted() { this.editor = new VueEditor({ // 配置项 height: '500px', // 设置编辑器高度 lang: 'zh_CN', // 设置语言为中文 imagePath: '/ueditor/php/upload/', // 设置图片上传地址 }); this.editor.ready(() => { // 编辑器加载完成后的操作,例如初始化内容等 }); this.editor.render(); // 渲染编辑器到指定容器中 }, }; </script> ``` 以上是对 Vue-ueditor 的简要介绍,具体的使用方法和配置项可以参考官方文档。Vue-ueditor 是一个功能强大、易于集成的富文本编辑器,适合在各种场景下使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值