vuetify學習第7天之v-editor---自定義封裝富文本編輯器

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、常用屬性和事件

  • 常用屬性詳解
名稱 類型 默認值 說明
value string ‘’ 編輯器雙向綁定輸入內容
options object {} 編輯器工具類配置
uploadUrl string ‘’ 圖片上傳地址
fileName string file 圖片上傳後臺獲取變量名稱
  • 常用事件詳解
名稱 參數 說明
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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章