富文本编辑器 vue-quill

安装

pnpm install @vueup/vue-quill@beta --save

components 文件夹下新建 quillEditor.vue文件

element-plus  el-upload版

<template>
  <div style="margin-bottom: 5px; border-radius: 10px">
    <el-upload class="editor-img-uploader" :action="upLoadUrl" :show-file-list="false" :headers="headers"
      :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
      <i class="el-icon-plus editor-img-uploader"></i>
    </el-upload>
    <QuillEditor id="editorId" ref="myQuillEditor" v-model:content="content" theme="snow" @update:content="onEditorUpdate($event)"
      contentType="html" :options="options" />
  </div>
</template>

<script setup>
import { getCode as uploadImage } from '@/api/user'
import { QuillEditor, Quill } from "@vueup/vue-quill";
import { reactive, ref, toRaw, defineEmits, defineProps } from "vue";
import { ElMessage } from "element-plus";
import "@vueup/vue-quill/dist/vue-quill.snow.css";

const props = defineProps({
  value: { type: String, default: "" },
  size: { type: Number, default: 5}
})
let content = ref("");
let size = ref("")
content.value = props.value;
size.value = props.size
watch(props.value, (newVal, oldVal) => {
  content.value = props.value
}, {
  deep: true,	// 深度监听 
  immediate: true,	// 立即执行
})

let upLoadUrl = ref(uploadImage);
let headers = reactive({
  token: sessionStorage.getItem("token"),
});
const myQuillEditor = ref(null)


