vue-quill-editor rich text image and video upload settings

<template>
  <div class="card" style="overflow: hidden; padding-bottom: 10px">
    <div class="box">
      <div>
        <div class="box_count">
          <div class="box_l">
            <FlexLinePI :title="'图文标题'" :dropshow="true">
              <el-input
                v-model.trim="ruleForm.title"
                maxlength="50"
                type="text"
                placeholder="请输入标题"
                v-clear-emoij
                @input="(e) => (ruleForm.title = formateText(e))"
              ></el-input>
            </FlexLinePI>
            <FlexLinePI :title="'图文摘要'" :dropshow="true"
              ><el-input
                v-model.trim="ruleForm.note"
                type="textarea"
                maxlength="200"
                show-word-limit
                rows="5"
                placeholder="请输入正文详情"
                v-clear-emoij
                @input="(e) => (ruleForm.note = formateText(e))"
              ></el-input>
            </FlexLinePI>
            <FlexLinePI :title="'封面图片'" :dropshow="true">
              <el-upload
                class="avatar-uploader"
                :action="upload"
                :show-file-list="false"
                :on-success="handleAvatarSuccess"
                :before-upload="beforeAvatarUpload"
              >
                <img
                  v-if="ruleForm.cover"
                  :src="ruleForm.cover"
                  class="avatar"
                />
                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
                <div slot="tip" class="el-upload__tip">
                  尺寸720x1280px,大小不超过5M
                </div>
              </el-upload>
            </FlexLinePI>
            <FlexLinePI :title="'图文描述'" :dropshow="true" :position="false">
              <div>
                <quill-editor
                  v-model="ruleForm.content"
                  ref="quillEditor"
                  :options="editorOption"
                >
                </quill-editor>
                <div class="rich-uploader">
                  <!-- 图片上传组件辅助-->
                  <el-upload
                    class="rich-uploader2"
                    name="pic"
                    action=""
                    :show-file-list="false"
                    :http-request="httpRequest"
                  >
                  </el-upload>
                  <el-upload
                    class="rich-uploader3"
                    name="pic"
                    action=""
                    :show-file-list="false"
                    :http-request="httpRequestvideo"
                  >
                  </el-upload>
                </div>
              </div>
            </FlexLinePI>
            <div class="buttom">
              <el-button
                type="primary"
                @click="addandedit(0)"
                :loading="loadingbtn"
                >{
   
   { loadingbtn == false ? "保存" : "记载中" }}</el-button
              >
              <el-button
                type="primary"
                @click="addandedit(1)"
                :loading="loadingbtn"
              >
                {
   
   { loadingbtn == false ? "保存并发布" : "记载中" }}
              </el-button>
              <!-- <el-button type="primary" @click="look">预览</el-button> -->
            </div>
          </div>
          <div
            class="box_r"
            :style="{
              'background-image': `url(${require('../../../assets/images/phone.png')}`,
            }"
          >
            <div class="box_r_informationurl">
              <div class="box_r_inImg">
                <img
                  src="../../../assets/images/authorlogo.png"
                  alt=""
                  srcset=""
                  style="width: 100%; height: 100%"
                />
              </div>
              <div class="box_r_inlanguage" style="width: 180px">
                <p
                  style="-webkit-line-clamp: 1; font-size: 18px; width: 100%"
                  class="ellipsis"
                >
                  {
   
   { ruleForm.title != "" ? ruleForm.title : "请输入标题" }}
                </p>
                <div style="display: flex; margin-top: 5px; height: 90px">
                  <div style="flex: 1; -webkit-line-clamp: 5" class="ellipsis">
                    {
   
   { ruleForm.note != "" ? ruleForm.note : "请输入正文详情" }}
                  </div>
                  <div class="box_r_inlanguageimg">
                    <img
                      :src="ruleForm.cover != '' ? ruleForm.cover : urlimg"
                      alt=""
                      srcset=""
                      style="width: 100%; height: 100%"
                    />
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import step from "@/components/EchartsChart/nextstep.vue";
import {
      
       upload, uploadvideo } from "@/api/common.js";
import {
      
       quillEditor, Quill } from "vue-quill-editor";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
