基础使用
vue-quill-editor入口
import _Quill from 'quill'
import quillEditor from './editor.vue'
const Quill = window.Quill || _Quill
const install = (Vue, globalOptions) => {
if (globalOptions) {
quillEditor.props.globalOptions.default = () => globalOptions
}
Vue.component(quillEditor.name, quillEditor)
}
const VueQuillEditor = { Quill, quillEditor, install }
export default VueQuillEditor
export { Quill, quillEditor, install }
由vue-quill-editor入口可以知道,可以直接通过Vue.use直接注册,便可使用组件,如果不希望全局注册,也可以接受导出的组件quillEditor来使用
项目中的使用
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
import { quillEditor, Quill } from 'vue-quill-editor';
export { Quill };
export default quillEditor;
导出组件和Quill,Quill方便扩展使用
组件的使用
<template>
<div>
<Quill-editor
ref="myQuillEditor"
:value="value"
:options="editorOption"
@input="handleInput"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@ready="onEditorReady($event)"
@change="onEditorChange($event)"
/>
</div>
</template>
<script>
import QuillEditor from './vue-quill-editor';
export default {
components: {
QuillEditor
},
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
editorOption: {
placeholder: '输入文本...',
theme: 'snow',
modules: {
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线
['blockquote', 'code-block'], // 引用,代码块
[{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、2表示字体大小
[{ list: 'ordered' }, { list: 'bullet' }], // 列表
[{ script: 'sub' }, { script: 'super' }], // 上下标
[{ indent: '-1' }, { indent: '+1' }], // 缩进
[{ direction: 'rtl' }], // 文本方向
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 几级标题
[{ color: [] }, { background: [] }], // 字体颜色,字体背景颜色
[{ align: [] }], // 对齐方式
['clean'], // 清除字体样式
['image', 'video'] // 上传图片、上传视频
]
}
}
}
};
},
methods: {
handleInput(val) {
this.$emit('input', val);
},
onEditorBlur(quill) {
console.log('editor blur!', quill);
},
onEditorFocus(quill) {
console.log('editor focus!', quill);
},
onEditorReady(quill) {
console.log('editor ready!', quill);
},
onEditorChange({ quill, html, text }) {
console.log('editor change!', quill, html, text);
this.content = html;
}
}
};
</script>
源码流程分析
Quill注册方法
class Quill {
static register(path, target, overwrite = false) {
// 如果传入的path不是字符串,那么就根据传入的参数判断,转为对应的字符串参数继续调用
if (typeof path !== 'string') {
let name = path.attrName || path.blotName;
if (typeof name === 'string') {
// register(Blot | Attributor, overwrite)
this.register('formats/' + name, path, target);
} else {
Object.keys(path).forEach((key) => {
this.register(key, path[key], target);
});
}
} else {
if (this.imports[path] != null && !overwrite) {
debug.warn(`Overwriting ${path} with`, target);
}
// 将字符串和对应构造函数添加到Quill.import的对象中
this.imports[path] = target;
// 如果是以blots/或者以formats/开头,那么用Parchment注册
if ((path.startsWith('blots/') || path.startsWith('formats/')) &&
target.blotName !== 'abstract') {
Parchment.register(target);
// 如果以modules开头,并且有register且为函数,则调用注册
} else if (path.startsWith('modules') && typeof target.register === 'function') {
target.register();
}
}
}
}
Quill.imports = {
'delta' : Delta,
'parchment' : Parchment,
'core/module' : Module,
'core/theme' : Theme
};
获取Quill注册的构造函数
class Quill {
// 传入注册的名字即可获得构造函数
static import(name) {
if (this.imports[name] == null) {
debug.error(`Cannot import ${name}. Are you sure it was registered?`);
}
return this.imports[name];
}
}
主要的流程new Quill
class Quill {
constructor(container, options = {}) {
this.options = expandConfig(container, options); // 合并theme,Quill,传入的option参数
this.container = this.options.container;
if (this.container == null) {
return debug.error('Invalid Quill container', container);
}
if (this.options.debug) {
Quill.debug(this.options.debug);
}
let html = this.container.innerHTML.trim();
this.container.classList.add('ql-container');
this.container.innerHTML = '';
this.container.__quill = this;
this.root = this.addContainer('ql-editor');
this.root.classList.add('ql-blank');
this.root.setAttribute('data-gramm', false);
this.scrollingContainer = this.options.scrollingContainer || this.root;
this.emitter = new Emitter(); // 监听
this.scroll = Parchment.create(this.root, {
emitter: this.emitter,
whitelist: this.options.formats
});
this.editor = new Editor(this.scroll); // 实例化editor(编辑内容相关)
this.selection = new Selection(this.scroll, this.emitter); // 实例化selection(光标相关)
this.theme = new this.options.theme(this, this.options); // 实例化对应的theme(工具主题相关)
this.keyboard = this.theme.addModule('keyboard'); // 实例化keyboard(快捷键相关)
this.clipboard = this.theme.addModule('clipboard'); // 实例化clipboard(复制黏贴相关)
this.history = this.theme.addModule('history'); // 实例化history(前进撤销相关)
this.theme.init(); // 进入
this.emitter.on(Emitter.events.EDITOR_CHANGE, (type) => {
if (type === Emitter.events.TEXT_CHANGE) {
this.root.classList.toggle('ql-blank', this.editor.isBlank());
}
});
this.emitter.on(Emitter.events.SCROLL_UPDATE, (source, mutations) => {
let range = this.selection.lastRange;
let index = range && range.length === 0 ? range.index : undefined;
modify.call(this, () => {
return this.editor.update(null, mutations, index);
}, source);
});
let contents = this.clipboard.convert(`<div class='ql-editor' style="white-space: normal;">${html}<p><br></p></div>`);
this.setContents(contents);
this.history.clear();
if (this.options.placeholder) {
this.root.setAttribute('data-placeholder', this.options.placeholder);
}
if (this.options.readOnly) {
this.disable();
}
}
}
Theme构造函数的init(主要的是toolbar的实例化)
class Theme {
constructor(quill, options) {
this.quill = quill;
this.options = options;
this.modules = {};
}
init() {
// 实例化合并后的option中的modules
Object.keys(this.options.modules).forEach((name) => {
if (this.modules[name] == null) {
this.addModule(name);
}
});
}
addModule(name) {
let moduleClass = this.quill.constructor.import(`modules/${name}`);
// 传入模块实例化的参数为对应模块名的option(传入的modules和其他默认值合并后的结果)
this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {});
return this.modules[name];
}
}
class BaseTheme extends Theme {
addModule(name) {
let module = super.addModule(name);
if (name === 'toolbar') {
// toolbar实例化后进入
this.extendToolbar(module);
}
return module;
}
}
Toolbar构造函数
class Toolbar extends Module {
constructor(quill, options) {
super(quill, options);
if (Array.isArray(this.options.container)) {
let container = document.createElement('div');
// 根据module.toolbar里面的container,建立对应dom
addControls(container, this.options.container);
quill.container.parentNode.insertBefore(container, quill.container);
this.container = container;
} else if (typeof this.options.container === 'string') {
this.container = document.querySelector(this.options.container);
} else {
this.container = this.options.container;
}
if (!(this.container instanceof HTMLElement)) {
return debug.error('Container required for toolbar', this.options);
}
this.container.classList.add('ql-toolbar');
this.controls = [];
this.handlers = {};
// 将module.toolbar里面的handlers都赋值给this.handlers
Object.keys(this.options.handlers).forEach((format) => {
this.addHandler(format, this.options.handlers[format]);
});
// 给根据toolbar.container建立的dom元素都建立事件,触发对应的效果
[].forEach.call(this.container.querySelectorAll('button, select'), (input) => {
this.attach(input);
});
this.quill.on(Quill.events.EDITOR_CHANGE, (type, range) => {
if (type === Quill.events.SELECTION_CHANGE) {
this.update(range);
}
});
this.quill.on(Quill.events.SCROLL_OPTIMIZE, () => {
let [range, ] = this.quill.selection.getRange(); // quill.getSelection triggers update
this.update(range);
});
}
addHandler(format, handler) {
this.handlers[format] = handler;
}
attach(input) {
let format = [].find.call(input.classList, (className) => {
return className.indexOf('ql-') === 0;
});
if (!format) return;
format = format.slice('ql-'.length);
if (input.tagName === 'BUTTON') {
input.setAttribute('type', 'button');
}
if (this.handlers[format] == null) {
if (this.quill.scroll.whitelist != null && this.quill.scroll.whitelist[format] == null) {
debug.warn('ignoring attaching to disabled format', format, input);
return;
}
if (Parchment.query(format) == null) {
debug.warn('ignoring attaching to nonexistent format', format, input);
return;
}
}
// 确保触发的事件正确
let eventName = input.tagName === 'SELECT' ? 'change' : 'click';
input.addEventListener(eventName, (e) => {
let value;
if (input.tagName === 'SELECT') {
if (input.selectedIndex < 0) return;
let selected = input.options[input.selectedIndex];
if (selected.hasAttribute('selected')) {
value = false;
} else {
value = selected.value || false;
}
} else {
if (input.classList.contains('ql-active')) {
value = false;
} else {
value = input.value || !input.hasAttribute('value');
}
e.preventDefault();
}
this.quill.focus();
let [range, ] = this.quill.selection.getRange();
// 查看toolbar.handlers里面有没有跟触发事件的dom相同名字的,有的话就调用,没有的话就根据事先定义好的方法进行处理
if (this.handlers[format] != null) {
this.handlers[format].call(this, value);
// Parchment.query应该是找到Parchment注册的内容,之后按照找到的内容进行处理,待了解
} else if (Parchment.query(format).prototype instanceof Parchment.Embed) {
value = prompt(`Enter ${format}`);
if (!value) return;
this.quill.updateContents(new Delta()
.retain(range.index)
.delete(range.length)
.insert({ [format]: value })
, Quill.sources.USER);
} else {
this.quill.format(format, value, Quill.sources.USER);
}
this.update(range);
});
// TODO use weakmap
this.controls.push([format, input]);
}
update(range) {
let formats = range == null ? {} : this.quill.getFormat(range);
this.controls.forEach(function(pair) {
let [format, input] = pair;
if (input.tagName === 'SELECT') {
let option;
if (range == null) {
option = null;
} else if (formats[format] == null) {
option = input.querySelector('option[selected]');
} else if (!Array.isArray(formats[format])) {
let value = formats[format];
if (typeof value === 'string') {
value = value.replace(/\"/g, '\\"');
}
option = input.querySelector(`option[value="${value}"]`);
}
if (option == null) {
input.value = ''; // TODO make configurable?
input.selectedIndex = -1;
} else {
option.selected = true;
}
} else {
if (range == null) {
input.classList.remove('ql-active');
} else if (input.hasAttribute('value')) {
// both being null should match (default values)
// '1' should match with 1 (headers)
let isActive = formats[format] === input.getAttribute('value') ||
(formats[format] != null && formats[format].toString() === input.getAttribute('value')) ||
(formats[format] == null && !input.getAttribute('value'));
input.classList.toggle('ql-active', isActive);
} else {
input.classList.toggle('ql-active', formats[format] != null);
}
}
});
}
}
Toolbar.DEFAULTS = {};
function addButton(container, format, value) {
let input = document.createElement('button');
input.setAttribute('type', 'button');
input.classList.add('ql-' + format);
if (value != null) {
input.value = value;
}
container.appendChild(input);
}
// 遍历container数组里面数组的每一项,
// 如果是字符串就创建button,
// 如果是对象,就看对象的值是不是数组,是的话,就创建select,option值为对象数组内的每一项
// 不是的话同样创建button
function addControls(container, groups) {
if (!Array.isArray(groups[0])) {
groups = [groups];
}
groups.forEach(function(controls) {
let group = document.createElement('span');
group.classList.add('ql-formats');
controls.forEach(function(control) {
if (typeof control === 'string') {
addButton(group, control);
} else {
let format = Object.keys(control)[0];
let value = control[format];
if (Array.isArray(value)) {
addSelect(group, format, value);
} else {
addButton(group, format, value);
}
}
});
container.appendChild(group);
});
}
function addSelect(container, format, values) {
let input = document.createElement('select');
input.classList.add('ql-' + format);
values.forEach(function(value) {
let option = document.createElement('option');
if (value !== false) {
option.setAttribute('value', value);
} else {
option.setAttribute('selected', 'selected');
}
input.appendChild(option);
});
container.appendChild(input);
}
extendToolbar(如果实例化的模块式toolbar,则还需要进行这个处理)
class BaseTheme extends Theme {
addModule(name) {
let module = super.addModule(name);
if (name === 'toolbar') {
this.extendToolbar(module);
}
return module;
}
buildButtons(buttons, icons) {
buttons.forEach((button) => {
let className = button.getAttribute('class') || '';
className.split(/\s+/).forEach((name) => {
if (!name.startsWith('ql-')) return;
name = name.slice('ql-'.length);
if (icons[name] == null) return;
if (name === 'direction') {
button.innerHTML = icons[name][''] + icons[name]['rtl'];
} else if (typeof icons[name] === 'string') {
button.innerHTML = icons[name];
} else {
let value = button.value || '';
if (value != null && icons[name][value]) {
button.innerHTML = icons[name][value];
}
}
});
});
}
buildPickers(selects, icons) {
this.pickers = selects.map((select) => {
if (select.classList.contains('ql-align')) {
if (select.querySelector('option') == null) {
fillSelect(select, ALIGNS);
}
// 根据data-value映射事先定义好的图标对象获取对应图标,data-value为创建option时的value
return new IconPicker(select, icons.align);
} else if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {
let format = select.classList.contains('ql-background') ? 'background' : 'color';
if (select.querySelector('option') == null) {
fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000');
}
// 获取对应颜色,,data-value为创建option时的value
return new ColorPicker(select, icons[format]);
} else {
if (select.querySelector('option') == null) {
if (select.classList.contains('ql-font')) {
fillSelect(select, FONTS);
} else if (select.classList.contains('ql-header')) {
fillSelect(select, HEADERS);
} else if (select.classList.contains('ql-size')) {
fillSelect(select, SIZES);
}
}
// 取值,,data-value为创建option时的value 进入
return new Picker(select);
}
});
let update = () => {
this.pickers.forEach(function(picker) {
picker.update();
});
};
this.quill.on(Emitter.events.EDITOR_CHANGE, update);
}
}
class SnowTheme extends BaseTheme {
extendToolbar(toolbar) {
toolbar.container.classList.add('ql-snow');
// 给所有类型为button的添加图标
this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), icons);
// 给所有类型为select的添加图标或者设置值,并且,看传入的toolbar.container里面的align,background,color,font,header,size数组是否为空,如果是,那么就采用默认配置
this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), icons);
this.tooltip = new SnowTooltip(this.quill, this.options.bounds);
if (toolbar.container.querySelector('.ql-link')) {
this.quill.keyboard.addBinding({ key: 'K', shortKey: true }, function(range, context) {
toolbar.handlers['link'].call(toolbar, !context.format.link);
});
}
}
}
buildPickers(toolbar里面类型为select的设置)
class Picker {
constructor(select) {
this.select = select;
this.container = document.createElement('span');
// 进入
this.buildPicker();
this.select.style.display = 'none';
this.select.parentNode.insertBefore(this.container, this.select);
this.label.addEventListener('mousedown', () => {
this.togglePicker();
});
this.label.addEventListener('keydown', (event) => {
switch(event.keyCode) {
// Allows the "Enter" key to open the picker
case Keyboard.keys.ENTER:
this.togglePicker();
break;
// Allows the "Escape" key to close the picker
case Keyboard.keys.ESCAPE:
this.escape();
event.preventDefault();
break;
default:
}
});
this.select.addEventListener('change', this.update.bind(this));
}
buildItem(option) {
let item = document.createElement('span');
item.tabIndex = '0';
item.setAttribute('role', 'button');
// 给新的dom设置类名
item.classList.add('ql-picker-item');
// 获取之前toolbar.container创建好的option的value,给新的dom设置data-value
// 即之后这个设置值可以这样 对应类型 + .ql-picker-item[data-value="10px"]::before 里面给content: 设值
if (option.hasAttribute('value')) {
item.setAttribute('data-value', option.getAttribute('value'));
}
if (option.textContent) {
item.setAttribute('data-label', option.textContent);
}
// 点击调用取值对应的option的事件
item.addEventListener('click', () => {
this.selectItem(item, true);
});
item.addEventListener('keydown', (event) => {
switch(event.keyCode) {
// Allows the "Enter" key to select an item
case Keyboard.keys.ENTER:
this.selectItem(item, true);
event.preventDefault();
break;
// Allows the "Escape" key to close the picker
case Keyboard.keys.ESCAPE:
this.escape();
event.preventDefault();
break;
default:
}
});
return item;
}
buildOptions() {
let options = document.createElement('span');
options.classList.add('ql-picker-options');
// Don't want screen readers to read this until options are visible
options.setAttribute('aria-hidden', 'true');
options.tabIndex = '-1';
// Need a unique id for aria-controls
options.id = `ql-picker-options-${optionsCounter}`;
optionsCounter += 1;
this.label.setAttribute('aria-controls', options.id);
this.options = options;
// 到这里,遍历之前根据toolbar.container创建好的option
[].slice.call(this.select.options).forEach((option) => {
// 进入
let item = this.buildItem(option);
options.appendChild(item);
if (option.selected === true) {
this.selectItem(item);
}
});
this.container.appendChild(options);
}
buildPicker() {
[].slice.call(this.select.attributes).forEach((item) => {
this.container.setAttribute(item.name, item.value);
});
// 创建新容器
this.container.classList.add('ql-picker');
this.label = this.buildLabel();
// 进入
this.buildOptions();
}
// 点击新容器中的选项会触发旧select中的事件
selectItem(item, trigger = false) {
let selected = this.container.querySelector('.ql-selected');
if (item === selected) return;
if (selected != null) {
selected.classList.remove('ql-selected');
}
if (item == null) return;
item.classList.add('ql-selected');
this.select.selectedIndex = [].indexOf.call(item.parentNode.children, item);
if (item.hasAttribute('data-value')) {
this.label.setAttribute('data-value', item.getAttribute('data-value'));
} else {
this.label.removeAttribute('data-value');
}
if (item.hasAttribute('data-label')) {
this.label.setAttribute('data-label', item.getAttribute('data-label'));
} else {
this.label.removeAttribute('data-label');
}
if (trigger) {
if (typeof Event === 'function') {
this.select.dispatchEvent(new Event('change'));
} else if (typeof Event === 'object') { // IE11
let event = document.createEvent('Event');
event.initEvent('change', true, true);
this.select.dispatchEvent(event);
}
this.close();
}
}
扩展
- 当new Quill,实例化module时,是将配置的option.module参数传入对应的module里面所以,新增模块时,可以自定义参数参入自定义的模块里面
- Quill.import('对应名字'),可以拿到里面注册的构造函数,修改会影响原有功能
- toolbar如果传false工具栏就不会出现了,参数container为数组的话,会覆盖默认的值,所以需要手动配置,工具栏可以触发定义好的事件,默认有一些处理事件,如果需要可以在handlers中拦截,需要拦截函数需要命名和container定义的一样,会将当前触发事件后拿到的值传过来.如果有默认的处理方式也会被重写
- toolbar.container中select会被创建多一份容器用作显示,当用作显示的呗选择时,会触发原本select的事件,新容器中的option类名为.ql-picker-item,并且data-value为原本option中的value,所以,可以借此来通过css来设置值
- 在toolbar.container配置新的select类型的项目,或者对旧的进行修改,配置的内容需要和format中对应,而下拉框的值,可以通过 类型 + .ql-picker-item[data-value="对应的值"]::before,设置content,如果需要其他样式接着在这设置,只要toolbar.container设置的值和format中对应了,效果也就可以了
例子1: 自定义对复制黏贴进行处理模块
export class PasteFormat {
constructor(quill, config) {
this.quill = quill;
this.config = Object.assign(
{
onInsertImage: () => {},
onInsertRichText: () => {},
onInsertText: () => {},
onInsertLink: () => {}
},
config
);
quill.root.addEventListener('paste', this.pasteHandler.bind(this), false);
}
/**
* 右键黏贴内容
*/
pasteHandler() {
const matchers = this.quill && this.quill.clipboard.matchers;
const pasteMatcher = [Node.ELEMENT_NODE, this.nodeHandler.bind(this)];
matchers.push(pasteMatcher);
}
/**
* 针对黏贴内容进行处理
* 可以控制内容格式、或禁止内容输入
* https://quilljs.com/docs/modules/clipboard/
*/
nodeHandler(node, delta) {
// eslint-disable-next-line no-param-reassign
delta.ops = delta.ops
.map(op => {
if (typeof op.insert === 'string') {
if (op.attributes && op.attributes.link) {
return this.config.onInsertLink(op);
}
return this.config.onInsertText(op);
}
if (op.insert && op.insert.image) {
return this.config.onInsertImage(op);
}
return this.config.onInsertRichText(op);
})
.filter(op => typeof op === 'object');
return delta;
}
}
<template>
<div>
<Quill-editor
ref="myQuillEditor"
:value="value"
:options="editorOption"
@input="handleInput"
/>
</div>
</template>
<script>
import QuillEditor, { Quill } from './vue-quill-editor';
import { PasteFormat } from './utils/PasteFormat';
Quill.register('modules/PasteFormat', PasteFormat); // 注册到modules/PasteFormat,之后传参名也需要与PasteFormat一致
export default {
components: {
QuillEditor
},
props: {
value: {
type: String,
default: ''
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '300px'
}
},
data() {
return {
editorOption: {
placeholder: '输入文本...',
theme: 'snow', // 主题
modules: {
// 传参进自定义module
PasteFormat: {
onInsertImage: () => undefined,
onInsertRichText: () => undefined,
onInsertText: op => ({
insert: op.insert
}),
onInsertLink: op => ({
insert: op.insert
})
},
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线
['blockquote', 'code-block'], // 引用,代码块
[{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、2表示字体大小
[{ list: 'ordered' }, { list: 'bullet' }], // 列表
[{ script: 'sub' }, { script: 'super' }], // 上下标
[{ indent: '-1' }, { indent: '+1' }], // 缩进
[{ direction: 'rtl' }], // 文本方向
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 几级标题
[{ color: [] }, { background: [] }], // 字体颜色,字体背景颜色
[{ align: [] }], // 对齐方式
['clean'], // 清除字体样式
['image', 'video'] // 上传图片、上传视频
]
}
}
}
};
},
computed: {
editor() {
return this.$refs.myQuillEditor.quill;
}
},
mounted() {
this.editor.container.style.width = this.width;
this.editor.container.style.height = this.height;
},
methods: {
handleInput(val) {
this.$emit('input', val);
}
}
};
</script>
例子2: 对字体大小的修改
<template>
<div>
<Quill-editor
ref="myQuillEditor"
:value="value"
:options="editorOption"
@input="handleInput"
/>
</div>
</template>
<script>
import QuillEditor, { Quill } from './vue-quill-editor';
const SizeStyle = Quill.import('attributors/style/size');
SizeStyle.whitelist = [ //和下面配置对应
'10px',
'12px',
'14px',
'16px',
'18px',
'20px',
'24px',
false
];
Quill.register(SizeStyle, true);
export default {
components: {
QuillEditor
},
props: {
value: {
type: String,
default: ''
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '300px'
}
},
data() {
return {
editorOption: {
placeholder: '输入文本...',
theme: 'snow', // 主题
modules: {
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线
['blockquote', 'code-block'], // 引用,代码块
[{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、2表示字体大小
[{ list: 'ordered' }, { list: 'bullet' }], // 列表
[{ script: 'sub' }, { script: 'super' }], // 上下标
[{ indent: '-1' }, { indent: '+1' }], // 缩进
[{ direction: 'rtl' }], // 文本方向
[
{
size: [
'10px',
'12px',
'14px',
'16px',
'18px',
'20px',
'24px',
false
]
}
], // 字体大小,false设置自定义大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 几级标题
[{ color: [] }, { background: [] }], // 字体颜色,字体背景颜色
[{ align: [] }], // 对齐方式
['clean'], // 清除字体样式
['image', 'video'] // 上传图片、上传视频
]
}
}
}
};
},
computed: {
editor() {
return this.$refs.myQuillEditor.quill;
}
},
mounted() {
this.editor.container.style.width = this.width;
this.editor.container.style.height = this.height;
},
methods: {
handleInput(val) {
this.$emit('input', val);
}
}
};
</script>
<style>
@import url(./utils/font.css);
</style>
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="10px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="10px"]::before {
content: '10px';
font-size: 10px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="12px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="12px"]::before {
content: '12px';
font-size: 12px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
content: '14px';
font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
content: '16px';
font-size: 16px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
content: '18px';
font-size: 18px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
content: '20px';
font-size: 20px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="24px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="24px"]::before {
content: '24px';
font-size: 24px;
}