这里使用的是 vue-quill-editor 富文本组件
先安装一下:
npm i vue-quill-editor -s
下载之后 去components文件夹中新建一个文件夹 KEditor,然后里面创建俩文件 一个js 一个vue
其中 : index.js 内容
/*
* @Description:导出富文本编辑器
* @Author: kcz
* @Date: 2020-03-30 12:45:04
* @LastEditors: kcz
* @LastEditTime: 2020-03-30 12:45:40
*/
import KEditor from "./kEditor.vue";
export default KEditor;
kEditor.vue 内容
<template>
<div class="edit_container">
<!-- 新增时输入 -->
<quill-editor
:disabled = isDisabled
v-model="content"
ref="myQuillEditor"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)"
>
</quill-editor>
<!-- 从数据库读取展示 -->
<div v-html="str" class="ql-editor">
{{ str }}
</div>
</div>
</template>
<script>
import { quillEditor } from 'vue-quill-editor' // 调用编辑器
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
export default {
name: 'Editor',
props:['contentText', 'isDisabled'],
components: {
quillEditor
},
data () {
return {
content: ``,
str: '',
editorOption: {
placeholder: '请在这里输入',
modules: {
toolbar: [
['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': ['small', false, 'large', 'huge'] }], // 字体大小
[{ 'header': [1, 2, 3, 4, 5, 6, false] }], // 几级标题
[{ 'color': [] }, { 'background': [] }], // 字体颜色,字体背景颜色
[{ 'font': [false, 'heiti', 'songti', 'kaiti', 'lishu', 'arial', 'monospace'] }], // 字体
[{ 'align': [] }], // 对齐方式
['clean'], // 清除字体样式
// ['image', 'video'] // 上传图片、上传视频
]
}
}
}
},
computed: {
// 当前富文本实例
editor () {
return this.$refs.myQuillEditor.quill
}
},
mounted () {
const content = ''// 请求后台返回的内容字符串
this.content = this.contentText
this.str = this.escapeStringHTML(content)
},
methods: {
onEditorReady (editor) { // 准备编辑器
},
onEditorBlur () {}, // 失去焦点事件
onEditorFocus () {}, // 获得焦点事件
onEditorChange () {
this.$emit('change', this.escapeStringHTML(this.content))
console.log(this.content,"子组件中的")
}, // 内容改变事件
// 转码
escapeStringHTML (str) {
str = str.replace(/</g, '<')
str = str.replace(/>/g, '>')
return str
}
}
}
</script>
<style>
.editor {
line-height: normal !important;
height: 500px;
}
.ql-snow .ql-tooltip[data-mode=link]::before {
content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0px;
content: '保存';
padding-right: 0px;
}
.ql-snow .ql-tooltip[data-mode=video]::before {
content: "请输入视频地址:";
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: '14px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
content: '10px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
content: '18px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
content: '32px';
}
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: '文本';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: '标题1';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: '标题2';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: '标题3';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: '标题4';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: '标题5';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: '标题6';
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: '标准字体';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
content: '衬线字体';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
content: '等宽字体';
}
</style>
到这里 准备工作就差不多了 剩下就是使用了
在需要引入的vue界面中:注册刚刚的组件 路径和我的不一样 记得更换
import KEditor from '../../components/portalSetting/KEditor'
...
components: {
KEditor
},
使用:
<KEditor
:key="eidtFlag"
ref="Editor"
:isDisabled="isDisabled"
:contentText="notice.noticeContent"
v-model="notice.noticeContent"
@change="callbackChangeEditor"
/>
再去data中定义几个属性:和一个回调方法
// 是否禁用
isDisabled:false,
// 富文本强制每次打开刷新
eidtFlag: 0,
notice:{
noticeId:'',
noticeTitle:'',
noticeContent:'',
priority:1,
}
// 富文本的值改变后的回调
callbackChangeEditor(e){
this.notice.noticeContent = e;
}
<style scoped>
>>> .edit_container .quill-editor .ql-container .ql-editor {
height: 260px;
}
</style>
一个简单的使用实例
<template> <div> <Editor class="CKEditor" ref="Editor" v-model="content" @change="callbackChangeEditor"></Editor> </div> </template> <script> import { Editor } from '../../components/portalSetting/KEditor' export default { components: { Editor }, data () { return { content: '', } }, mounted () {}, methods: { callbackChangeEditor (value) { this.content = value }, } } </script> <style lang="less" scoped> </style>
到这里 去页面刷新 就可以看到效果了
下面是我在使用时候的vue代码
<template> <div> <!-- 头部按钮 --> <div style="display: flex; justify-content: space-between; padding: 8px; border-radius: 8px; background-color: #f5f5f5;"> <div> <el-select @change="initNoticeList" size="mini" clearable v-model="noticeState" placeholder="全部"> <el-option v-for="item in noticeStateSelect" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </div> <div> <el-button size="mini" type="primary" @click="showAddDialog">新增</el-button> <el-button size="mini" plain @click="publishOrPauseNotice('发布',1)">发布</el-button> <el-button size="mini" plain @click="publishOrPauseNotice('暂停',2)">暂停</el-button> </div> </div> <!-- 列表数据 --> <div style="border-radius: 8px; background-color: #f5f5f5;"> <el-table :data="noticeList" style="width: 100%; height: 100%;" height="500" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55"> </el-table-column> <el-table-column fixed align="center" prop="noticeTitle" label="公告标题" width="250"> </el-table-column> <el-table-column align="center" prop="priority" label="优先级" width="100"> </el-table-column> <el-table-column align="center" prop="noticeStateName" label="发布状态" width="150"> <template slot-scope="scope"> <el-tag v-if="scope.row.noticeState==0" type="info">{{ scope.row.noticeStateName }}</el-tag> <el-tag v-if="scope.row.noticeState==1" type="success">{{ scope.row.noticeStateName }}</el-tag> <el-tag v-if="scope.row.noticeState==2" type="warning">{{ scope.row.noticeStateName }}</el-tag> </template> </el-table-column> <el-table-column prop="publishUser" label="发布人" width="160"> </el-table-column> <el-table-column prop="publishDate" label="发布时间" width="250"> </el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <!-- 已发布 不可修改 --> <div v-if="scope.row.noticeState==1"> <i @click="showViewDialog(scope.row)" style="color:#409EFF; margin-left: 6px;" class="el-icon-view"></i> </div> <!-- 未发布 已暂停可修改 --> <div v-else> <i style="color:#409eff" class="el-icon-edit-outline" @click="showEditDialog(scope.row)"></i> <el-popconfirm placement="left" confirm-button-text='确定' cancel-button-text='取消' icon="el-icon-info" confirm-button-type="primary" cancel-button-type="" icon-color="red" title="确认删除本条数据吗?" @confirm="deleteNotice(scope.row.noticeId)" > <i slot="reference" style="color:red; margin-left: 6px;" class="el-icon-delete"></i> </el-popconfirm> </div> </template> </el-table-column> </el-table> </div> <!-- 分页 --> <div style="display: flex; justify-content: flex-end;"> <el-pagination background @current-change="currentChange" @size-change="sizeChange" :page-sizes="[10, 20, 100, 200]" :page-size="20" layout="total, prev, sizes" :total="total"> </el-pagination> </div> <!-- 添加公告/修改公告弹出框 --> <el-dialog size="mini" :title="dialogVisibleTitle" :visible.sync="dialogVisible" width="60%"> <div> <el-form ref="noticeForm" :model="notice" > <!-- 第一行 --> <el-row :gutter="20"> <el-col :span="3"> 公告标题: </el-col> <el-col :span="6"> <el-input :disabled="isDisabled" size="mini" v-model="notice.noticeTitle" placeholder="请输入公告标题"></el-input> </el-col> </el-row> <!-- 第二行 --> <el-row :gutter="20" style="margin-top:10px"> <el-col :span="3"> 公告优先级: </el-col> <el-col :span="6"> <el-input :disabled="isDisabled" size="mini" v-model="notice.priority" placeholder="请输入公告优先级"></el-input> </el-col> </el-row> <!-- 第三行 --> <el-row :gutter="20" style="margin-top:10px; height: 300px;"> <el-col :span="3"> 公告内容: </el-col> <el-col :span="19"> <KEditor class="CKEditor" :key="eidtFlag" ref="Editor" :isDisabled="isDisabled" :contentText="notice.noticeContent" v-model="notice.noticeContent" @change="callbackChangeEditor" /> </el-col> </el-row> </el-form> </div> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="addOrEditNotice">确 定</el-button> </span> </el-dialog> </div> </template> <script> import KEditor from '../../components/portalSetting/KEditor' export default { components: { KEditor }, data(){ return { // 是否禁用 isDisabled:false, // 富文本强制每次打开刷新 eidtFlag: 0, // 公告状态下拉框 noticeStateSelect: [{ value: '0', label: '未发布' }, { value: '1', label: '已发布' }, { value: '2', label: '已暂停' }], noticeState: '', // 列表数据 noticeList:[], // 表格选中的id multipleSelection: [], // 分页参数 total:0, currentPage: 1, pageSize: 20, // 添加公告/修改公告弹出框 dialogVisible:false, dialogVisibleTitle:'', // 添加公告临时变量 notice:{ noticeId:'', noticeTitle:'', noticeContent:'', priority:1, } } }, created(){ this.initNoticeList(); }, methods:{ // 加载公告列表 initNoticeList(){ let params = { noticeState: this.noticeState, currentPage: this.currentPage, pageSize: this.pageSize } this.postRequest('/notice/listNotice',params).then(resp => { if(resp.code == 200){ this.noticeList = resp.obj.list; this.total = resp.obj.total; }else{ this.$message.error(resp.message) } }) }, // 选中表格的数据 handleSelectionChange(val) { let noticeIds = val.map((notice) => { return notice.noticeId; }) this.multipleSelection = noticeIds; }, // 页数改变时触发 方法会带过来当前页 currentChange(currentPage){ this.currentPage = currentPage; this.initNoticeList(); }, // 每页大小改变 sizeChange(pageSize){ this.pageSize = pageSize; this.initNoticeList(); }, // 删除招聘公告 deleteNotice(noticeId){ let formData = new FormData(); formData.append('noticeId', noticeId) this.postRequest2('/notice/deleteNoticeById',formData).then(resp=>{ if(resp.code==200){ this.initNoticeList(); }else{ this.$message.error(resp.message) } }) }, // 发布 / 暂停公告 publishOrPauseNotice(title,state){ if(this.multipleSelection.length!=0){ this.$confirm('确认要'+title+'这'+this.multipleSelection.length+'条公告吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { let formData = new FormData(); formData.append('noticeIds', this.multipleSelection); formData.append('noticeState', state); this.postRequest2('/notice/updateNoticeState',formData).then(resp=>{ if(resp.code==200){ this.initNoticeList(); }else{ this.$message.error(resp.message) } }) }).catch(() => { this.$message({ type: 'info', message: '已取消操作' }); }); }else{ this.$message.error("至少选择一条数据") } }, // 打开添加公告弹框 showAddDialog(){ this.isDisabled = false; this.dialogVisibleTitle='添加公告' this.notice = { noticeId:'', noticeTitle:'', noticeContent:'', priority:1, } this.eidtFlag++ this.dialogVisible = true; }, // 添加/修改公告 addOrEditNotice(){ if(!this.isDisabled){ this.postRequest('/notice/saveOrUpdateNotice',this.notice).then(resp=>{ if(resp.code==200){ this.initNoticeList(); this.dialogVisible = false; }else{ this.$message.error(resp.message) } }) } }, // 打开修改公告弹框 showEditDialog(val){ this.isDisabled = false; this.dialogVisibleTitle='修改公告' this.notice = { noticeId:val.noticeId, noticeTitle:val.noticeTitle, noticeContent:val.noticeContent, priority:val.priority, } this.eidtFlag++; this.dialogVisible = true; }, // 打开查看已发布的公告 showViewDialog(val){ this.isDisabled = true; this.dialogVisibleTitle='公告' this.notice = { noticeId:val.noticeId, noticeTitle:val.noticeTitle, noticeContent:val.noticeContent, priority:val.priority, } this.eidtFlag++; this.dialogVisible = true; }, // 富文本的值改变后的回调 callbackChangeEditor(e){ this.notice.noticeContent = e; }, } } </script> <style scoped> >>> .edit_container .quill-editor .ql-container .ql-editor { height: 260px; } </style>
如果需要更详细的使用 其他参数 比如上传图片 请移步
https://blog.youkuaiyun.com/qq_44091061/article/details/122453951
官网:quill-image-extend-module · Quill官方中文文档 · 看云
引入带有图片和视频的:
下载依赖
npm i quill-image-extend-module
增加后的组件:
主要是引入这些:这是和上面比较后,加的
import { quillEditor, Quill } from 'vue-quill-editor' // 调用编辑器
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'
Quill.register('modules/ImageExtend', ImageExtend)
modules: {
ImageExtend: {
loading: true,
name: 'file',
// 更多配置见官方文档https://www.kancloud.cn/liuwave/quill/1434141
// 设置请求头部
// headers: (xhr) => {
// xhr.setRequestHeader('Authorization', window.sessionStorage.getItem('tokenStr'))
// },
//action:'/common/uploadBase64ImageToQiniu', // this.fileUrl, // 这里写入请求后台地址 例如:"http://xxx.xxx.xxx.xxx:xxx/api/file/upload/indexFile",
action:'/common/uploadImageToQiniu',
response: (res) => {
return res.obj // 这里写入请求返回的数据,也就是一个图片字符串
}
},
toolbar: {
container: container,
handlers: {
'image': function () {
QuillWatch.emit(this.quill.id)
}
}
}
// toolbar: [
// ['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': ['small', false, 'large', 'huge'] }], // 字体大小
// [{ 'header': [1, 2, 3, 4, 5, 6, false] }], // 几级标题
// [{ 'color': [] }, { 'background': [] }], // 字体颜色,字体背景颜色
// [{ 'font': [false, 'heiti', 'songti', 'kaiti', 'lishu', 'arial', 'monospace'] }], // 字体
// [{ 'align': [] }], // 对齐方式
// ['clean'], // 清除字体样式
// ['image'],
// // ['image', 'video'] // 上传图片、上传视频,
// ]
}
这是完整的组件
<template>
<div class="edit_container">
<!-- 新增时输入 -->
<quill-editor
:disabled = isDisabled
v-model="content"
ref="myQuillEditor"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)"
>
</quill-editor>
<!-- 从数据库读取展示 -->
<div v-html="str" class="ql-editor">
{{ str }}
</div>
</div>
</template>
<script>
import { quillEditor, Quill } from 'vue-quill-editor' // 调用编辑器
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
Quill.register('modules/ImageExtend', ImageExtend)
export default {
name: 'Editor',
props:['contentText', 'isDisabled'],
components: {
quillEditor
},
data () {
return {
content: ``,
str: '',
editorOption: {
placeholder: '请在这里输入',
modules: {
ImageExtend: {
loading: true,
name: 'file',
// 更多配置见官方文档https://www.kancloud.cn/liuwave/quill/1434141
// 设置请求头部
// headers: (xhr) => {
// xhr.setRequestHeader('Authorization', window.sessionStorage.getItem('tokenStr'))
// },
//action:'/common/uploadBase64ImageToQiniu', // this.fileUrl, // 这里写入请求后台地址 例如:"http://xxx.xxx.xxx.xxx:xxx/api/file/upload/indexFile",
action:'/common/uploadImageToQiniu',
response: (res) => {
return res.obj // 这里写入请求返回的数据,也就是一个图片字符串
}
},
toolbar: {
container: container,
handlers: {
'image': function () {
QuillWatch.emit(this.quill.id)
}
}
}
// toolbar: [
// ['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': ['small', false, 'large', 'huge'] }], // 字体大小
// [{ 'header': [1, 2, 3, 4, 5, 6, false] }], // 几级标题
// [{ 'color': [] }, { 'background': [] }], // 字体颜色,字体背景颜色
// [{ 'font': [false, 'heiti', 'songti', 'kaiti', 'lishu', 'arial', 'monospace'] }], // 字体
// [{ 'align': [] }], // 对齐方式
// ['clean'], // 清除字体样式
// ['image'],
// // ['image', 'video'] // 上传图片、上传视频,
// ]
}
}
}
},
computed: {
// 当前富文本实例
editor () {
return this.$refs.myQuillEditor.quill
}
},
mounted () {
const content = ''// 请求后台返回的内容字符串
this.content = this.contentText
this.str = this.escapeStringHTML(content)
},
methods: {
onEditorReady (editor) { // 准备编辑器
},
onEditorBlur () {}, // 失去焦点事件
onEditorFocus () {}, // 获得焦点事件
onEditorChange () {
this.$emit('change', this.escapeStringHTML(this.content))
console.log(this.content,"子组件中的")
}, // 内容改变事件
// 转码
escapeStringHTML (str) {
str = str.replace(/</g, '<')
str = str.replace(/>/g, '>')
return str
}
}
}
</script>
<style>
.editor {
line-height: normal !important;
height: 500px;
}
.ql-snow .ql-tooltip[data-mode=link]::before {
content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0px;
content: '保存';
padding-right: 0px;
}
.ql-snow .ql-tooltip[data-mode=video]::before {
content: "请输入视频地址:";
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: '14px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
content: '10px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
content: '18px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
content: '32px';
}
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: '文本';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: '标题1';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: '标题2';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: '标题3';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: '标题4';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: '标题5';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: '标题6';
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: '标准字体';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
content: '衬线字体';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
content: '等宽字体';
}
</style>