| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- <template>
- <div class="my-editor">
- <div
- class="quill-editor"
- :content="content"
- :placeholder="placeholder || ''"
- @change="handleChange"
- v-quill:[quillName]="editorOption"
- ></div>
- <input type="file" accept="image/png, image/jpeg" :id="'imgInput'+quillName" @change="handleContentFileChange($event)"
- style="display: none;" />
- <el-dialog title="提示" :visible.sync="linkDialog" :before-close="handleLinkClose">
- <el-form :model="link">
- <el-form-item label="标题" :label-width="formLabelWidth">
- <el-input v-model="link.title" autocomplete="off"></el-input>
- </el-form-item>
- <el-form-item label="链接" :label-width="formLabelWidth">
- <el-input v-model="link.url" autocomplete="off"></el-input>
- </el-form-item>
- </el-form>
- <span slot="footer" class="dialog-footer">
- <el-button @click="handleLinkClose">取 消</el-button>
- <el-button type="primary" @click="handleLinkOk">确 定</el-button>
- </span>
- </el-dialog>
- </div>
- </template>
- <script>
- import Vue from "vue";
- import "quill/dist/quill.core.css";
- import "quill/dist/quill.snow.css";
- // import 'quill/dist/quill.bubble.css'
- let handerLink = null
- if (process.browser) {
- const VueQuillEditor = require("vue-quill-editor/dist/ssr");
- Vue.use(VueQuillEditor);
- handerLink = VueQuillEditor.Quill.import("formats/link")
- }
- import hljs from "hljs";
- export default {
- props: ["content", "hideImage", "placeholder", "haveVideo"],
- components: {
- },
- data() {
- const extra = ["link"];
- if (!this.hideImage) {
- extra.push("image");
- }
- // if (this.haveVideo) {
- // extra.push("video");
- // }
- let placeholder = this.placeholder
- return {
- editorOption: {
- name: Math.random(),
- theme: "snow",
- placeholder: placeholder || "请输入正文...",
- modules: {
- toolbar: [
- ["bold", "italic", "underline", "strike"],
- ["blockquote"],
- [{ list: "ordered" }, { list: "bullet" }],
- [{ indent: "-1" }, { indent: "+1" }],
- [{ size: [false, "small", "large", "huge"] }],
- [{ header: [false, 1, 2, 3, 4, 5, 6] }],
- [{ font: [] }],
- [{ color: [] }, { background: [] }],
- [{ align: [] }],
- extra
- ],
- syntax: {
- highlight: text => hljs.highlightAuto(text).value
- }
- }
- },
- formLabelWidth: "60px",
- linkDialog: false,
- link: {
- title: "",
- url: ""
- },
- quillName: 'myQuillEditor' + Math.floor(Math.random() * 1000000000)
- };
- },
- computed: {},
- mounted() {
- console.log('this.quillName', this.quillName)
- // 为图片ICON绑定事件 getModule 为编辑器的内部属性
- this[this.quillName]
- .getModule("toolbar").addHandler("image", this.imgHandler);
- this.changeParseEvent()
- this.addLinkEvent()
- },
- methods: {
- handleChange(e) {
- if (typeof e.html === "undefined") {
- return;
- }
- this.$emit("change", e.html);
- this.$emit("changeText", e.text);
- },
- // 点击图片ICON触发事件
- imgHandler(state) {
- this.addRange = this[this.quillName].getSelection();
- if (state) {
- let fileInput = document.getElementById("imgInput"+this.quillName);
- fileInput.click(); // 加一个触发事件
- }
- this.uploadType = "image";
- },
- //change parse
- changeParseEvent() {
- // 自定义粘贴图片功能
- this[this.quillName].root.addEventListener('paste', evt => {
- console.log("evt", evt)
- if (evt.clipboardData && evt.clipboardData.files && evt.clipboardData.files.length) {
- evt.preventDefault();
- [].forEach.call(evt.clipboardData.files, file => {
- if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
- return;
- }
- this.handleContentFileChange({target: {files:[file]}})
- });
- }
- }, false);
- },
- addLinkEvent() {
- let dom = document.getElementsByClassName("ql-link")
- console.log("dom", dom)
- if (dom.length === 0) {
- setTimeout(()=>{
- this.addLinkEvent()
- }, 2000)
- } else {
- dom[0].addEventListener("click", this.linkHandler.bind(this), false)
- }
- },
- // 点击link触发事件
- linkHandler() {
- const selectedRange = this[this.quillName].getSelection();
- if (selectedRange && !selectedRange.length ) {
- this.$message.warning("请先选择需要插入超链接的内容")
- }
- },
- handleLinkClose() {
- this.linkDialog = false;
- this.link = {
- title: "",
- url: ""
- };
- },
- handleLinkOk() {
- const link = this.link;
- this[this.quillName].deleteText(link.index, link.length);
- this[this.quillName].insertEmbed(link.index, "link", {
- href: link.url,
- innerText: link.title
- });
- // this[this.quillName].insertText(
- // link.index,
- // `<a href="${this.link.url}">${this.link.title}</a>`
- // );
- this.handleLinkClose();
- },
- // 点击视频ICON触发事件
- videoHandler(state) {
- this.addRange = this[this.quillName].getSelection();
- if (state) {
- let fileInput = document.getElementById("imgInput"+this.quillName);
- fileInput.click(); // 加一个触发事件
- }
- // this.uploadType = "video";
- },
- handleContentFileChange(e) {
- const file = e.target.files[0];
- if (file.size / 1024/1024 > 2) {
- this.$message.error("图片大小不得超过2M,请重新选择");
- return false;
- }
- const formData = new FormData();
- formData.append("file", file);
- formData.append("original_filename", file.name);
- this.$axios
- .$post(`/upload_image`, formData, {
- headers: { "Content-Type": "multipart/form-data" }
- })
- .then(res => {
- const index = this[this.quillName].selection.savedRange.index;
- this[this.quillName].insertEmbed(index, "image", res.filename);
- });
- }
- }
- };
- </script>
- <style>
- /* editor: custom style & custom content */
- /* font-size */
- .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: "S" !important;
- }
- .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: "L" !important;
- }
- .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: "H" !important;
- }
- /* header */
- .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: "H1" !important;
- }
- .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: "H2" !important;
- }
- .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: "H3" !important;
- }
- .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: "H4" !important;
- }
- .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: "H5" !important;
- }
- .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: "H6" !important;
- }
- </style>
- <style lang="scss">
- .my-editor {
- position: relative;
- width: 100%;
- height: 100%;
- min-height: 244px;
- background: #fff;
- .ql-toolbar {
- border-width: 0 !important;
- }
- .ql-editor,
- .quill-editor {
- min-height: 244px;
- border: 0 !important;
- font-size: 14px;
- line-height: 25px;
- }
- .ql-snow.ql-toolbar::after {
- display: inline-block;
- }
- }
- strong {
- font-weight: bold !important;
- }
- em {
- font-style: italic !important;
- }
- </style>
|