<template>
<div class="in-editor-wrapper">
<div :class="this.className"></div>
<form
action
method="post"
enctype="multipart/form-data"
id="uploadFormMulti"
>
<input
style="display: none"
:id="uniqueId"
type="file"
name="file"
multiple
accept="image/jpg, image/jpeg, image/png, image/gif"
@change="uploadImgEditor('uploadFormMulti')"
/>
</form>
</div>
</template>
<script>
import { uploadImg } from "@/api/uploadImg";
import Quill from "quill";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "./styles/editor.css";
import imageResize from "quill-blot-formatter";
import QuillBetterTable from "quill-better-table";
import "quill-better-table/dist/quill-better-table.css";
Quill.register(
{
"modules/better-table": QuillBetterTable,
},
true
);
Quill.register("modules/imageResize", imageResize);
const toolbarOptions = [
["bold", "italic", "underline", "strike"],
["blockquote", "code-block"],
[{ header: 1 }, { header: 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: [] }],
[{ align: [] }],
["clean"],
["image"],
[{ table: "TD" }],
];
export default {
name: "MEditor",
props: {
editContent: String,
className: {
type: String,
default: "in-editor",
},
},
data() {
return {
editor: null,
uniqueId: this.className,
content: this.editContent,
options: {
theme: "snow",
modules: {
toolbar: {
container: toolbarOptions,
handlers: {
table: function () {
this.quill.getModule("better-table").insertTable(3, 3);
},
},
},
table: false,
"better-table": {
operationMenu: {
items: {
insertColumnRight: {
text: "向右插入列",
},
insertColumnLeft: {
text: "向左插插列",
},
insertRowUp: {
text: "向上插入行",
},
insertRowDown: {
text: "向下插入行",
},
mergeCells: {
text: "合并单元格",
},
unmergeCells: {
text: "取消合并单元格",
},
deleteColumn: {
text: "删除列",
},
deleteRow: {
text: "删除行",
},
deleteTable: {
text: "删除表格",
},
},
background: {
color: "#333",
},
color: {
colors: ["green", "red", "yellow", "blue", "white"],
text: "背景色:",
},
},
},
imageResize: {
displayStyles: {
backgroundColor: "black",
border: "none",
color: "white",
},
modules: ["Resize", "DisplaySize", "Toolbar"],
},
clipboard: {
matchVisual: false,
},
keyboard: {
bindings: QuillBetterTable.keyboardBindings,
},
},
placeholder: "请输入内容 ...",
},
};
},
watch: {
editContent(val) {
if (!this.editor) {
return;
}
let content = this.editor.root.innerHTML;
if (val && val !== content) {
let delta = this.editor.clipboard.convert({
html: val,
});
console.log(delta);
this.editor.setContents(delta);
}
},
},
mounted() {
this.initEditor();
var imgHandler = async (image) => {
if (image) {
var fileInput = document.getElementById(this.uniqueId);
fileInput.click();
}
};
this.editor.getModule("toolbar").addHandler("image", imgHandler);
this.editor.on("text-change", () => {
this.$emit("getContent", this.editor.root.innerHTML);
});
},
methods: {
initEditor() {
let editorDom = this.$el.querySelector(`.${this.className}`);
this.editor = new Quill(editorDom, this.options);
this.editor.on("text-change", () => {
this.$emit("input", this.editor.root.innerHTML);
this.$emit("getContent", this.editor.root.innerHTML);
});
this.setupPasteHandler();
let delta = this.editor.clipboard.convert({
html: this.content,
});
this.editor.setContents(delta);
},
setupPasteHandler() {
this.editor.root.addEventListener("paste", async (e) => {
if (!e.clipboardData?.items) return;
const items = e.clipboardData.items;
let htmlContent = "";
const imageFiles = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
console.log(item);
if (item.type.includes("text/html")) {
htmlContent = await new Promise((resolve) => {
item.getAsString(resolve);
});
} else if (item.type.includes("image")) {
imageFiles.push(item.getAsFile());
}
}
console.log(imageFiles);
if (imageFiles.length > 0) {
e.preventDefault();
await this.processMixedContent(this.editor, htmlContent, imageFiles);
}
});
},
async processMixedContent(quill, htmlContent, imageFiles) {
const tempDiv = document.createElement("div");
tempDiv.innerHTML = htmlContent;
const images = tempDiv.querySelectorAll("img");
for (let i = 0; i < images.length; i++) {
if (imageFiles[i]) {
let content = {
file: imageFiles[i],
data: { type: "image", xtype: "ysb_product", editor: true },
};
const imageUrl = await this.uploadImage(content);
images[i].src = imageUrl;
}
}
const delta = quill.clipboard.convert(tempDiv.innerHTML);
quill.setContents(delta, "silent");
},
uploadImage(content) {
try {
uploadImg(content).then((res) => {
let url = res.data.url;
if (url != null && url.length > 0) {
let Range = this.editor.getSelection();
url = url.indexOf("http") != -1 ? url : "http:" + url;
this.editor.insertEmbed(
Range != null ? Range.index : 0,
"image",
url
);
} else {
this.$message.warning("图片上传失败");
}
document.getElementById(this.uniqueId).value = "";
});
} catch ({ message: msg }) {
document.getElementById(this.uniqueId).value = "";
this.$message.warning(msg);
}
},
uploadImgEditor: async function () {
let content = {
file: document.getElementById(this.uniqueId).files[0],
data: { type: "image", xtype: "ysb_product", editor: true },
};
let fileArr = document.getElementById(this.uniqueId).files;
for (const el of fileArr) {
let content = {
file: el,
data: { type: "image", xtype: "ysb_product", editor: true },
};
this.uploadImage(content);
}
},
},
};
</script>