需求:一个页面需要多个 富文本编辑器 ,暂选vue-quill-editor。
在使用过程中,添加 上传图片、视频的操作。单个编辑器正常使用,复用之后,第二个编辑器上传成功后找不到quill正确对象,还是用的第一个编辑器的对象。(请教大神后,需添加index,如下图,需要传不同的组件id,当时着急就没有仔细调试了),还未使用,目前使用vue-quill-editor 和 vue-quill-editor-extend,哈哈哈哈,当时急着用,增强版的里面没有视频,还得单独写,就两个一起用了。
更新同一组件冲突问题:简单解决方法,实例化两次,分别调用。亲测管用。我在试试传不同组件id试试
<el-form-item label="绘本介绍">
<UploadEditorImage1 :pcontent="form.introduction" @content="childByIntroduction" key="1"></UploadEditorImage1>
</el-form-item>
<el-form-item label="其他">
<!-- <el-input type="textarea" :rows="2" placeholder="请输入内容" v-model="form.content"></el-input> -->
<UploadEditorImage :pcontent="form.content" @content="childByContent" key="2"></UploadEditorImage>
</el-form-item>
...
...
...
import UploadEditorImage from "../util/UploadEditorImage";
import UploadEditorImage1 from "../util/UploadEditorImage";
export default {
components: { /*UploadEditor,*/ UploadEditorImage,UploadEditorImage1 },
下载vue-quill-editor ,vue-quill-editor-extend
Editor.vue
<template>
<div>
<!-- 图片上传组件辅助-->
<el-upload
class="pic-uploader"
:action="serverUrl"
accept="image/jpg, image/jpeg, image/png, image/gif"
:data="{method:'upload_files_qiniu',type:'image'}"
:show-file-list="false"
:on-success="uploadSuccess"
:on-error="uploadError"
:before-upload="beforeUpload"
:multiple="true"
></el-upload>
<!-- 视频上传组件辅助-->
<el-upload
class="mp4-uploader"
:action="serverUrl"
accept="audio/mpeg"
:data="{method:'upload_files_qiniu',type:'video'}"
:show-file-list="false"
:on-success="uploadSuccess"
:on-error="uploadError"
:before-upload="beforeUploadvideo"
></el-upload>
<quill-editor
class="editor"
v-model="content"
:ref="myQuillEditor"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)"
></quill-editor>
</div>
</template>
<script>
import { addQuillTitle } from "./quill-title.js";
// 工具栏配置
const toolbarOptions = [
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
["blockquote", "code-block"], // 引用 代码块
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
[{ indent: "-1" }, { indent: "+1" }], // 缩进
[{ size: ["small", false, "large", "huge"] }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ font: [] }], // 字体种类
[{ align: [] }], // 对齐方式
["clean"], // 清除文本格式
["link", "image", "video"], // 链接、图片、视频
["sourceEditor"]
];
export default {
props: {
pcontent: ""
},
data() {
return {
myQuillEditor: "myQuillEditor",
content: "",
quillUpdateImg: false, // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示
editorOption: {
placeholder: "",
theme: "snow", // or 'bubble'
placeholder: "",
modules: {
toolbar: {
container: toolbarOptions,
handlers: {
shadeBox: null,
// 添加工具方法
sourceEditor: function() {
// alert('我新添加的工具方法');
const container = this.container;
const firstChild = container.nextElementSibling.firstChild;
// 在第一次点击源码编辑的时候,会在整个工具条上加一个div,层级比工具条高,再次点击工具条任意位置,就会退出源码编辑。可以在下面cssText里面加个背景颜色看看效果。
if (!this.shadeBox) {
let shadeBox = (this.shadeBox = document.createElement(
"div"
));
shadeBox.style.cssText =
"position:absolute; top:0; left:0; width:100%; height:100%; cursor:pointer;background-color:rgba(255,255,255,0.7);";
container.style.position = "relative";
container.appendChild(shadeBox);
firstChild.innerText = firstChild.innerHTML;
shadeBox.addEventListener(
"click",
function() {
this.style.display = "none";
firstChild.innerHTML = firstChild.innerText.trim();
},
false
);
} else {
this.shadeBox.style.display = "block";
firstChild.innerText = firstChild.innerHTML;
}
},
image: function(value) {
if (value) {
// 触发input框选择图片文件
document.querySelector(".pic-uploader input").click();
} else {
this.quill.format("image", false);
}
},
video: function(value) {
if (value) {
// 触发input框选择视频文件
document.querySelector(".mp4-uploader input").click();
} else {
this.quill.format("video", false);
}
}
}
}
}
},
serverUrl: "http://bzhw/book_back.php", // 这里写你要上传的图片服务器地址
header: {
// token: sessionStorage.token 有的图片服务器要求请求头需要有token
}
};
},
created: function() {
var that = this;
that.content = that.pcontent;
},
watch: {
pcontent(newValue, oldValue) {
var that = this;
that.content = newValue;
},
},
mounted() {
var that = this;
addQuillTitle();
that.initButton();
},
methods: {
// 在使用的页面中初始化按钮样式
initButton: function() {
// 样式随便改
const sourceEditorButton = document.querySelector(".ql-sourceEditor");
sourceEditorButton.style.cssText = "font-size:18px";
// 加了elementui的icon
sourceEditorButton.classList.add("el-icon-edit-outline");
// 鼠标放上去显示的提示文字
sourceEditorButton.title = "源码编辑";
},
onEditorBlur() {
//失去焦点事件
},
onEditorFocus() {
//获得焦点事件
},
onEditorChange() {
//内容改变事件
this.$emit("content", this.content);
},
// 富文本图片、视频上传前
beforeUploadvideo() {
// 显示loading动画
this.quillUpdateImg = true;
},
beforeUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 3;
if (!isLt2M) {
this.$message.error("上传图片大小不能超过 3MB!");
}
return isLt2M;
},
uploadSuccess(res, file) {
console.log(res);
let quill = this.$refs.myQuillEditor.quill; // 获取富文本组件实例
if (res.code == 1) {
//let length = quill.getSelection().index; // 获取光标所在位置
let length = quill.selection.savedRange.index;
quill.insertEmbed(length, res.data.type, res.data.url); // 插入资源 res.url为服务器返回的地址
quill.setSelection(length + 1); // 调整光标到最后
} else {
this.$message.error("插入失败");
}
// loading动画消失
this.quillUpdateImg = false;
},
// 富文本图片上传失败
uploadError() {
// loading动画消失
this.quillUpdateImg = false;
this.$message.error("资源插入失败");
}
}
};
</script>
<style>
.pic-uploader {
display: none;
}
.mp4-uploader {
display: none;
}
.editor {
line-height: normal !important;
height: 150px;
padding-bottom: 20px;
margin-bottom: 20px;
}
</style>
增强版的
<template>
<div class="quill-wrap">
<quill-editor
class="editor"
v-model="content"
ref="myQuillEditor"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)"
></quill-editor>
</div>
</template>
<script>
import { quillEditor, Quill } from "vue-quill-editor";
import { container, ImageExtend, QuillWatch } from "quill-image-extend-module";
Quill.register("modules/ImageExtend", ImageExtend);
import { addQuillTitle } from "./quill-title.js";
// 工具栏配置
const toolbarOptions = [
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
["blockquote", "code-block"], // 引用 代码块
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
[{ indent: "-1" }, { indent: "+1" }], // 缩进
[{ size: ["small", false, "large", "huge"] }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ font: [] }], // 字体种类
[{ align: [] }], // 对齐方式
["clean"], // 清除文本格式
["link", "image"] // 链接、图片
];
export default {
props: {
pcontent: ""
},
components: { quillEditor },
data() {
return {
content: "",
// 富文本框参数设置
editorOption: {
placeholder: "",
modules: {
ImageExtend: {
loading: true,
name: "file",
action:
"http://baby.zhw.php?method=upload&type=image",
response: res => {
console.log(res);
return res.data.url;
}
},
toolbar: {
container: toolbarOptions,
handlers: {
image: function() {
QuillWatch.emit(this.quill.id);
}
}
}
}
}
};
},
mounted() {
addQuillTitle();
},
created: function() {
var that = this;
that.content = that.pcontent;
},
watch: {
pcontent(newValue, oldValue) {
var that = this;
that.content = newValue;
}
},
methods: {
onEditorBlur() {
//失去焦点事件
},
onEditorFocus() {
//获得焦点事件
},
onEditorChange() {
//内容改变事件
this.$emit("content", this.content);
}
}
};
</script>
<style>
.pic-uploader {
display: none;
}
.mp4-uploader {
display: none;
}
.editor {
line-height: normal !important;
height: 150px;
padding-bottom: 20px;
margin-bottom: 20px;
}
</style>
quill-title.js
const titleConfig = {
'ql-bold':'加粗',
'ql-color':'颜色',
'ql-font':'字体',
'ql-code':'插入代码',
'ql-italic':'斜体',
'ql-link':'添加链接',
'ql-background':'背景颜色',
'ql-size':'字体大小',
'ql-strike':'删除线',
'ql-script':'上标/下标',
'ql-underline':'下划线',
'ql-blockquote':'引用',
'ql-header':'标题',
'ql-indent':'缩进',
'ql-list':'列表',
'ql-align':'文本对齐',
'ql-direction':'文本方向',
'ql-code-block':'代码块',
'ql-formula':'公式',
'ql-image':'图片',
'ql-video':'视频',
'ql-clean':'清除字体样式',
'ql-sourceEditor':'源码编辑'
};
export function addQuillTitle(){
const oToolBar = document.querySelector('.ql-toolbar'),
aButton = oToolBar.querySelectorAll('button'),
aSelect = oToolBar.querySelectorAll('select');
aButton.forEach(function(item){
if(item.className === 'ql-script'){
item.value === 'sub' ? item.title = '下标': item.title = '上标';
}else if(item.className === 'ql-indent'){
item.value === '+1' ? item.title ='向右缩进': item.title ='向左缩进';
}else{
item.title = titleConfig[item.classList[0]];
}
});
aSelect.forEach(function(item){
item.parentNode.title = titleConfig[item.classList[0]];
});
}
服务器端保存资源(qiniu 配置略)
//七牛云
require_once __DIR__ . "/../../util/php-sdk-7.2.9/autoload.php";
use Qiniu\Auth;
use Qiniu\Storage\UploadManager;
/**
* 七牛云的配置类
*/
class BaseConfig
{
public static $access_key = '2y9G******NJk6-p';
public static $secret_key = 'aX0*********i8-rSf';
public static $bucketname = 'm****s';
}
...
...
...
/**
* 静态资源上传
* @param $_FILES 文件
* @param string $type 资源类型,默认为图片 image,将会保存到类型文件夹中
* @param string $saveName 文件保存名 默认随机名:date("YmdHis") . rand(1000, 99999);
* @return array $arr['ret'] 状态 ,$arr['msg'] 提示,$arr['data'] 保存后的资源路径
*/
public function upload($saveName = '')
{
$file = isset($_FILES['file']) ? $_FILES['file'] : null;
if (is_null($file) || empty($file['tmp_name'])) {
$arr = array(
'ret' => 0,
'msg' => '文件有误'
);
echo json_encode($arr);
die;
};
$type = isset($this->get['type']) ? $this->get['type'] : 'image';
$file_dir = '/opt/zhw*********/' . $type . '/' . date("Ymd/");
$file_name = $saveName != '' ? $saveName : date("YmdHis") . rand(1000, 99999);
$data = array();
if (!file_exists($file_dir)) {
mkdir($file_dir, 0777, true); //原图路径
}
if ($file["error"] > 0) {
$arr = array(
'code' => 0,
'msg' => '文件上传失败:' . $file["error"]
);
} else {
$tmp = '.' . end(explode(".", $file['name']));
$url = $file_dir . $file_name . $tmp;
$st = move_uploaded_file($file["tmp_name"], $url);
//获取音频名、时长
if ($type == 'mp3') {
$mp3_name = reset(explode(".", $file['name']));
$ThisFileInfo = $this->getID3->analyze($url);
$fileduration = $ThisFileInfo['playtime_seconds'];
$data['name'] = $mp3_name;
$data['dura'] = (int) $fileduration;
}
$data['type'] = $type;
if ($st) {
$url = str_replace('/opt/zhw*********/', 'http://fzhw.com', $url);
$data['url'] = $url;
$arr = array(
'code' => 1,
'msg' => '文件上传成功',
'data' => $data
);
} else {
$arr = array(
'code' => 0,
'msg' => '文件保存失败'
);
}
}
echo json_encode($arr);
}
/**
* 上传到七牛(先上传的七牛,在保存到本地,七牛云保存名是本地文件名)
* @param $_FILES 文件
*/
public function upload_files_qiniu()
{
$file = isset($_FILES['file']) ? $_FILES['file'] : null;
if (is_null($file) || empty($file['tmp_name'])) {
$arr = array(
'ret' => 0,
'msg' => '文件有误'
);
echo json_encode($arr);
die;
}
$fileInfo = $file;
$fileName = $fileInfo['tmp_name'];
$key = date("YmdHis") . rand(1000, 99999);
$qn_key = 'qn_mmsq_hb_' . $key;
$upToken = $this->auth->uploadToken(BaseConfig::$bucketname);
list($ret, $err) = $this->uploadMgr->putFile($upToken, $qn_key, $fileName);
if (!is_null($err)) {
$arr = array(
'ret' => 0,
'msg' => $err
);
echo json_encode($arr);
die;
};
//保存到本地
$this->upload($key);
/*
$tmpinfo = file_get_contents('http://rs.zhw.com/' . $ret['key'] . '?avinfo');
$tmpinfo = json_decode($tmpinfo, true);
$duration = intval($tmpinfo['format']['duration']); //时长
$filesize = $tmpinfo['format']['size'];
*/
}