// 工具栏配置
const toolbarOptions = [
  ['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
  ["blockquote", "code-block"], // 引用
  [{ 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'], // 清除文本格式
  ['link', 'image', 'video'] // 链接、图片、视频
]

const options = reactive({
  modules: {
    toolbar: {
      container: toolbarOptions,
      handlers: {
        image: function (value) {
          if (value) {
            // 调用element图片上传
            document
              .querySelector(".editor-img-uploader>.el-upload")
              .click();
          } else {
            Quill.format("image", true);
          }
        },
        link: function (value) {
          if (value) {
            var href = prompt('请输入链接地址:')
            this.quill.format('link', href)
          } else {
            this.quill.format('link', false)
          }
        },
        video: function (value) {
          if (value) {
            var href = prompt('请输入视频链接:')
            this.quill.format('video', href)
          } else {
            this.quill.format('video', false)
          }
        }
      },
    },
    history: {
      delay: 1000,
      maxStack: 50,
      userOnly: false
    },
  },
});
// 图片上传成功返回图片地址
function handleAvatarSuccess(res, file) {
  // 如果上传成功
  if (res) {
    // 获取富文本实例
    let quill = toRaw(myQuillEditor.value).getQuill();
    // 获取光标位置
    let length = quill.selection.savedRange.index;
    // 插入图片,res为服务器返回的图片链接地址
    quill.insertEmbed(length, "image", res);
    // 调整光标到最后
    quill.setSelection(length + 1);
  } else {
    ElMessage({
      message: "提交失败!",
      type: "error",
    });
  }
}
// 图片上传前拦截
function beforeAvatarUpload(file) {
  const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
  const isJPG = type.includes(file.type);
  const isLt2M = file.size / 1024 / 1024 < 2;
  if (!isJPG) {
    ElMessage({
      message: "图片格式错误",
      type: "success",
    });
  }
  if (!isLt2M) {
    ElMessage({
      message: "上传图片不能超过" + size.value + "M",
      type: "success",
    });
  }
  return isJPG && isLt2M;
}
const emit = defineEmits(['change'])
const onEditorUpdate = () => emit('change', this.content)
</script>
<style scoped lang='scss'>
.editor-img-uploader {
  display: none;
}

.ql-editor {
  min-height: 300px;
}
</style>

input版  -- 推荐

<template>
  <div style="margin-bottom: 5px; border-radius: 10px">
    <QuillEditor id="editorId" ref="myQuillEditor" v-model:content="content" theme="snow" @update:content="onEditorUpdate($event)"
      contentType="html" :options="options" />
    <input id="upload" type="file" style="display: none" accept="image/*" @change="handleInputChange">
  </div>
</template>

<script setup>
import { getCode as uploadImage } from '@/api/user'
import { QuillEditor, Quill } from "@vueup/vue-quill";
import { reactive, ref, toRaw, defineEmits, defineProps } from "vue";
import { ElMessage } from "element-plus";
import "@vueup/vue-quill/dist/vue-quill.snow.css";

const props = defineProps({
  value: { type: String, default: "" },
  size: { type: Number, default: 5 }
})
let content = ref("");
let size = ref("");
content.value = props.value;
size.value = props.size;

watch(props.value, (newVal, oldVal) => {
  content.value = props.value
}, {
  deep: true,	// 深度监听 
  immediate: true,	// 立即执行
})

const myQuillEditor = ref(null)

// 工具栏配置
const toolbarOptions = [
  ['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
  ["blockquote", "code-block"], // 引用
  [{ 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'], // 清除文本格式
  ['link', 'image', 'video'] // 链接、图片、视频
]

const options = reactive({
  modules: {
    toolbar: {
      container: toolbarOptions,
      handlers: {
        image: function (value) {
          if (value) {
            // 调用element图片上传
            document
              .querySelector("#upload")
              .click();
          } else {
            Quill.format("image", true);
          }
        },
        link: function (value) {
          if (value) {
            var href = prompt('请输入链接地址:')
            Quill.format('link', href)
          } else {
            Quill.format('link', false)
          }
        },
        video: function (value) {
          if (value) {
            var href = prompt('请输入视频链接:')
            Quill.format('video', href)
          } else {
            Quill.format('video', false)
          }
        }
      },
    },
    history: {
      delay: 1000,
      maxStack: 50,
      userOnly: false
    },
  },
});
const dataURItoBlob = (dataURI, type) => {
  // atob用于解码使用 base-64 编码的字符串
  var binary = atob(dataURI.split(',')[1])
  var array = []
  for (var i = 0; i < binary.length; i++) {
    array.push(binary.charCodeAt(i))
  }
  return new Blob([new Uint8Array(array)], { type: type })
}

const handleInputChange = (e) => {
  const file = e.target.files[0]
  if (!/image\/\w+/.test(file.type)) {
    ElMessage.error('图片格式不正确')
    return
  }
  const isLt5M = file.size / 1024 / 1024 < size
  if (!isLt5M) {
    ElMessage.error(`图片大小不能超过 ${size.value}MB!`)
    return
  }
  const reader = new FileReader()
  const image = new Image()
  image.onload = () => {
    const canvas = document.createElement('canvas')
    const context = canvas.getContext('2d')
    // 根据原图片大小和图片压缩配置确定压缩后图片的宽和高
    const width = image.width * 0.5
    const height = image.height * 0.5
    canvas.width = width
    canvas.height = height
    // 清空canvas画布上填充的矩形
    context.clearRect(0, 0, width, height)
    // 将原图片按压缩后的尺寸大小绘制在canvas画布中
    context.drawImage(image, 0, 0, width, height)
    const dataUrl = canvas.toDataURL(file.type)
    // 将base64格式的图片转换为Blob对象
    const blobData = dataURItoBlob(dataUrl, file.type)
    // 将文件流转换为formData对象
    const formData = new FormData()
    formData.append('file', blobData)
    uploadImage(formData)
      .then((res) => {
        if (res.data.code === 200) {
          // 获取富文本实例
          let quill = toRaw(myQuillEditor.value).getQuill();
          // 获取光标位置
          let length = quill.selection.savedRange.index;
          // 插入图片,res为服务器返回的图片链接地址
          quill.insertEmbed(length, "image", res);
          // 调整光标到最后
          quill.setSelection(length + 1);
        }
      })
  }
  reader.onload = (e) => {
    image.src = e.target.result
  }
  reader.readAsDataURL(file)
}
/*
*注册emit事件
*/
const emit = defineEmits(['change'])
/*
* 当内容变动时,把内容发给富文本调用者,触发change事件
*/
const onEditorUpdate = () => emit('change', content)


</script>
<style scoped lang='scss'>
.editor-img-uploader {
  display: none;
}

.ql-editor {
  min-height: 300px;
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值