vuetify学习第7天之v-editor---自定义封装富文本编辑器

8 篇文章 1 订阅
vuetify学习第7天之v-editor---自定义封装富文本编辑器

目录


内容

1、vue-quill-editor

Quill editor component for Vue.

基于 Quill、适用于 Vue 的富文本编辑器,支持服务端渲染和单页应用。

详情请参考github地址:https://github.com/surmon-china/vue-quill-editor#readme

2、自定义封装

  • 优点:vueQuillEditor组件为轻量级富文本编辑器,可全局或者部分导入,实现丰富的图文混排。
  • 缺点
    • 图片上传:图片转化为base64编码,当图片较大时,占用很多空间
    • 视频资源:只显示url地址。

根据以上考虑,我们基于vueQuillEditor封装自定义富文本编辑器,主要变更功能为图片上传,实现图片上传至给定url地址功能,同时添加字符统计功能。

  • Editor.vue源代码2-1:
<template>
  <div class="myEditor">
    <quilleditor v-model="content" ref="myTextEditor" :options="editorOption" @change="onChange">
      <div id="toolbar" slot="toolbar">
        <select class="ql-size">
          <option value="small"></option>
          <!-- Note a missing, thus falsy value, is used to reset to default -->
          <option selected></option>
          <option value="large"></option>
          <option value="huge"></option>
        </select>
        <!-- Add subscript and superscript buttons -->
        <span class="ql-formats">
          <button class="ql-script" value="sub"></button>
        </span>
        <span class="ql-formats">
          <button class="ql-script" value="super"></button>
        </span>
        <span class="ql-formats">
          <button type="button" class="ql-bold"></button>
        </span>
        <span class="ql-formats">
          <button type="button" class="ql-italic"></button>
        </span>
        <span class="ql-formats">
          <button type="button" class="ql-blockquote"></button>
        </span>
        <span class="ql-formats">
          <button type="button" class="ql-list" value="ordered"></button>
        </span>
        <span class="ql-formats">
          <button type="button" class="ql-list" value="bullet"></button>
        </span>
        <span class="ql-formats">
          <button type="button" class="ql-link"></button>
        </span>
        <span class="ql-formats">
          <button type="button" @click="imgClick" style="outline:none">
            <svg viewBox="0 0 18 18">
              <rect class="ql-stroke" height="10" width="12" x="3" y="4" />
              <circle class="ql-fill" cx="6" cy="7" r="1" />
              <polyline class="ql-even ql-fill" points="5 12 5 11 7 9 8 10 11 7 13 9 13 12 5 12" />
            </svg>
          </button>
        </span>
        <span class="ql-formats">
          <button type="button" class="ql-video"></button>
        </span>
      </div>
    </quilleditor>
    <div class="limit">
      当前已输入
      <span>{{currentChars}}</span> 个字符,您还可以输入
      <span>{{remainChars}}</span> 个字符。
    </div>
  </div>
</template>
<script>
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";

import { quillEditor } from "vue-quill-editor";

export default {
  name: "v-editor",
  props: {
    value: {
      type: String
    },
    /*上传图片的地址*/
    uploadUrl: {
      type: String,
      default: "/"
    },
    /*上传图片的file控件name*/
    fileName: {
      type: String,
      default: "file"
    },
    maxUploadSize: {
      type: Number,
      default: 1024 * 1024 * 500
    }
  },
  data() {
    return {
      content: "",
      editorOption: {
        modules: {
          toolbar: "#toolbar"
        }
      }
    };
  },
  methods: {
    onChange() {
      this.$emit("input", this.content);
    },
    /*选择上传图片切换*/
    onFileChange(e) {
      var fileInput = e.target;
      if (fileInput.files.length === 0) {
        return;
      }
      this.editor.focus();
      if (fileInput.files[0].size > this.maxUploadSize) {
        this.$message("图片不能大于500KB,图片尺寸过大");
      }
      var data = new FormData();
      data.append(this.fileName, fileInput.files[0]);
      this.axios.post(this.uploadUrl, data).then(res => {
        if (res.data) {
          this.editor.insertEmbed(
            this.editor.getSelection().index,
            "image",
            res.data
          );
        }
      });
    },
    /*点击上传图片按钮*/
    imgClick() {
      if (!this.uploadUrl) {
        console.log("no editor uploadUrl");
        return;
      }
      /*内存创建input file*/
      var input = document.createElement("input");
      input.type = "file";
      input.name = this.fileName;
      input.accept = "image/jpeg,image/png,image/jpg,image/gif";
      input.onchange = this.onFileChange;
      input.click();
    }
  },
  computed: {
    editor() {
      return this.$refs.myTextEditor.quill;
    },
    // 当前已输入字符数
    currentChars() {
      return this.content.length;
    },
    // 剩余可输入字符数
    remainChars() {
      let num = 10000 - Number(this.content.length);
      return num > 0 ? num : 0;
    }
  },
  components: {
    quilleditor: quillEditor
  },
  mounted() {
    this.content = this.value;
    this.currentChars;
    this.remainChars;
  },
  watch: {
    value(newVal) {
      if (this.editor) {
        if (newVal !== this.content) {
          this.content = newVal;
        }
      }
    }
  }
};
</script>

