关于 Quill
Quill 是一个富文本编辑器库,API 驱动,高自由度定制化。
但 Quill 界面都是英文的,面向普通用户不太友好,需要显示成中文。
默认效果如下:
想要的效果如下:
汉化处理
常用功能中需要转换成中文的有4个部分:
placeholder
这个很简单,加个配置项
new Quill(this.$refs.editor, {
placeholder: '请输入内容'
})
picker
字号和标题这里英文就很明显,需要覆盖 quill 的样式来实现。
.ql-snow.ql-toolbar {
.ql-picker.ql-header {
width: 70px;
.ql-picker-label::before,
.ql-picker-item::before {
content: '普通';
}
.ql-picker-label[data-value='1']::before,
.ql-picker-item[data-value='1']::before {
content: '标题一';
}
.ql-picker-label[data-value='2']::before,
.ql-picker-item[data-value='2']::before {
content: '标题二';
}
.ql-picker-label[data-value='3']::before,
.ql-picker-item[data-value='3']::before {
content: '标题三';
}
.ql-picker-label[data-value='4']::before,
.ql-picker-item[data-value='4']::before {
content: '标题四';
}
.ql-picker-label[data-value='5']::before,
.ql-picker-item[data-value='5']::before {
content: '标题五';
}
.ql-picker-label[data-value='6']::before,
.ql-picker-item[data-value='6']::before {
content: '标题六';
}
}
.ql-picker.ql-size {
width: 70px;
.ql-picker-label::before,
.ql-picker-item::before {
content: '普通';
}
.ql-picker-label[data-value='small']::before,
.ql-picker-item[data-value='small']::before {
content: '小';
}
.ql-picker-label[data-value='large']::before,
.ql-picker-item[data-value='large']::before {
content: '中';
}
.ql-picker-label[data-value='huge']::before,
.ql-picker-item[data-value='huge']::before {
content: '大';
}
}
}
title
其实 Quill 默认是没有 title 的,但格式操作图标不给个提示难免让用户困惑,有必要加个 title 提示下每个图标的含义。
加 title 就要麻烦点了,需要遍历所有图标设置 title 属性。
const TOOLBAR_TILTE = {
'ql-bold': ['加粗'],
'ql-italic': ['倾斜'],
'ql-underline': ['下划线'],
'ql-strike': ['删除线'],
'ql-blockquote': ['块引用'],
'ql-script': ['下标', '上标'],
'ql-list': ['编号列表', '项目符号列表'],
'ql-indent': ['减少缩进量', '增加缩进量'],
'ql-align': ['对齐', '左对齐', '居中', '右对齐', '两端对齐'],
'ql-color': ['字体颜色'],
'ql-background': ['文本突出显示颜色'],
'ql-size': ['字号'],
'ql-header': ['标题'],
'ql-link': ['链接'],
'ql-image': ['图片'],
'ql-clean': ['清除样式']
}
for (let key in TOOLBAR_TILTE) {
Array.from(
this.$refs.bounds.querySelectorAll(
`.ql-toolbar .ql-formats > .${key}`
)
).forEach((item, index) => {
if (key === 'ql-align') {
Array.from(item.querySelectorAll('[role=button]')).forEach(
(role, rIndex) => {
role.title = TOOLBAR_TILTE[key][rIndex]
}
)
} else {
item.title = TOOLBAR_TILTE[key][index]
}
})
}
tooltip
主要是链接的编辑会用到这个组件,也是通过覆盖 quill 的样式来实现。
.ql-snow .ql-tooltip::before {
content: '链接';
}
.ql-snow .ql-tooltip a.ql-action::after {
content: '编辑';
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
content: '保存';
}
.ql-snow .ql-tooltip a.ql-remove::before {
content: '移除';
}
完整实现
把 Quill 封装成一个 Vue 组件,想要的使用效果就是 <Quill v-model="foo" />,简单明了。
首先需要安装 quill
npm i quill --save
Quill.vue
<template>
<div ref="bounds" class="quill-editor">
<div ref="editor"></div>
</div>
</template>
<script>
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import Quill from 'quill'
export default {
name: 'QuillEditor',
props: {
value: Object
},
data() {
return {
editor: null
}
},
mounted() {
const options = {
theme: 'snow',
bounds: this.$refs.bounds,
placeholder: '请输入内容',
modules: {
toolbar: {
container: [
[
{ size: ['small', false, 'large', 'huge'] },
{ header: [1, 2, 3, 4, 5, 6, false] }
],
[
{ color: [] },
{ background: [] },
'bold',
'italic',
'underline',
'strike',
'blockquote',
{ script: 'sub' },
{ script: 'super' }
],
[
{ list: 'ordered' },
{ list: 'bullet' },
{ indent: '-1' },
{ indent: '+1' },
{ align: [] }
],
['link'],
['clean']
]
}
}
}
this.editor = new Quill(this.$refs.editor, options)
this.editor.setContents(this.value)
this.sinicize()
this.change()
},
watch: {
value() {
if (
JSON.stringify(this.value) !== JSON.stringify(this.editor.getContents())
) {
this.editor.setContents(this.value)
}
}
},
methods: {
sinicize() {
const TOOLBAR_TILTE = {
'ql-bold': ['加粗'],
'ql-italic': ['倾斜'],
'ql-underline': ['下划线'],
'ql-strike': ['删除线'],
'ql-blockquote': ['块引用'],
'ql-script': ['下标', '上标'],
'ql-list': ['编号列表', '项目符号列表'],
'ql-indent': ['减少缩进量', '增加缩进量'],
'ql-align': ['对齐', '左对齐', '居中', '右对齐', '两端对齐'],
'ql-color': ['字体颜色'],
'ql-background': ['文本突出显示颜色'],
'ql-size': ['字号'],
'ql-header': ['标题'],
'ql-link': ['链接'],
'ql-image': ['图片'],
'ql-clean': ['清除样式']
}
for (let key in TOOLBAR_TILTE) {
Array.from(
this.$refs.bounds.querySelectorAll(
`.ql-toolbar .ql-formats > .${key}`
)
).forEach((item, index) => {
if (key === 'ql-align') {
Array.from(item.querySelectorAll('[role=button]')).forEach(
(role, rIndex) => {
role.title = TOOLBAR_TILTE[key][rIndex]
}
)
} else {
item.title = TOOLBAR_TILTE[key][index]
}
})
}
},
change() {
this.editor.on('text-change', () => {
this.$emit('input', this.editor.getContents())
})
}
}
}
</script>
<style lang="scss" scoped>
.quill-editor {
::v-deep {
.ql-editor {
min-height: 100px;
max-height: 500px;
}
.ql-snow.ql-toolbar {
.ql-picker.ql-header {
width: 70px;
.ql-picker-label::before,
.ql-picker-item::before {
content: '普通';
}
.ql-picker-label[data-value='1']::before,
.ql-picker-item[data-value='1']::before {
content: '标题一';
}
.ql-picker-label[data-value='2']::before,
.ql-picker-item[data-value='2']::before {
content: '标题二';
}
.ql-picker-label[data-value='3']::before,
.ql-picker-item[data-value='3']::before {
content: '标题三';
}
.ql-picker-label[data-value='4']::before,
.ql-picker-item[data-value='4']::before {
content: '标题四';
}
.ql-picker-label[data-value='5']::before,
.ql-picker-item[data-value='5']::before {
content: '标题五';
}
.ql-picker-label[data-value='6']::before,
.ql-picker-item[data-value='6']::before {
content: '标题六';
}
}
.ql-picker.ql-size {
width: 70px;
.ql-picker-label::before,
.ql-picker-item::before {
content: '普通';
}
.ql-picker-label[data-value='small']::before,
.ql-picker-item[data-value='small']::before {
content: '小';
}
.ql-picker-label[data-value='large']::before,
.ql-picker-item[data-value='large']::before {
content: '中';
}
.ql-picker-label[data-value='huge']::before,
.ql-picker-item[data-value='huge']::before {
content: '大';
}
}
}
.ql-snow .ql-tooltip::before {
content: '链接';
}
.ql-snow .ql-tooltip a.ql-action::after {
content: '编辑';
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
content: '保存';
}
.ql-snow .ql-tooltip a.ql-remove::before {
content: '移除';
}
}
}
</style>
page.vue
<template>
<Quill v-model="foo" />
</template>
<script>
import Quill from './Quill'
export default {
name: 'Page',
components: { Quill },
data() {
return {
foo: null
}
}
}
</script>
试试效果