wangeditor富文本编辑器使用(详细)

1.下载安装

yarn add wangeditor -S
npm i wangeditor

2.导入

import E from ‘wangeditor’;

3. 具体使用

3.1 页面使用

<editor  ref="editor"
         v-model="submitData.content"
         :img-sep="editorImgSep"
         :req-file-name="reqFileName"
         :upload-params="editorUploadParams"
         :custom-upload-img="customUploadImg"
         v-bind="ruleConfig.content.fieldProps" />
 import Editor from '@bus_comp/business/editor/index.vue';

3.2 富文本编辑器相关文件

src/components/business/editor/

3.2.1 custom_btns/full_screen.ts

/**
 * @description: 全屏按钮
 */

import E from 'wangeditor';
let { BtnMenu } = E;
export class FullScreen extends BtnMenu {
    constructor (editor) {
        let $elem = E.$(
            `<div class="w-e-menu">
                 <a class="_editor_btn_fullscreen" data-fullsreen-status="false">
                     <i class="w-e-icon-fullscreen"></i>
                 </a>
             </div>`
        );
        super($elem, editor);
    }
 
    clickHandler () {
        let sirEditor = document.querySelector('#SirEditor');
        let classes = sirEditor?.className;
        if (!classes) {
             sirEditor?.className = 'fullscreen-editor';
        } else {
            let classArr = classes.split(' ');
            if (!classArr.includes('fullscreen-editor')) {
                classArr.push('fullscreen-editor');
            } else {
                let findIndex = classArr.findIndex(item => item === 'fullscreen-editor');
                classArr.splice(findIndex, 1);
            }
             sirEditor?.className = classArr.join(' ');
        }
        let iel = document.querySelector('#SirEditor' + ' ._editor_btn_fullscreen i');
        let ielClasses = iel?.className;
        let ielClassesArr = ielClasses?.split(' ');
        if (ielClassesArr.includes('w-e-icon-fullscreen')) {
             iel?.classList.remove('w-e-icon-fullscreen');
             iel?.classList.add('w-e-icon-fullscreen_exit');
        } else {
             iel?.classList.remove('w-e-icon-fullscreen_exit');
             iel?.classList.add('w-e-icon-fullscreen');
        }
    }
 
    tryChangeActive () {
        this.active();
    }
}

3.2.2 2. const.ts

/**
 * @description: 富文本编辑器配置
 */


type MENU_TYPE =
    | 'head'
    | 'bold'
    | 'fontSize'
    | 'fontName'
    | 'italic'
    | 'underline'
    | 'strikeThrough'
    | 'foreColor'
    | 'backColor'
    | 'link'
    | 'list'
    | 'justify'
    | 'quote'
    | 'emoticon'
    | 'image'
    | 'table'
    | 'video'
    | 'code'
    | 'undo'
    | 'fullscreen'
    | 'lineHeight'
    | 'indent'
    | 'splitLine'
    | 'todo'
    | 'redo';
interface MenuTipItem {
    menuItem: MENU_TYPE; // 菜单的menu
    class?: string; // 实际对应的className 样式命名不一样按照menuItem...
    tip: string; // title悬浮提示
}

// 这里只为那些悬浮上去没有选择菜单的按钮加提示
export const MENU_TIPS: MenuTipItem[] = [
    {
        menuItem: 'bold',
        tip: _('粗体')
    },
    {
        menuItem: 'italic',
        tip: _('斜体')
    },
    {
        menuItem: 'underline',
        tip: _('下划线')
    },
    {
        menuItem: 'strikeThrough',
        tip: _('删除线')
    },
    {
        menuItem: 'link',
        tip: _('插入链接')
    },
    {
        menuItem: 'justify',
        tip: _('对齐方式')
    },
    {
        menuItem: 'quote',
        class: 'quotes-left',
        tip: _('引用')
    },
    {
        menuItem: 'code',
        tip: _('插入代码')
    },
    {
        menuItem: 'image',
        tip: _('插入图片')
    },
    {
        menuItem: 'table',
        class: 'table2',
        tip: _('插入表格')
    },
    {
        menuItem: 'undo',
        tip: _('撤销')
    },
    {
        menuItem: 'redo',
        tip: _('重复')
    },
    {
        menuItem: 'fullscreen',
        tip: _('全屏')
    },
    {
        menuItem: 'indent',
        tip: _('缩进')
    },
    {
        menuItem: 'splitLine',
        tip: _('分割线')
    },
    {
        menuItem: 'todo',
        tip: _('待办事项')
    },
    {
        menuItem: 'lineHeight',
        tip: _('行高')
    }
];

