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