«

vue中wangEditor5编辑器如何使用

时间:2024-7-17 16:57     作者:韩俊     分类: Javascript


这篇文章主要介绍了vue中wangEditor5编辑器如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue中wangEditor5编辑器如何使用文章都会有所收获,下面我们一起来看看吧。

    一、wangEditor5是什么

    wangEditor是一款富文本编译器插件,其他的我就不再过多赘述,因为官网上有一大截对于这个编译器的介绍,但我摸索使用的这两天里给我的最直观的感受就是,它是由中国开发者开发,所有的文档都是中文的,这一点上对我这个菜鸡来说非常友好,不用再去逐字逐句翻译,然后去读那些蹩脚的机翻中文。而且功能很丰富,能够满足很多需求,wangEditor5提供很多版本的代码,vue2,vue3,react都支持。

    二、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" ></el-input>
          </el-form-item>
          <el-form-item label="分类">
            <el-select
              v-model="appDetail.appClassificationID"
              
              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="请选择所属组织"
              
            >
              <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
              
              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 >
                  建议上传 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"
              
            ></el-input>
          </el-form-item>
          <el-form-item prop="introduction" label="应用详情">
            <div >
            <Toolbar
                
                :editor="editor"
                :defaultConfig="toolbarConfig"
                :mode="mode"
                class="barStyle"
            />
            <Editor
                
                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>

    标签: javascript vue

    热门推荐