vue中wangEditor5編輯器的基本使用
一、wangEditor5是什麼
wangEditor是一款富文本編譯器插件,其他的我就不再過多贅述,因為官網上有一大截對於這個編譯器的介紹,但我摸索使用的這兩天裡給我的最直觀的感受就是,它是由中國開發者開發,所有的文檔都是中文的,這一點上對我這個菜雞來說非常友好,不用再去逐字逐句翻譯,然後去讀那些蹩腳的機翻中文。而且功能很豐富,能夠滿足很多需求,wangEditor5提供很多版本的代碼,vue2,vue3,react都支持。
接下來就介紹一下wangEditor5的基本使用,以及博主在使用中遇到的各種問題以及其解決方案。
官方網站:
wangEditor開源 Web 富文本編輯器,開箱即用,配置簡單https://www.wangeditor.com/
二、wangEditor5基本使用
(一)、安裝
yarn add @wangeditor/editor-for-vue # 或者 npm install @wangeditor/editor-for-vue --save
(二)、編譯器引入
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
Editor:引入@wangEditor編譯器
Toolbar:引入菜單欄
(三)、css及變量引入
<style src="@wangeditor/editor/dist/css/style.css" > </style>
這裡需要註意,引入的樣式寫在帶有scoped標簽的style內無效。隻能引入在全局樣式裡,但可能會造成樣式覆蓋,一般會有個清除樣式的文件,會把裡面的樣式覆蓋掉。
三、wangEditor5工具欄配置
工具欄配置有很多選項,這裡以官方為主,我隻做一些常用的配置介紹。
(一)、editor.getAllMenuKeys()
查詢編輯器註冊的所有菜單 key (可能有的不在工具欄上)這裡註意要在
onCreated(editor) { this.editor = Object.seal(editor) },
這個函數中去調用 (這個函數是基本配置之一),不然好像調不出來,當然也有可能是博主太菜。
(二)、toolbarConfig中的excludeKeys
toolbarConfig: { excludeKeys:["uploadVideo","fullScreen","emotion","insertTable"] },
這個是菜單欄配置的一種:排除某項配置 ,這裡填寫的key值就是用上面那個方法,查出來的key值。
四、wangEditor5上傳圖片
首先在data中return以下信息。
editorConfig: { placeholder: '請輸入內容...' , MENU_CONF: { uploadImage: { customUpload: this.uploadImg, }, } },
然後書寫this.uploadImg函數。
uploadImg(file, insertFn){ let imgData = new FormData(); imgData.append('file', file); axios({ url: this.uploadConfig.api, method: 'post', data: imgData, }).then((response) => { insertFn(response.data.FileURL); }); },
註意,這裡因為返回的數據結構與@wangeditor要求的不一致,因此要使用 insertFn 函數 去包裹返回的url地址。
五、wangEditor5的一些問題收集及解決
(一)、引入@wangEditor 編譯報錯 " Module parse failed: Unexpected token (12828:18)You may need an appropriate loader to handle this file type."
解決方法:在 wwebpack.base.conf.js 文件的module>rules>.js 的include下加入
resolve('node_modules/@wangeditor')
就可以瞭。
(二)、@wangeditor有序列表無序列表的樣式消失問題。
大概率是全局樣式清除導致的樣式消失,可以去調試工具裡看一看,樣式覆蓋的問題。
然後在style裡deep一下改變樣式就行瞭。
.editorStyle{ /deep/ .w-e-text-container>.w-e-scroll>div ol li{ list-style: auto ; } /deep/ .w-e-text-container>.w-e-scroll>div ul li{ list-style: disc ; } /deep/ .w-e-text-placeholder{ top:7px; } }
六、完整代碼
<template> <div v-loading="Loading" class="app_detail"> <el-form ref="form" :rules="rules" :model="appDetail" label-width="80px"> <el-form-item prop="name" label="應用名稱"> <el-input v-model="appDetail.name" style="width: 360px"></el-input> </el-form-item> <el-form-item label="分類"> <el-select v-model="appDetail.appClassificationID" style="width: 360px" placeholder="選擇應用分類" > <template v-for="item in classes"> <el-option v-if="item.parentAppClassificationID" :key="item.appClassificationID" :label="item.appClassificationName" :value="item.appClassificationID" ></el-option> </template> </el-select> <div class="inputdesc">為瞭適應前臺展示,應用隻能屬於二級分類</div> </el-form-item> <el-form-item label="所屬組織"> <el-select v-model="appDetail.orgID" placeholder="請選擇所屬組織" style="width: 360px" > <el-option v-for="item in myorgs" :key="item.orgID" :label="item.name" :value="item.orgID" ></el-option> </el-select> </el-form-item> <el-form-item prop="tags" label="標簽"> <el-select v-model="appDetail.tags" multiple filterable style="width: 360px" placeholder="請輸入或選擇應用標簽" > <el-option v-for="item in existTags" :key="item" :label="item" :value="item" ></el-option> </el-select> </el-form-item> <el-row> <el-col :span="8" class="appsFrom"> <el-form-item label="應用Logo" ref="uploadpic" class="el-form-item-cen" prop="logo" > <el-upload class="avatar-uploader" :action="uploadConfig.api" :with-credentials="true" :headers="uploadConfig.headers" :show-file-list="false" :on-success="handleAvatarSuccess" :on-error="handleAvatarError" :before-upload="beforeAvatarUpload" > <img v-if="appDetail.logo" :src="appDetail.logo" class="avatar" /> <i v-else class="el-icon-plus avatar-uploader-icon"></i> <i v-if="appDetail.logo" class="el-icon-delete" @click.stop="() => handleRemove()" ></i> </el-upload> <span style="color: #999999; font-size: 12px"> 建議上傳 100*100 比例的Logo </span> </el-form-item> </el-col> </el-row> <el-form-item prop="desc" label="應用簡介"> <el-input type="textarea" v-model="appDetail.desc" :rows="3" style="width: 360px" ></el-input> </el-form-item> <el-form-item prop="introduction" label="應用詳情"> <div style="border: 1px solid #ccc; "> <Toolbar style="border-bottom: 1px solid #ccc" :editor="editor" :defaultConfig="toolbarConfig" :mode="mode" class="barStyle" /> <Editor style="height: 500px; overflow-y: hidden;" v-model="appDetail.introduction" :defaultConfig="editorConfig" :mode="mode" @onCreated="onCreated" class="editorStyle" /> </div> </el-form-item> </el-form> <el-button class="save_btn" type="primary" @click="onSubmit" :loading="commitLoading" >保存</el-button > </div> </template> <script> import { updateApp } from '@/api/app'; import { getStoreAvailableTags } from '@/api/appStore'; import { getToken } from '@/utils/auth'; import axios from 'axios'; import { errorHandle } from '../../../../utils/error'; import { Editor, Toolbar } from '@wangeditor/editor-for-vue'; import { IToolbarConfig, DomEditor, IEditorConfig } from '@wangeditor/editor' export default { name: 'BasicInfo', components: { Editor, Toolbar }, props: { appDetail: { type: Object }, marketID: { type: String }, Loading: Boolean }, data() { var baseDomain = process.env.BASE_API; if (baseDomain == '/') { baseDomain = window.location.origin; } const isChinese = (temp) => { return /^[\u4e00-\u9fa5]+$/i.test(temp); }; const tagValidate = (rule, value, callback) => { let checked = true; value.map((tag) => { if (tag.length < 2) { callback('每個標簽至少兩個字符'); checked = false; return; } if (isChinese(tag) && tag.length > 5) { callback('中文標簽字數應處於2-5個之間'); checked = false; return; } if (Number(tag) > 0) { callback('標簽不能為純數字組成'); checked = false; return; } }); if (checked) { callback(); } }; return { editor: null, toolbarConfig: { excludeKeys:["uploadVideo","fullScreen","emotion","insertTable"] }, editorConfig: { placeholder: '請輸入內容...' , MENU_CONF: { uploadImage: { customUpload: this.uploadImg, }, } }, mode: 'default', // or 'simple' commitLoading: false, classes: [], existTags: [], appPublishTypes: [ { value: 'public', label: '免費公開' }, { value: 'integral', label: '金額銷售' }, { value: 'private', label: '私有' }, { value: 'show', label: '展覽' } ], uploadConfig: { api: `${baseDomain}/app-server/uploads/picture`, headers: { Authorization: getToken() }, }, editorOption: {}, rules: { name: [ { required: true, message: '應用名稱不能為空', trigger: 'blur' }, { min: 2, message: '至少兩個字符', trigger: 'blur' }, { max: 24, message: '應用名稱建議不超過24個字符', trigger: 'blur' } ], desc: [ { required: true, message: '應用簡介不能為空', trigger: 'blur' }, { min: 10, message: '至少10個字符', trigger: 'blur' }, { max: 82, message: '描述最多82個字符', trigger: 'blur' } ], introduction: [ { max: 10140, message: '描述最多10240個字符', trigger: 'blur' } ], tags: [{ validator: tagValidate, trigger: 'change' }] } }; }, created() { this.fetchStoreAppClassList(); this.fetchStoreAppTags(); }, computed: { myorgs() { return this.$store.state.user.userOrgs; } }, methods: { uploadImg(file, insertFn){ let imgData = new FormData(); imgData.append('file', file); axios({ url: this.uploadConfig.api, method: 'post', data: imgData, }).then((response) => { insertFn(response.data.FileURL); }); }, onCreated(editor) { this.editor = Object.seal(editor) }, fetchStoreAppTags() { getStoreAvailableTags({ marketID: this.marketID, size: -1 }) .then((res) => { if (res && res.tags) { const tags = []; res.tags.map((item) => { tags.push(item.name); }); this.existTags = tags; } }) .catch((err) => { this.Loading = false; }); }, fetchStoreAppClassList() { this.$store .dispatch('GetStoreAppClassificationList', { marketID: this.marketID, disableTree: true }) .then((res) => { if (res) { this.classes = res; } }) .catch(() => {}); }, fetchUserOrgs() { this.$store .dispatch('GetUserOrgList') .then((res) => { if (res) { this.myorgs = res; } }) .catch(() => {}); }, markdownContentUpdate(md, render) { this.appData.introduction_html = render; }, markdownImgAdd(pos, $file) { // 第一步.將圖片上傳到服務器. var formdata = new FormData(); formdata.append('file', $file); axios({ url: this.api, method: 'post', data: formdata, headers: this.Token }).then((re) => { if (re && re.data && re.data.data) { this.$refs.md.$img2Url(pos, re.data.data); } }); }, handleAvatarSuccess(res, file) { this.appDetail.logo = res.FileURL; }, handleAvatarError(re) { if (re.code == 10024) { this.$message.warning( '上傳圖片類型不支持,請上傳以.png .jpg .jpeg 結尾的圖片' ); return; } this.$message.warning('上傳失敗!'); }, beforeAvatarUpload(file) { const isJPG = file.type === 'image/jpeg'; const isPng = file.type === 'image/png'; const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG && !isPng) { this.$message.warning('上傳Logo圖片隻能是JPG、PNG格式!'); } if (!isLt2M) { this.$message.warning('上傳頭像圖片大小不能超過 2MB!'); } return (isJPG || isPng) && isLt2M; }, handleRemove() { this.$confirm('是否刪除logo', '提示', { confirmButtonText: '確定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.appDetail.logo = ''; }); }, handlePictureCardPreview(file) { this.dialogImageUrl = file.url; this.dialogVisible = true; }, changeSelectApp_type_id(value) { this.appData.app_type_id = value; this.$forceUpdate(); }, changeSelectPublish_type(value) { this.appData.publish_type = value; this.$forceUpdate(); }, onSubmit() { this.$refs.form.validate((valid) => { if (valid) { this.commitLoading = true; this.$confirm('是否提交數據', '提示', { confirmButtonText: '確定', cancelButtonText: '取消', type: 'warning' }) .then(() => { updateApp(this.appDetail) .then((res) => { this.$message.success('應用信息更新成功'); this.commitLoading = false; }) .catch((err) => { errorHandle(err); this.commitLoading = false; }); }) .catch(() => { this.commitLoading = false; }); } else { return false; } }); } } }; </script> <style lang="scss" scoped > .app_detail { position: relative; padding-bottom: 20px; .save_btn { margin-left: 80px; } .el-select { width: 100%; } } .editorStyle{ /deep/ .w-e-text-container>.w-e-scroll>div ol li{ list-style: auto ; } /deep/ .w-e-text-container>.w-e-scroll>div ul li{ list-style: disc ; } /deep/ .w-e-text-placeholder{ top:7px; } } .barStyle{ /deep/ .w-e-bar-item{ padding:2.5px } /deep/ .w-e-bar-item > button >.title{ border-left:0 !important; } } </style> <style src="@wangeditor/editor/dist/css/style.css" > .inputdesc { font-size: 12px; color: rgba(0, 0, 0, 0.45); transition: color 0.3s cubic-bezier(0.215, 0.61, 0.355, 1); } .app_detail img { width: auto; } .app_detail .ql-formats { line-height: 22px; } </style>
總結
到此這篇關於vue中wangEditor5編輯器的基本使用的文章就介紹到這瞭,更多相關vue wangEditor5的使用內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!