<style lang='scss' scoped>
.quill-editor {
  height: 400px;
}


.limit {
  height: 30px;
  border: 1px solid #ccc;
  line-height: 30px;
  text-align: right;
  padding: 0;
  margin: 0;
  margin: 10px 0;

  span {
    color: #ee2a7b;
  }
}
</style>

3、常用属性和事件

  • 常用属性详解
名称类型默认值说明
valuestring‘’编辑器双向绑定输入内容
optionsobject{}编辑器工具类配置
uploadUrlstring‘’图片上传地址
fileNamestringfile图片上传后台获取变量名称
  • 常用事件详解
名称参数说明
change$event
blur$event
focus$event
ready{$event, html, text }

4、应用案例

  • 需求:视频学习到了商品添加功能,涉及商品描述,需要图文混排,故选择富文本编辑器组件。
  • 商品表单源代码4-1:
<template>
  <v-stepper v-model="step">
    <v-stepper-header>
      <v-stepper-step :complete="step > 1" step="1">基本信息</v-stepper-step>

      <v-divider></v-divider>

      <v-stepper-step :complete="step > 2" step="2">商品描述</v-stepper-step>
      <v-divider></v-divider>

      <v-stepper-step :complete="step > 3" step="3">规格参数</v-stepper-step>
      <v-divider></v-divider>

      <v-stepper-step step="4">SKU属性</v-stepper-step>
    </v-stepper-header>

    <v-stepper-items>
      <v-stepper-content step="1">
        <v-row align="center">
          <v-col cols="6">
            <v-cascader-single
              v-model="goods.categories"
              label="请选择商品分类"
              url="/item/category/list"
              required
              showAllLevels
            />
          </v-col>
          <v-col cols="5">
            <v-select
              :items="brandList"
              v-model="goods.brandID"
              label="请选择品牌"
              item-value="id"
              item-text="name"
              dense
            >
              <template v-slot:selection="{ item }">
                <v-chip color="primary" close>{{ item.name }}</v-chip>
              </template>
            </v-select>
          </v-col>
        </v-row>
        <v-text-field label="商品标题" v-model="goods.title" required counter="200"></v-text-field>
        <v-text-field label="商品卖点" v-model="goods.subTitle" counter="200"></v-text-field>
        <v-textarea
          label="包装清单"
          v-model="spuDetail.packingList"
          counter="1000"
          :rows="3"
          no-resize
        ></v-textarea>
        <v-textarea label="售后服务" v-model="goods.aterService" counter="1000" :rows="3" no-resize></v-textarea>
      </v-stepper-content>
      <v-stepper-content step="2">
        <v-editor v-model="spuDetail.description" uploadUrl="/upload/image"></v-editor>
      </v-stepper-content>
      <v-stepper-content step="3">规格参数</v-stepper-content>
      <v-stepper-content step="4">SKU属性</v-stepper-content>
    </v-stepper-items>
  </v-stepper>
</template>

<script>
export default {
  name: "goods-form",
  props: {
    oldGoods: {
      type: Object
    },
    isEdit: {
      type: Boolean,
      default: false
    },
    step: {
      type: Number,
      default: 1
    }
  },
  data() {
    return {
      valid: false, // 表单校验结果标记
      goods: {
        categories: [], // 商品分类

        brandID: "", // 商品品牌
        title: "", // 商品标题
        subTitle: "" // 商品卖点
      },
      spuDetail: {
        packingList: "", // 包装清单
        afterService: "", // 售后服务
        descirption: "" // 商品描述
      },
      brandList: [], // 品牌列表
      brandName: "" // 选中品牌名称
    };
  },
  methods: {
    submit() {
      // 表单校验
      if (this.$refs.myGoodsForm.validate()) {
        // 定义一个请求参数对象,通过解构表达式来获取goods中的属性
        const { categories, letter, ...params } = this.goods;
        // 数据库中只要保存分类的id即可,因此我们对categories的值进行处理,只保留id,并转为字符串
        params.cids = categories.map(c => c.id).join(",");
        // 将字母都处理为大写
        params.letter = letter.toUpperCase();
        // 将数据提交到后台
        // this.$http.post('/item/goods', this.$qs.stringify(params))
        this.$http({
          method: this.isEdit ? "put" : "post",
          url: "/item/goods",
          data: this.$qs.stringify(params)
        })
          .then(() => {
            // 关闭窗口
            this.$emit("close");
            this.$message.success("保存成功!");
          })
          .catch(() => {
            this.$message.error("保存失败!");
          });
      }
    },
    clear() {
      // 重置表单
      this.$refs.myGoodsForm.reset();
      // 需要手动清空商品分类
      this.categories = [];
    }
  },
  watch: {
    "goods.categories": {
      // 监控分类的变化
      handler() {
        // 根据分类最后一级id查询品牌数据
        this.axios
          .get("/item/goods/cateBrand/" + this.goods.categories[2].id)
          .then(resp => {
            if (resp.status !== 200) {
              // 报错提示
            }
            this.brandList = resp.data;
          });
      },
      deep: true
    }
  }
};
</script>