const FULLSCREEN_TIP = {
    isFullscreen: _('退出全屏'),
    notFullscreen: _('全屏')
};
export const VIEWER_TIP = {
    FULLSCREEN_TIP
};

/**
 * 文件大小单位
 */
const BYTE = 1024;
export const CN = {
    trillion: BYTE * BYTE // 1M
};

export const DEFAULT_FILE_SIZE = 200;


3.2.3 editor.vue

<template>
    <div id="SirEditor"
         class="editor-box">
        <div :id="editorBar"
             ref="toolbar"
             class="toolbar"></div>
        <div ref="textArea"
             class="text-area"
             @mousewheel="_onDomMouseWheel">
            <div :id="editorElem"
                 class="editor-content"></div>
            <!--用来模拟placeholder-->
            <textarea
                v-show="showTip"
                ref="input"
                v-model.trim="tip"
                class="placeholder-tip form-control"
                :placeholder="placeholder"
                @blur="_onDomBlur"
                @focus="_onDomFocus"></textarea>
        </div>
    </div>
</template>

<script>

/**
 * @description: 富文本编辑器-组件
 */

import { removeErrTip, showErrTip } from '../utils/index';
import E from 'wangeditor';
import { uuid } from '../utils/uuid';
import lodashIsFunction from 'lodash/isFunction';
import { MENU_TIPS, CN } from './const';
import { FullScreen } from './custom_btns/full_screen';

