在Vue.js中使用Quill富文本编辑器自定义块级元素

再一次开发中,我试图在vue.js搭建的网站中引入富文本编辑器,我是用了vue2-editor. 这个库将开源富文本编辑器Quill包装进了vue.js的组件中。从易用性角度来说,非常令人满意。

不过,在我们的设计中,我们尝试使用富文本实现一个问卷系统,例如对于选择题,我们希望把一个包含了四个选项的“不可分割块级元素”作为一个整体,像插入图片一样插到富文本编辑器中。

实际上,插入块级元素本身是有先例可循的,比如视频插入模块,可以再富文本编辑器中插入一个iframe块。其代码可在github上面看到——

import { BlockEmbed } from '../blots/block';
import Link from './link';

const ATTRIBUTES = ['height', 'width'];

class Video extends BlockEmbed {
  static create(value) {
    const node = super.create(value);
    node.setAttribute('frameborder', '0');
    node.setAttribute('allowfullscreen', true);
    node.setAttribute('src', this.sanitize(value));
    return node;
  }

  static formats(domNode) {
    return ATTRIBUTES.reduce((formats, attribute) => {
      if (domNode.hasAttribute(attribute)) {
        formats[attribute] = domNode.getAttribute(attribute);
      }
      return formats;
    }, {});
  }

  static sanitize(url) {
    return Link.sanitize(url); // eslint-disable-line import/no-named-as-default-member
  }

  static value(domNode) {
    return domNode.getAttribute('src');
  }

  format(name, value) {
    if (ATTRIBUTES.indexOf(name) > -1) {
      if (value) {
        this.domNode.setAttribute(name, value);
      } else {
        this.domNode.removeAttribute(name);
      }
    } else {
      super.format(name, value);
    }
  }

  html() {
    const { video } = this.value();
    return `<a href="{video}">{video}</a>`;
  }
}
Video.blotName = 'video';
Video.className = 'ql-video';
Video.tagName = 'IFRAME';

export default Video;

仿照上面的代码,我们实现我们自己的题目添加控件——

import {Quill} from 'vue2-editor';

const ATTRIBUTES = [
  'alt',
  'height',
  'width'
];

var Embed = Quill.import('blots/embed');
class SelectProblemEmbeder extends Embed {
	static create(values) {
		let node = super.create();
		let class_id = values.class_id;
		let problem_id = values.problem_id;
		let choices = values.choices;
		let context = global.problemEmbederContext;
		node.setAttribute('id','problem-'+class_id+'-'+problem_id);
		node.setAttribute('pid',problem_id);
		node.setAttribute('cid',class_id);
		let container = document.createElement('div');
		for (var item in choices) {
			let choice_node = document.createElement('div');
			choice_node.setAttribute('class','select-option');
			choice_node.setAttribute('index',item);
			choice_node.setAttribute('content',choices[item]);
			choice_node.appendChild(document.createTextNode(item + '.'+ choices[item]));
			container.appendChild(choice_node);
		}
		node.appendChild(container);
		let panel = document.createElement('div');
		let panel_answer = document.createElement('span');
		panel_answer.appendChild(document.createTextNode('[编辑]'));
		panel_answer.setAttribute('class','correct-answer-button');
		panel_answer.onclick = function() {
			context.handleShowAnswerDialog(problem_id);
		};
		let panel_analyze = document.createElement('span');
		panel_analyze.appendChild(document.createTextNode('[情况分析]'));
		panel_analyze.setAttribute('class','analyze-button');
		panel.appendChild(panel_answer);
		panel.appendChild(panel_analyze);
		node.appendChild(panel);
		return node;
	}

	static value(node) {
		let res = {
			class_id:node.getAttribute('cid'),
			problem_id:node.getAttribute('pid'),
			choices: {
			}
		};
		let container = node.children[0].children[0];
		for (let item of container.children) {
			res.choices[item.getAttribute('index')] = item.getAttribute('content');
		}
		return res;
	}

	static formats(domNode) {
		console.log("CALL formats()",domNode);
		return domNode.getAttribute('id');
	}

	static sanitize(url) {
		console.log("CALL sanitize()",domNode);
	}

	format(name, value) {
		if (ATTRIBUTES.indexOf(name) > -1) {
			if (value) {
				this.domNode.setAttribute(name, value);
			} else {
				this.domNode.removeAttribute(name);
			}
		} else {
			super.format(name, value);
		}
	}
}

SelectProblemEmbeder.blotName = 'select-problem';
SelectProblemEmbeder.className = 'smart-area';
SelectProblemEmbeder.tagName = 'div';

export default SelectProblemEmbeder;

这是我们设计的独立功能模块,通过命令 Quill.register(SelectProblemEmbeder); 进行引入。在保存时,我们需要同时保存富文本的HTML信息,以及DELTAS信息,HTML信息可以用于渲染内容,可以通过vue2-editor暴露在外的content获取,而DELTAS信息可以用于保存富文本编辑的状态,供后续恢复。可以使用let quill_deltas = JSON.stringify(this.quill.getContents());得到。其中,this.quill 是通过$refs获取到的模块内部信息。恢复时,我们读取上一次编辑的DELTAS信息,使用this.quill.setContents(quill_deltas); 即可回复之前的状态。

《在Vue.js中使用Quill富文本编辑器自定义块级元素》有2个想法

  1. 你好 我有个问题想咨询下,请问你这篇文章是讲的如何把一个dom片段插到富文本里吗?比如说我我有一个vue卡片组件,我想直接把这个卡片组件插到富文本里不知道行不行

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据