<style scoped>
</style>

  • 效果图示4-1:在这里插入图片描述

后记

  整页源代码参考博文’ 原创 仿乐优商城后台管理-前端vue+后端thinkphp5.1+数据库mysql项目开发----前端第三天’

  本项目为参考某马视频thinkphp5.1-乐优商城前后端项目开发,相关视频及配套资料可自行度娘或者联系本人。上面为自己编写的开发文档,持续更新。欢迎交流,本人QQ:806797785

前端项目源代码地址:https://gitee.com/gaogzhen/vue-leyou
后端thinkphp源代码地址:https://gitee.com/gaogzhen/leyou-backend-thinkphp
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Element UI的vue-quill-editor富文本编辑器支持插入图片,但是默认的图片上传功能可能不能满足所有需求,需要进行自定义。 首先,在vue-quill-editor的配置中添加`imageHandler`方法,用于处理图片上传: ```javascript <template> <quill-editor v-model="content" :options="editorOption"></quill-editor> </template> <script> import { quillEditor } from 'vue-quill-editor' export default { components: { quillEditor }, data () { return { content: '', editorOption: { imageHandler: this.imageHandler // 添加imageHandler方法 } } }, methods: { imageHandler () { // 处理图片上传 } } } </script> ``` 然后,可以使用第三方上传组件(如`el-upload`)进行图片上传,上传完成后将图片地址返回给`quill-editor`。可以在`imageHandler`方法中实现该逻辑: ```javascript <template> <div> <el-upload class="upload-demo" :action="uploadUrl" :on-success="handleSuccess" :show-file-list="false" :headers="headers" ref="upload" > <el-button size="small" type="primary">上传图片</el-button> </el-upload> </div> </template> <script> import { quillEditor } from 'vue-quill-editor' import { mapGetters } from 'vuex' export default { components: { quillEditor }, data () { return { content: '', editorOption: { imageHandler: this.imageHandler }, uploadUrl: 'https://www.example.com/upload' // 图片上传地址 } }, computed: { ...mapGetters(['getToken']) // 获取token }, methods: { imageHandler () { const self = this const uploadImg = this.$refs.upload uploadImg.click() uploadImg.$refs.input.onchange = function () { const file = uploadImg.$refs.input.files[0] const formData = new FormData() formData.append('file', file) self.$axios.post(self.uploadUrl, formData, { headers: { 'Authorization': self.getToken // 设置token } }).then(res => { const url = res.data.url // 获取图片地址 const editor = self.$refs.editor.quill // 获取quill对象 const index = (editor.getSelection() || {}).index || editor.getLength() editor.insertEmbed(index, 'image', url) // 插入图片 }).catch(err => { console.log(err) }) } } } } </script> ``` 在这个例子中,使用了`el-upload`组件进行图片上传,上传完成后将图片地址返回给`quill-editor`。在`imageHandler`方法中,通过`this.$refs.editor.quill`获取到了`quill`对象,然后调用`insertEmbed`方法插入图片。 需要注意的是,由于`quill`对象是异步创建的,所以需要在`mounted`生命周期中获取到`quill`对象才能进行图片插入。可以使用`$nextTick`方法来确保获取到了`quill`对象: ```javascript <template> <quill-editor v-model="content" :options="editorOption" ref="editor"></quill-editor> </template> <script> import { quillEditor } from 'vue-quill-editor' export default { components: { quillEditor }, data () { return { content: '', editorOption: { imageHandler: this.imageHandler } } }, mounted () { this.$nextTick(() => { // 获取quill对象 const editor = this.$refs.editor.quill // 在quill对象中添加图片上传功能 editor.getModule('toolbar').addHandler('image', () => { this.$refs.upload.click() }) }) }, methods: { imageHandler () { // 处理图片上传 } } } </script> ``` 在这个例子中,通过`editor.getModule('toolbar').addHandler`方法,在`quill`对象中添加了一个`image`按钮,点击该按钮时触发了上传图片的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值