// import xss from 'xss';
/* eslint-disable */
export default {
    props: {
        richContent: {
            // 文本内容
            type: String,
            default: ''
        },
        placeholder: {
            type: String,
            default: _('请输入正文')
        },
        menus: {
            // 菜单配置,默认全部显示
            type: Array,
            default() {
                return [];
            }
        },
        isBase64: {
            // 是否以Base64形式存储
            type: Boolean,
            default: false
        },
        customUploadImg: {
            // 是否完全自定义上传
            type: Function,
            default: null
        },
        callback: Function, // 自定义上传回调

        serverUrl: {
            // 上传情况的上传地址
            type: String,
            default: ''
        },
        beforeUpload: Function, // 插件方式上传图片之前的校验

        reqFileName: {
            // 上传图片请求图片名
            type: String,
            defaule: 'file'
        },
        acceptImgs: {
            // 富文本支持上传图片格式
            type: Array,
            default: () => ['png', 'jpg', 'jpeg', 'bmp']
        },
        uploadImgParams: {
            // 配置上传参数,默认会被添加到formdata中。
            type: Object,
            default() {
                return {};
            }
        },
        imgSep: {
            type: String,
            default: 'file:'
        },
        uploadHeaders: {
            type: Object,
            default() {
                return {};
            }
        },
        menuTooltipPosition: {
          type: String,
          default: 'down'
        },
        paramsWithUrl: {
            // 将参数拼接到 url 中
            type: Boolean,
            default: false
        },
        maxSize: {
            // 不传就是不限制大小
            type: Number,
            default: 0
        },
        timeout: {
            // 上传超时时间
            type: Number,
            default: 5000
        },
        maxNum: {
            // 最多上传几张
            type: Number,
            default: 0
        },
        showLinkImg: {
            // 是否显示“网络图片”tab,默认显示
            type: Boolean,
            default: false
        },
        blankText: {
            type: String,
            default: _('该输入项不允许为空')
        },
        clickFocus: {
            // 点击立即聚焦富文本,粘贴图片会需要
            type: Boolean,
            default: false
        },
        checkBase64: Function,
        showMenusTip: {
            type: Boolean,
            default: false
        },
        menusTips: {
            // 给工具栏的按钮增加title悬浮提示
            type: Array,
            default() {
                return MENU_TIPS;
            }
        },
        preventScroll: {
            type: Boolean,
            default: false
        }
    },

    data() {
        return {
            editorBar: uuid(),
            editorElem: uuid(),
            tipTime: 2000, // 提示显示的时间
            editorContent: '', /

3.2.4 index.vue

<template>
    <div class="sir-editor-wrapper"
         :class="prefixCls">
        <editor
            ref="sirEditor"
            class="sir-editor"
            :rich-content.sync="editorContent"
            :show-link-img="showLinkImg"
            :server-url="uploadUrl"
            :before-upload="beforeUpload"
            :upload-img-params="uploadParams"
            :custom-upload-img="customUploadImg"
            :req-file-name="reqFileName"
            show-menus-tip
            :menus="menus"
            :max-num="fileNumber"
            :img-sep="imgSep"
            :click-focus="true"
            :placeholder="placeholder"
            @update:richContent="handleChange" />
    </div>
</template>

<script lang="ts">

/**
 * @description: 富文本编辑器
 */

import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { API_PREFIX } from '@common/const/const';
import { DEFAULT_FILE_SIZE, CN } from './const';

import Editor from './editor.vue';

@Component({
    name: 'SirEditor',
    components: {
        Editor
    },
    model: {
        prop: 'value',
        event: 'change'
    }
})
export default class SirEditor extends Vue {
    @Prop(String) private readonly value?: string;

    @Prop(String) private readonly placeholder?: string;

    @Prop(String) private readonly imgSep?: string;

    @Prop({ default: false }) private readonly showLinkImg?: boolean;

    @Prop({ default: () => ({}) }) private readonly uploadParams?: object;

    @Prop({ default: '' }) private readonly prefixCls?: string;

    @Prop(Number) private readonly fileSize?: number;

    @Prop(Number) private readonly fileNumber?: number;

    @Prop({
        type: String,
        default: 'file'
    }) 
    private readonly reqFileName?: number;

    @Prop({
        type: Function,
        default: null
    }) 
    private readonly customUploadImg?;

    /** 内容 */
    private editorContent?: string | null = null;

    /** 文件上传路径 */
    private uploadUrl?: string = API_PREFIX + '/attachments';
    private menus = [
        'head', // 标题
        'bold', // 粗体
        'fontSize', // 字号
        'fontName', // 字体
        'italic', // 斜体
        'underline', // 下划线
        'strikeThrough', // 删除线
        'foreColor', // 文字颜色
        'backColor', // 背景颜色
        'link', // 插入链接
        'list', // 列表
        'justify', // 对齐方式
        'image', // 插入图片
        'quote', // 引用
        // 'emoticon', // 表情
        'table', // 表格
        // 'video', // 插入视频
        // 'code', // 插入代码
        'undo', // 撤销
        'redo', // 重复
        'lineHeight', // 行高
        'indent', // 增加缩进/减少缩进
        'fullscreen', // 全屏
        'splitLine', // 分割线
        'todo' // 待办事项
    ]; // 富文本toolbar配置
    @Watch('value', { immediate: true })
    private handleValueChange (value: string) {

        // 防止在编辑时,触发watch
        if (value !== this.editorContent) {
            let sirEditor: any = this.$refs.sirEditor;
            if (sirEditor) {
                sirEditor.setJsonValue(value);
            }
            this.editorContent = value || null;
        }
    }

    beforeUpload (xhr, editor, files) {
        const FILE_SIZE = this.fileSize || DEFAULT_FILE_SIZE;
        const IMG_MAX_SIZE = FILE_SIZE * CN.trillion; // 单张不超过5M
        let vm = this as any;
        let currentImgs = vm.getContentImgs();
        let curImgLength = currentImgs.length;
        if (this.fileNumber) {
            let canUploadCount = this.fileNumber - curImgLength;

            if (canUploadCount <= 0) {
                
                // 为0 不能再上传了
                vm.$warn(_(`最多上传{0}个文件`, this.fileNumber), { autoHide: true });
                return true;
            }

            // 截取可上传的总数
            if (files.length > canUploadCount) {
                files = files.slice(0, canUploadCount);
                vm.$warn(_(`最多上传{0}个文件`, this.fileNumber), { autoHide: true });
            }
        }

        if (files.some(fi

3.2.5 viewer.vue

<!-- eslint-disable vue/no-v-html -->
<template>
    <div class="sir-editor-viewer w-e-text">
        <div v-html="content"></div>
    </div>
</template>
<script>

/**
 * @description: 富文本编辑器-viewer查看页面
 */

import { VIEWER_TIP } from './const';

export default {
    name: 'SirEditorViewer',
    props: {
        allowFullscreen: {

            // 是否允许全屏
            type: Boolean,
            default: true
        },
        toolbarDirection: {

            // 工具栏的位置,left or right
            type: String,
            default: 'right'
        },
        maxHeight: {

            // 最大高度
            type: String,
            default: '300px'
        },
        content: {

            // 文本内容
            type: String,
            default: ''
        }
    },
    data () {
        return {
            isFullscreen: false,
            VIEWER_TIP
        };
    },
    computed: {
        gettoolbarDirection () {
            return this.toolbarDirection === 'left' ? 'left' : 'right';
        },
        fullscreenText () {
            return this.isFullscreen ? VIEWER_TIP.FULLSCREEN_TIP.isFullscreen : VIEWER_TIP.FULLSCREEN_TIP.notFullscreen;
        }
    },
    methods: {
        toggleFullscreen () {
            this.isFullscreen = !this.isFullscreen;
        },
        onCopy () {
            this.$ok(_('复制成功'));
        },
        onError () {
            this.$fail(_('复制失败'));
        }
    }
};
</script>
<style lang="less" scoped>
/* stylelint-disable */
@toolbar-height: 40px;

.sir-editor-viewer {
    position: relative;
    display: inline-block;
    width: 100%;
    min-height: calc(@toolbar-height + 2px);
    padding: 0;
    cursor: pointer;

    &.fullscreen {
        position: fixed !important;
        top: 0 !important;
        left: 0 !important;
        z-index: 9999;
        display: flex;
        flex-direction: column;
        width: 100% !important;
        height: 100% !important;
        background-color: white;

        .content-box {
            max-height: 100% !important;
        }

        .toolbar-wrapper {
            position: static;
            display: block;
            border-bottom: 1px solid rgb(211, 211, 211);

            .toolbar-item {
                background-color: transparent;
            }
        }
    }

    &:hover {
        &:not(.fullscreen) {
            .toolbar-wrapper {
                display: block;
            }
        }
    }

    .toolbar-wrapper {
        position: absolute;
        top: 0;
        right: 0;
        left: 0;
        z-index: 100;
        display: none;
        height: @toolbar-height;
        line-height: @toolbar-height;

        &.left {
            .toolbar-item {
                float: left;
            }
        }

        &.right {
            .toolbar-item {
                float: right;
            }
        }

        .toolbar-item {
            width: @toolbar-height;
            height: 100%;
            color: rgb(129, 129, 129);
            text-align: center;
            background-color: #f6f6f6a8;
            transition: all 0.2s;

            &:hover {
                color: #333;
                background-color: #e4e4e4;
            }
        }
    }

    .content-box {
        padding: 6px 10px;
        overflow: auto;
    }
    /deep/ li {
        list-style: inherit;
    }
}
</style>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值