element-ui 图片上传 及 quillEditor富文本(图片视频上传)

js 复制代码
<template>
  <div class="card" style="overflow: hidden; padding-bottom: 10px">
    <div style="padding: 20px 20px 0 20px">
      <span class="title_top"><span class="top_icon"></span>基本信息</span>
      <el-divider></el-divider>
    </div>
    <div class="box">
      <div>
        <div class="box_count">
          <div class="box_l">
            <FlexLinePI :title="'图文标题'" :dropshow="true">
              <el-input v-model.trim="ruleForm.title" maxlength="20" show-word-limit type="text" placeholder="请输入标题"
                v-clear-emoij @input="e => (ruleForm.title = formateText(e))"></el-input>
            </FlexLinePI>
            <FlexLinePI :title="'图文摘要'" :dropshow="true"><el-input style="white-space: pre-wrap;word-break: break-all;"
                v-model.trim="ruleForm.note" type="textarea" maxlength="150" show-word-limit rows="5"
                placeholder="请输入正文详情" @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" :on-progress="onprogress">
                <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" style="">
                  ( 建议上传图片尺寸100x100px,大小不超过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" accept=".jpg,.png,jpeg"
                    :http-request="httpRequest">
                  </el-upload>
                  <AliUpload class="rich-uploader3" :url="videoURL" @deleteVideo="deleteVideo"
                    @beforeupload="beforeupload" @uploadSuccess="uploadVideoSuccess" />
                  <!-- 
                  <el-upload class="rich-uploader3" name="pic" action="" :show-file-list="false" accept="video/*"
                    :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: 14px; width: 100%" class="ellipsis">
                  {{ ruleForm.title != "" ? ruleForm.title : "请输入标题" }}
                </p>
                <div style="display: flex; margin-top: 5px; height: 80px">
                  <div style="
                      flex: 1;
                      -webkit-line-clamp: 6;
                      font-size: 12px;
                      color: #999999;
                    " 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>

    <!-- 图片视频上传中 -->
    <el-dialog title="" :visible.sync="dialogVisiblevideo" width="160px" :close-on-click-modal="false"
      :close-on-press-escape="false" :show-close="false">
      <span slot="title"></span>
      <div class="loadding_image" style="">
        <i class="el-icon-loading" style="font-size: 50px;"></i>
        <div>{{ uptext }}</div>
        <div v-if="videosize != ''">此视频有 {{ videosize }} MB大小,请稍等...</div>
      </div>
    </el-dialog>
  </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";
import AliUpload from './component/aliUpload.vue'
Quill.register(video);
export default {
  name: "AddImageText",
  components: {
    quillEditor,
    AliUpload
    // , 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: {
          clipboard: {
            // 粘贴版,处理粘贴时候带图片
            matchers: [[Node.ELEMENT_NODE, this.handleCustomMatcher]],
          },
          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: "",

      // 提交
      loadingbtn: false,
      dialogVisiblevideo: false,
      uptext: "",
      videosize: ""
    };
  },
  mounted() {
    this.updateId = this.$route.query.id
    if (this.updateId == undefined) {
      this.ruleForm = {
        id: "",
        title: "",
        note: "",
        cover: "",
        content: ""
      };
    } else {
      // 详情
      this.getdetail(this.updateId);
    }

    // 复制图片到富文本
    // let quill = this.$refs.quillEditor.quill;
    // quill.root.addEventListener(
    //   "paste",
    //   (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;
    //         }
    //         const formdata = new FormData();
    //         formdata.append("file", file, file.name);
    //         formdata.append("type", "1");
    //         // let length = quill.getSelection().index;
    //         quill.insertEmbed(1, "image", 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(1, "image", res.data.data.real_path);
    //               //       // 调整光标到最后
    //               quill.setSelection(1 + 1);
    //             } else {
    //               _this.$message.warning(res.data.msg || "数据响应过慢,请稍后再试");
    //             }
    //           })
    //           .catch(err => {
    //             console.log(err);
    //           });
    //       });
    //     }
    //   },
    //   false
    // );
  },
  computed: {},
  methods: {
    handleCustomMatcher(node, Delta) {
      console.log(Delta,123)
      let ops = []
      Delta.ops.forEach(op => {
        if (op.insert && typeof op.insert === 'string') {// 如果粘贴了图片,这里会是一个对象,所以可以这样处理
          ops.push({
            insert: op.insert,
          })
        } else {
          this.$message({
            message: '不允许粘贴图片,请手动上传',
            type: 'warning'
          })
        }
      })
      Delta.ops = ops
      return Delta
    },

    //
    handleAvatarSuccess(res, file) {
      if (res.status == 1) {
        this.dialogVisiblevideo = false;
        this.ruleForm.cover = res.data.real_path;
      }
    },
    beforeAvatarUpload(file) {
      let types = [
        "image/jpeg",
        "image/jpg",
        "image/png"
      ];
      const isImage = types.includes(file.type);
      const isLtSize = file.size / 1024 / 1024 < 5;
      if (!isImage) {
        this.$message.warning("上传图片只能是 JPG、JPEG、PNG 格式!");
        return false;
      }
      if (!isLtSize) {
        this.$message.warning("上传图片大小不能超过 5MB!");
        return false;
      }
      return true;
    },
    onprogress(event, file, fileList) {
      console.log(event, file, fileList);
      // this.dialogVisiblevideo = true;
    },
    // 详情
    getdetail(id) {
      detailGraphic({
        id: id
      })
        .then(res => {
          if (res.status == 1) {
            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) {
      const _this = this;
      _this.uptext = "图片正在上传中...";
      _this.videosize = "";
      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.data.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 == undefined) {
        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.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.loadingbtn = false;
            this.$router.push({
              name: "ImageText"
            });
          } else {
            this.$message.warning(res.msg || "数据响应过慢,请稍后再试");
          }
        })
        .catch(err => {
          console.log(err);
        });
    },
    // 视频上传 =================
    deleteVideo(val) {
      console.log(val)
    },
    beforeupload(val) {
      this.dialogVisiblevideo = val.isshow;
      this.uptext = "视频上传中...";
      this.videosize = Math.ceil(val.filesize / (1024 * 1024));
    },
    uploadVideoSuccess(val) {
      this.$message.success("上传成功");
      this.dialogVisiblevideo = val.isshow;
      let quill = this.$refs.quillEditor.quill;
      let length = quill.getSelection().index;
      // 插入图片  服务器返回的图片地址
      quill.insertEmbed(length, "video", val.videoURL);
      // 调整光标到最后
      quill.setSelection(length + 1);
    },
    //  ======
    httpRequestvideo(item) {
      let chunkSize = 1024 * 1024 * 2; // 每个切片的大小(这里设置为2MB)
      let totalChunks = Math.ceil(item.file.size / chunkSize); // 总切片数
      let currentChunk = 1; // 当前切片索引
      var start = currentChunk * chunkSize;
      var end = Math.min(totalChunks, start + chunkSize);
      this.dialogVisiblevideo = true;
      this.videosize = Math.ceil(item.file.size / (1024 * 1024));
      this.uptext = "视频上传中...";
      const uploadNextChunk = () => {
        const formData = new FormData();
        formData.append("name", item.file.name);
        formData.append("data", item.file.slice(start, end));
        // 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 => {
            this.dialogVisiblevideo = true;
            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);
      axios
        .post(uploadvideo + "?act=join", formdata, {
          headers: { "Content-Type": "multipart/form-data" }
        })
        .then(res => {
          if (res.data.status == 1) {
            this.dialogVisiblevideo = false;
            this.$message.success(res.msg || "完成视频上传");
            let length = quill.getSelection().index;
            // 插入图片  服务器返回的图片地址
            quill.insertEmbed(length, "video", res.data.path);
            // 调整光标到最后
            quill.setSelection(length + 1);
          } else {
            this.dialogVisiblevideo = false;
            this.$message.warning(res.data.msg || "数据响应过慢,请稍后再试");
          }
        })
        .catch(error => {
          this.dialogVisiblevideo = false;
          console.error("Error uploading file:", error);
        });
    }
  }
};
</script>
<style scoped lang="less">
p {
  margin: 0;
  padding: 0;
}

.box {
  width: 90%;
  // margin: 0 auto;
  margin-left: 35px;

  .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: 36px;
          height: 36px;
          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;
}

/deep/ .el-dialog__header {
  display: none;
}

/deep/ .el-dialog__body {
  padding: 10px 20px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: #ffffff00;
  color: #ccc;
}

.loadding_image {
  margin: 0 auto;
  text-align: center;
  width: 125px;
}
</style>
相关推荐
YBN娜3 分钟前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=4 分钟前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck8 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!29 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。34 分钟前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼40 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k093344 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning1 小时前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人1 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架