vue使用富文本编辑器wangEditor
wangEditor是一个轻量级 web 富文本编辑器,配置方便,使用简单。官方文档:Introduction · wangEditor 用户文档本文简单介绍vue如何使用wangEditor,进行这些操作(编辑富文本内容/上传图片/删除图片/显示内容)。...
wangEditor是一个轻量级 web 富文本编辑器,配置方便,使用简单。
官方文档:Introduction · wangEditor 用户文档
本文简单介绍vue如何使用wangEditor,进行这些操作(编辑富文本内容/显示内容/上传图片/删除图片)。后台数据库中保存的富文本就是html,顺便把图片的地址一并保存在里面。
1. npm安装wangEditor:
npm install wangEditor
2.单个Vue页面使用
<template>
<div>
<!--编辑区域-->
<div ref="editor" v-html="textEditor"></div>
<!--显示区域,一定要用样式-->
<pre class="sd_pre_change_note" style="overflow-y: scroll;" v-html="textDescription"></pre>
</div>
</template>
<script>
import wangEditor from 'wangeditor' // 引入 wangEditor
export default {
name: 'JustTest',
data (){
textEditor:'',
textDescription:'',
editor:null, //wangEditor编辑器
imgsrc: [],
},
created () {
this.createEditor()
},
destroyed() {
this.editor.destroy()
this.editor = null
},
methods: {
//创建富文本编辑器
createEditor(){
let that = this
const editor = new wangEditor(this.$refs.editor) //创建编辑器
editor.config.height = 370 // 设置编辑区域高度为 500px
editor.config.excludeMenus = [ // 配置菜单栏,设置不需要的菜单
'todo',
'video'
],
editor.config.showLinkImg = false //隐藏插入网络图片的功能
//文本内容发生变换
editor.config.onchange = (newHtml) => {
this.onContentchange(newHtml)
this.textDescription = newHtml
}
//上传文件
editor.config.uploadImgServer = 'http://127.0.0.1:8000/backend/upload'
editor.config.uploadImgMaxSize = 2 * 1024 * 1024 // 2M
editor.config.uploadImgAccept = ['jpg', 'jpeg', 'png']
editor.config.uploadImgMaxLength = 3 // 一次最多上传 3 个图片
editor.config.uploadImgParams = {
updir: 'detail',
}
editor.config.uploadFileName = 'image'
editor.config.uploadImgHooks = {
before: function(xhr) { // 上传图片之前
// 可阻止图片上传
// return {
// prevent: true,
// msg: '需要提示给用户的错误信息'
// }
},
success: function(xhr) { // 图片上传并返回了结果,图片插入已成功
console.log('success', xhr)
},
fail: function(xhr, editor, resData) { // 图片上传并返回了结果,但图片插入时出错了
this.$message({type: 'error', message: '插入图片失败!'})
console.log('fail', resData)
},
error: function(xhr, editor, resData) { // 上传图片出错,一般为 http 请求的错误
this.$message({type: 'error', message: '上传图片失败!'})
console.log('error', xhr, resData)
},
timeout: function(xhr) { // 上传图片超时
this.$message({type: 'error', message: '上传图片超时!'})
console.log('timeout')
},
// 图片上传并返回了结果,想要自己把图片插入到编辑器中
// 例如服务器端返回的不是 { errno: 0, data: [...] } 这种格式,可使用 customInsert
customInsert: function(insertImgFn, result) {
// result 即服务端返回的接口
// insertImgFn 可把图片插入到编辑器,传入图片 src ,执行函数即可
insertImgFn(result.data[0])
that.imgsrc = that.getSrc(editor.txt.html()) //获取富文本中包含的所有图片地址
}
}
editor.create()
this.editor = editor
},
// html 即变化之后的内容, 删除图片
onContentchange(html){
if (this.imgsrc.length !== 0) {
let nowimgs = this.getSrc(html)
let merge = this.imgsrc.concat(nowimgs).filter(function (v, i, arr) {
return arr.indexOf(v) === arr.lastIndexOf(v)
})
//后台请求,删除图片
let params = {imgPaths:JSON.stringify(merge)}
this.axios.post(`/auditInfo/deleteImg`,qs.stringify(params))
.then(response =>{
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
this.imgsrc = nowimgs
}
},
//得到html中的图片地址
getSrc (html) {
var imgReg = /<img.*?(?:>|\/>)/gi
// 匹配src属性
var srcReg = /src=[\\"]?([^\\"]*)[\\"]?/i
var arr = html.match(imgReg)
let imgs = []
if (arr) {
for (let i = 0; i < arr.length; i++) {
var src = arr[i].match(srcReg)[1]
imgs.push(src)
}
}
return imgs
},
}
}
</script>
<style scoped lang="less">
/*wangEditor css*/
/deep/.sd_pre_change_note{
table {
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
}
table td, table th {
border-bottom: 1px solid #ccc;
border-right: 1px solid #ccc;
padding: 3px 5px;
}
table th {
background-color: #f1f1f1;
border-bottom: 2px solid #ccc;
text-align: center;
}
/* blockquote 样式 */
blockquote {
display: block;
border-left: 8px solid #d0e5f2;
padding: 5px 10px;
margin: 10px 0;
line-height: 1.4;
font-size: 100%;
background-color: #f1f1f1;
}
/* code 样式 */
code {
display: inline-block;
*display: inline;
*zoom: 1;
background-color: #f1f1f1;
border-radius: 3px;
padding: 3px 5px;
margin: 0 3px;
}
pre code {
display: inline-flex;
}
/* ul ol 样式 */
ul, ol {
margin: 10px 0 10px 20px;
}
</style>
后台部分 urls.py
from django.conf.urls import url
from django.urls import path
from Apps.AuditList import auditList as auditListViews
urlpatterns = [
...
path('auditInfo/deleteImg', auditListViews.deleteImg, name='deleteImg'),
path('backend/upload', auditListViews.uploadImags, name='uploadImags'),
url(r'^auditImg/(?P<path>.*)$', serve, {'document_root': os.path.join(BASE_DIR, "static/img/detail"),}), #显示静态资源 图片
...
]
views.py
from django.http import JsonResponse, HttpResponse
import os
import re
import json
def deleteImg(request):
if request.method == 'POST':
response = {}
try:
imgPaths = json.loads(request.POST.get('imgPaths','[]'))
for i in imgPaths:
matchData = re.search(r'auditImg/(.*)', i)
if matchData:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
file_path = BASE_DIR + '/static/img/detail/'+matchData.groups()[0] #拼出server上保存的图片路径
os.remove(file_path)
response['msg'] = 'success'
response['error_num'] = 0
except Exception as e:
print(traceback.format_exc(limit=1))
response['msg'] = str(e)
response['error_num'] = 1
return JsonResponse(response)
#audit富文本 上传图片 图片保存在/static/img/detail/下
def uploadImags(request):
if request.method == 'POST':
resp = {'errno': 100, 'data': '请选择图片'}
upfiles = request.FILES.getlist('image', None)
updir = request.POST.get('updir', 'common')
if(upfiles == None):
return HttpResponse(json.dumps(resp), content_type="application/json")
resUrls = []
resIds = []
for up in upfiles:
upfileres = _upload(up, updir)
resUrls.append(upfileres['url'])
resIds.append(upfileres['id'])
#前端使用的plopload上传,一次上传一张,多张分多次上传, 使用的wangeditor,是多张图片是一次上传的
if updir == 'detail':
resp = {'errno': 0, 'data': resUrls}
else:
resp = {'errno': 200, 'id': resIds[0], 'url': resUrls[0]}
return HttpResponse(json.dumps(resp), content_type="application/json")
def _upload(upfile, updir):
new_file_name = _hash_filename(upfile.name)
res_save_path = _get_path(updir)
save_path = res_save_path['save_path']
local_save_path = res_save_path['local_save_path']
local_save_file = local_save_path + new_file_name
save_file = save_path + new_file_name
url = 'http://127.0.0.1:8000/' + save_file.replace('static/img/'+updir,'auditImg')
with open(local_save_file, 'wb') as f:
for line in upfile.chunks():
f.write(line)
f.close()
return {'id': 23, 'url': url}
def _hash_filename(filename):
_, suffix = os.path.splitext(filename)
return '%s%s' % (uuid.uuid4().hex, suffix)
def _get_path(updir):
if(updir == ''):
path = 'static/img/' + time.strftime("%Y%m%d", time.localtime()) + '/'
else:
path = 'static/img/' + updir + '/' + time.strftime("%Y%m%d", time.localtime()) + '/'
# 本地储存路径
local_save_path = path
# 数据库存储路径
save_path = os.path.join(path)
isExists = os.path.exists(local_save_path)
if not isExists:
os.makedirs(local_save_path)
return {'save_path': save_path, 'local_save_path': local_save_path}
注:该文省略了保存富文本html到后台的步骤,省略了读取后台保存的富文本html步骤
期间遇到的问题:
1.富文本插入表情,保存html到后台失败
要使用富文本的表情图,需要设置该字段的格式为utf8mb4
ALTER TABLE auditinfo MODIFY testDescription TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
SHOW FULL COLUMNS FROM auditinfo;
2.插入图片成功了,但是还是要实现删除图片的功能,防止服务器保存无用的信息
网上搜了删除图片的功能,成功添加
目录/大纲的使用
当编辑的文章过长时,可以使用WangEditor的Scroll-To-Head,获取编辑区域的所有标题,生成目录/大纲,可以通过点击大纲让编辑器滚动到某个标题。
官方文档:Scroll To Head · wangEditor 用户文档
代码实现:
创建编辑器的时候,配置 editor.config.
onCatalogChange,可以实时获取编辑器所有标题,标题变化即可触发该回调函数。
<el-row :gutter="20">
<el-col :span="headChecked?18:24">
<div ref="editor" v-html="noteEditor"></div>
<el-input type="textarea" v-model="noteContent" readonly v-show="false" ></el-input>
</el-col>
<el-col :span="headChecked?6:0" style="background:#F2F6FC">
<h4 style="text-align:center">目 录</h4>
<el-divider></el-divider>
<ul style="height:400px;overflow-y: scroll">
<li v-for="head in contentHeadList" :key="head.id">
<el-link :underline="false" v-if="head.tag=='H1'" style="font-size:16px" @click="scrollTo(head.id)">{{head.text}}</el-link>
<el-link :underline="false" v-else-if="head.tag=='H2'" @click="scrollTo(head.id)"> {{head.text}}</el-link>
<el-link :underline="false" v-else @click="scrollTo(head.id)"> {{head.text}}</el-link>
</li>
</ul>
</el-col>
</el-row>
//滚动到某个标题
editor.config.onCatalogChange = function (headList) {
this.headList = headList
console.log(headList)
/*
headList 格式
[
{
id: "eold9", // 标题 id
tag: "H1",
text: "标题文字"
},
{ ... },
{ ... }
]
*/
}
____________________________________________________________
scrollTo(headId){
this.editor.scrollToHead(headId)
}
后台数据库保存,编辑内容noteContent和标题headList,内容noteContent中,标题也是带着 id 的,格式如下。
<h1 id="eold9">标题一</h1>
<p>正文</p>
<h2 id="nh339">标题二</h2>
<p>正文</p>
<h3 id="5mgwk">标题三</h3>
<p>正文</p>
可以使用这些 id 来做锚点,实现 scroll to head 的功能。
更多推荐
所有评论(0)