import axios from "axios";
import {
      
      
  detailGraphic,
  addGraphic,
  updateGraphic,
} from "@/api/material/index.js";
// 引入修改video模块并注册
import video from "@/assets/js/quillVideo.js";
Quill.register(video);
export default {
      
      
  name: "AddImageText",
  components: {
      
       quillEditor, step },
  computed: {
      
      
    quill() {
      
      
      const {
      
       quill } = this.$refs.quillEditor || {
      
      };
      return quill;
    },
  },
  data() {
      
      
    const toolbarOptions = [
      ["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: [] }], //字体
      [{
      
       align: [] }], //对齐方式

      ["clean"], //清除字体样式
      ["image"], //上传图片
      ["video"], // 视频
    ];
    return {
      
      
      upload: upload,
      uploadvideo: uploadvideo,
      ruleForm: {
      
      
        id: "",
        title: "",
        note: "",
        cover: "",
        content: "",
      },
      // 视频
      progress: 0,
      // 富文本
      editorOption: {
      
      
        placeholder: "请输入内容",
        theme: "snow",
        modules: {
      
      
          toolbar: {
      
      
            container: toolbarOptions, // 工具栏
            handlers: {
      
      
              image: function (value) {
      
      
                if (value) {
      
      
                  console.log(value);
                  // 触发input框选择图片文件
                  document.querySelector(".rich-uploader2 input").click();
                } else {
      
      
                  this.quill.format("image", false);
                }
              },
              video: function (value) {
      
      
                if (value) {
      
      
                  console.log(value);
                  // 触发input框选择图片文件
                  document.querySelector(".rich-uploader3 input").click();
                } else {
      
      
                  this.quill.format("video", false);
                }
              },
            },
          },
        },
      },
      // 上传图片
      urlimg: require("../../../assets/images/gift.png"),

      // 当前id
      updateId: sessionStorage.getItem("updateId")
        ? sessionStorage.getItem("updateId")
        : "",

      // 提交
      loadingbtn: false,
      loading: null,
    };
  },
  mounted() {
      
      
    if (this.updateId == "" || this.updateId == null) {
      
      
      this.ruleForm = {
      
      
        id: "",
        title: "",
        note: "",
        cover: "",
        content: "",
      };
    } else {
      
      
      // 详情
      this.getdetail(this.updateId);
    }
  },
  computed: {
      
      },
  methods: {
      
      
    //
    handleAvatarSuccess(res, file) {
      
      
      if (res.status == 1) {
      
      
        this.$message.success(res.msg);
        this.ruleForm.cover = res.data.real_path;
      }
    },
    beforeAvatarUpload(file) {
      
      
      const isLt2M = file.size / 1024 / 1024 < 5;
      if (!isLt2M) {
      
      
        this.$message.error("上传封面图片大小不能超过 5MB!");
      }
      return isLt2M;
    },
    // 详情
    getdetail(id) {
      
      
      detailGraphic({
      
      
        id: id,
      })
        .then((res) => {
      
      
          if (res.status == 1) {
      
      
            this.$message.success(res.msg);
            this.ruleForm = {
      
      
              id: res.data.id,
              title: res.data.title,
              note: res.data.note,
              cover: res.data.cover,
              content: res.data.content,
            };
          } else {
      
      
            this.$message.warning(res.msg || "数据响应过慢,请稍后再试");
          }
        })
        .catch((err) => {
      
      
          console.log(err);
        });
    },
    //  步骤 2 =====================================================
    httpRequest(item) {
      
      
      let quill = this.$refs.quillEditor.quill;
      let formdata = new FormData();
      formdata.append("file", item.file, item.file.name);
      formdata.append("type", "1");
      let length = quill.getSelection().index;
      quill.insertEmbed(length, "image", item.file);
      axios
        .post(upload, formdata, {
      
      
          header: {
      
       "Content-Type": "multipart/form-data" },
        })
        .then((res) => {
      
      
          if (res.data.status == 1) {
      
      
            let length = quill.getSelection().index;
            // 插入图片  服务器返回的图片地址
            quill.insertEmbed(length, "image", res.data.data.real_path);
            //       // 调整光标到最后
            quill.setSelection(length + 1);
          } else {
      
      
            this.$message.warning(res.msg || "数据响应过慢,请稍后再试");
          }
        })
        .catch((err) => {
      
      
          console.log(err);
        });
    },

    // ==============================================================
    // 保存编辑
    addandedit(val) {
      
      
      if (this.ruleForm.title == "") {
      
      
        this.$message.warning("请输入图文标题");
        return;
      }
      if (this.ruleForm.note == "") {
      
      
        this.$message.warning("请输入图文摘要");
        return;
      }
      if (this.ruleForm.cover == "") {
      
      
        this.$message.warning("请输入封面图片");
        return;
      }
      if (this.ruleForm.content == "") {
      
      
        this.$message.warning("请输入具体内容");
        return;
      }
      this.loadingbtn = true;
      if (this.updateId == "" || this.updateId == null) {
      
      
        let tmpParams = {
      
      
          id: "",
          title: this.ruleForm.title, // 时间
          note: this.ruleForm.note, // 时间
          cover: this.ruleForm.cover,
          content: this.ruleForm.content,
          is_publish: val,
        };
        this.sever(tmpParams);
      } else {
      
      
        let tmpParams = {
      
      
          id: this.updateId,
          title: this.ruleForm.title, // 时间
          note: this.ruleForm.note, // 时间
          cover: this.ruleForm.cover,
          content: this.ruleForm.content,
          is_publish: val,
        };
        this.edit(tmpParams);
      }
    },
    // 保存
    sever(val) {
      
      
      addGraphic(val)
        .then((res) => {
      
      
          if (res.status == 1) {
      
      
            this.$message.success(res.msg);
            this.loadingbtn = false;
            this.$router.push({
      
      
              name: "ImageText",
            });
          } else {
      
      
            this.$message.warning(res.msg || "数据响应过慢,请稍后再试");
          }
        })
        .catch((err) => {
      
      
          console.log(err);
        });
    },
    // 编辑
    edit(val) {
      
      
      updateGraphic(val)
        .then((res) => {
      
      
          if (res.status == 1) {
      
      
            this.$message.success(res.msg);
            this.loadingbtn = false;
            this.$router.push({
      
      
              name: "ImageText",
            });
          } else {
      
      
            this.$message.warning(res.msg || "数据响应过慢,请稍后再试");
          }
        })
        .catch((err) => {
      
      
          console.log(err);
        });
    },
    // 视频上传 =================
    httpRequestvideo(item) {
      
      
      let chunkSize = 1024 * 1024 * 2; // 每个切片的大小(这里设置为1MB)
      let totalChunks = Math.ceil(item.file.size / chunkSize); // 总切片数
      let currentChunk = 1; // 当前切片索引
      // this.loading = this.$loading({
      
      
      //   lock: true,
      //   text: "视频上传中...",
      //   spinner: "el-icon-loading",
      //   background: "rgba(0, 0, 0, 0.2)",
      // });
      const uploadNextChunk = () => {
      
      
        const formData = new FormData();
        formData.append("name", item.file.name);
        formData.append("data", item.file);
        formData.append("total", totalChunks);
        formData.append("index", currentChunk);
        formData.append("filename", item.file.name);
        axios
          .post(uploadvideo + "?act=upload", formData, {
      
      
            headers: {
      
       "Content-Type": "multipart/form-data" },
            onUploadProgress: (progressEvent) => {
      
      
              this.progress = Math.round(
                (currentChunk * 100 +
                  progressEvent.loaded / progressEvent.total) /
                  totalChunks
              );
            },
          })
          .then((res) => {
      
      
            currentChunk++;
            if (currentChunk < totalChunks + 1) {
      
      
              uploadNextChunk();
            } else {
      
      
              this.progress = 0; // 上传完成后重置进度
              let date = {
      
      
                name: item.file.name,
                data: item.file,
                total: totalChunks,
                index: currentChunk,
                filename: item.file.name,
              };
              this.uploadok(date);
            }
          })
          .catch((error) => {
      
      
            console.error("Error uploading file:", error);
          });
      };
      uploadNextChunk();
    },
    uploadok(val) {
      
      
      let quill = this.$refs.quillEditor.quill;
      let formdata = new FormData();
      formdata.append("name", val.name);
      formdata.append("data", val.data);
      formdata.append("total", val.total);
      formdata.append("index", val.index);
      formdata.append("filename", val.filename);
      // let length = quill.getSelection().index;
      // quill.insertEmbed(length, "video", val.data);
      axios
        .post(uploadvideo + "?act=join", formdata, {
      
      
          headers: {
      
       "Content-Type": "multipart/form-data" },
        })
        .then((res) => {
      
      
          if (res.data.status == 1) {
      
      
            // this.loading.close();
            this.$message.success(res.msg || "完成视频上传");
            let length = quill.getSelection().index;
            // 插入图片  服务器返回的图片地址
            quill.insertEmbed(length, "video", res.data.path);
            // 调整光标到最后
            quill.setSelection(length + 1);
          } else {
      
      
            this.$message.warning(res.msg || "数据响应过慢,请稍后再试");
          }
        })
        .catch((error) => {
      
      
          console.error("Error uploading file:", error);
        });
    },
  },
};
</script>
<style scoped lang='less'>
p {
      
      
  margin: 0;
  padding: 0;
}
.box {
      
      
  width: 90%;
  margin: 0 auto;
  padding: 10px 0;
  .box_count {
      
      
    display: flex;
    // justify-content: center;
    .box_l {
      
      
      width: 50%;
    }
    .box_r {
      
      
      width: 320px;
      height: 580px;
      margin: 10px 10px 10px 100px;
      background-size: 100% 100%;
      // box-shadow: 0 0 10px 0 #ccc;
      border-radius: 25px;
      position: relative;
      .box_r_informationurl {
      
      
        width: 100%;
        margin-top: 60px;
        display: flex;
        padding: 30px;
        .box_r_inImg {
      
      
          width: 50px;
          height: 50px;
          margin-left: 10px;
        }
        .box_r_inlanguage {
      
      
          flex: 1;
          margin: 0 10px;
          background: #ffffff;
          padding: 10px;
          border-radius: 5px;
          .box_r_inlanguageimg {
      
      
            width: 50px;
            height: 50px;
          }
        }
      }
    }
  }
  .buttom {
      
      
    display: flex;
    justify-content: center;
    margin: 10px 0;
  }
}
/deep/ .ql-container {
      
      
  height: 600px;
}
/deep/ .avatar {
      
      
  border-radius: 0;
}
</style>

import video from “@/assets/js/quillVideo.js”;

import { Quill } from "vue-quill-editor";
// 源码中是import直接倒入,这里要用Quill.import引入
const BlockEmbed = Quill.import("blots/block/embed");
const Link = Quill.import("formats/link");

const ATTRIBUTES = ["height", "width"];

class Video extends BlockEmbed {
  static create(value) {
    const node = super.create(value);
    // 添加video标签所需的属性
    node.setAttribute("controls", "controls");
    node.setAttribute("type", "video/mp4");
    node.setAttribute("src", this.sanitize(value));
    return node;
  }

  static formats(domNode) {
    return ATTRIBUTES.reduce((formats, attribute) => {
      if (domNode.hasAttribute(attribute)) {
        formats[attribute] = domNode.getAttribute(attribute);
      }
      return formats;
    }, {});
  }

  static sanitize(url) {
    return Link.sanitize(url);
  }

  static value(domNode) {
    return domNode.getAttribute("src");
  }

  format(name, value) {
    if (ATTRIBUTES.indexOf(name) > -1) {
      if (value) {
        this.domNode.setAttribute(name, value);
      } else {
        this.domNode.removeAttribute(name);
      }
    } else {
      super.format(name, value);
    }
  }

  html() {
    const { video } = this.value();
    return `<a href="${video}">${video}</a>`;
  }
}
Video.blotName = "video"; // 这里不用改,楼主不用iframe,直接替换掉原来,如果需要也可以保留原来的,这里用个新的blot
Video.className = "ql-video";
Video.tagName = "video"; // 用video标签替换iframe

export default Video;


Rendering:
Insert image description here

Guess you like

Origin blog.csdn.net/weixin_44694682/article/details/130864382