当前位置:网站首页>Wangedit rich text editor usage (detailed)
Wangedit rich text editor usage (detailed)
2022-06-29 20:19:00 【Brave Niuniu, dash dash dash dash dash dash dash dash dash dash】
wangeditor The rich text editor uses
1. Download and install
yarn add wangeditor -S
npm i wangeditor
2. Import
import E from ‘wangeditor’;
3. Specific use
3.1 Page using
<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 Rich text editor related files
src/components/business/editor/
3.2.1 custom_btns/full_screen.ts
/** * @description: Full screen button */
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: Rich text editor configuration */
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 menu
class?: string; // The actual corresponding className Style naming is different according to menuItem...
tip: string; // title Suspended prompt
}
// Here, only those buttons suspended without menu selection are prompted
export const MENU_TIPS: MenuTipItem[] = [
{
menuItem: 'bold',
tip: _(' bold ')
},
{
menuItem: 'italic',
tip: _(' Italics ')
},
{
menuItem: 'underline',
tip: _(' Underline ')
},
{
menuItem: 'strikeThrough',
tip: _(' Delete line ')
},
{
menuItem: 'link',
tip: _(' Insert link ')
},
{
menuItem: 'justify',
tip: _(' Alignment mode ')
},
{
menuItem: 'quote',
class: 'quotes-left',
tip: _(' quote ')
},
{
menuItem: 'code',
tip: _(' Insert code ')
},
{
menuItem: 'image',
tip: _(' Insert a picture ')
},
{
menuItem: 'table',
class: 'table2',
tip: _(' Insert table ')
},
{
menuItem: 'undo',
tip: _(' revoke ')
},
{
menuItem: 'redo',
tip: _(' repeat ')
},
{
menuItem: 'fullscreen',
tip: _(' Full screen ')
},
{
menuItem: 'indent',
tip: _(' Indent ')
},
{
menuItem: 'splitLine',
tip: _(' Split line ')
},
{
menuItem: 'todo',
tip: _(' To do list ')
},
{
menuItem: 'lineHeight',
tip: _(' Row height ')
}
];
const FULLSCREEN_TIP = {
isFullscreen: _(' Exit full screen '),
notFullscreen: _(' Full screen ')
};
export const VIEWER_TIP = {
FULLSCREEN_TIP
};
/** * File size units */
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>
<!-- To simulate 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: Rich text editor - Components */
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: {
// Text content
type: String,
default: ''
},
placeholder: {
type: String,
default: _(' Please enter the text ')
},
menus: {
// Menu configuration , Show all by default
type: Array,
default() {
return [];
}
},
isBase64: {
// Whether or not to Base64 stored
type: Boolean,
default: false
},
customUploadImg: {
// Whether to completely customize the upload
type: Function,
default: null
},
callback: Function, // Custom upload callback
serverUrl: {
// Upload address of the upload situation
type: String,
default: ''
},
beforeUpload: Function, // Verification before uploading pictures in plug-in mode
reqFileName: {
// Upload picture request picture name
type: String,
defaule: 'file'
},
acceptImgs: {
// Rich text supports uploading image formats
type: Array,
default: () => ['png', 'jpg', 'jpeg', 'bmp']
},
uploadImgParams: {
// Configure upload parameters , By default, it will be added to formdata in .
type: Object,
default() {
return {
};
}
},
imgSep: {
type: String,
default: 'file:'
},
uploadHeaders: {
type: Object,
default() {
return {
};
}
},
menuTooltipPosition: {
type: String,
default: 'down'
},
paramsWithUrl: {
// Splice parameters to url in
type: Boolean,
default: false
},
maxSize: {
// No transmission means no size limit
type: Number,
default: 0
},
timeout: {
// Upload timeout
type: Number,
default: 5000
},
maxNum: {
// Upload a few at most
type: Number,
default: 0
},
showLinkImg: {
// Whether or not shown “ Network picture ”tab, Default display
type: Boolean,
default: false
},
blankText: {
type: String,
default: _(' This entry cannot be empty ')
},
clickFocus: {
// Click to focus on rich text now , Pasting pictures will require
type: Boolean,
default: false
},
checkBase64: Function,
showMenusTip: {
type: Boolean,
default: false
},
menusTips: {
// Add... To the toolbar buttons title Suspended prompt
type: Array,
default() {
return MENU_TIPS;
}
},
preventScroll: {
type: Boolean,
default: false
}
},
data() {
return {
editorBar: uuid(),
editorElem: uuid(),
tipTime: 2000, // Time of prompt display
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: Rich text editor */
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?;
/** Content */
private editorContent?: string | null = null;
/** File upload path */
private uploadUrl?: string = API_PREFIX + '/attachments';
private menus = [
'head', // title
'bold', // bold
'fontSize', // Font size
'fontName', // typeface
'italic', // Italics
'underline', // Underline
'strikeThrough', // Delete line
'foreColor', // Text color
'backColor', // The background color
'link', // Insert link
'list', // list
'justify', // Alignment mode
'image', // Insert a picture
'quote', // quote
// 'emoticon', // expression
'table', // form
// 'video', // Insert the video
// 'code', // Insert code
'undo', // revoke
'redo', // repeat
'lineHeight', // Row height
'indent', // Increase Indent / Reduce indent
'fullscreen', // Full screen
'splitLine', // Split line
'todo' // To do list
]; // Rich text toolbar To configure
@Watch('value', {
immediate: true })
private handleValueChange (value: string) {
// Prevent when editing , Trigger 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; // No more than 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) {
// by 0 No more uploads
vm.$warn(_(` Upload at most {0} File `, this.fileNumber), {
autoHide: true });
return true;
}
// Total number of intercepts that can be uploaded
if (files.length > canUploadCount) {
files = files.slice(0, canUploadCount);
vm.$warn(_(` Upload at most {0} File `, 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: Rich text editor -viewer view pages */
import {
VIEWER_TIP } from './const';
export default {
name: 'SirEditorViewer',
props: {
allowFullscreen: {
// Allow full screen
type: Boolean,
default: true
},
toolbarDirection: {
// The location of the toolbar ,left or right
type: String,
default: 'right'
},
maxHeight: {
// Maximum height
type: String,
default: '300px'
},
content: {
// Text 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(_(' Replication success '));
},
onError () {
this.$fail(_(' copy failed '));
}
}
};
</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>
边栏推荐
- Proxmox cluster node crash handling
- Sword finger offer 66 Building a product array
- Measures to support the development of advanced manufacturing industry in Futian District of Shenzhen in 2022
- Jmeter之BeanShell详解和夸线程调用
- 【编译原理】语义分析
- 4-1 port scanning technology
- liunx指令
- Flume配置1——基础案例
- Comparable比较器写法&ClassCastExcption类转换异常
- As the "only" privacy computing provider, insight technology is the "first" to settle in the Yangtze River Delta data element circulation service platform
猜你喜欢

The explain statement in MySQL queries whether SQL is indexed, and several types in extra collate and summarize

Configuration du Flume 4 - source personnalisée + sink

数据链路层

Flume理论
![[notes] take notes again -- learn by doing Verilog HDL – 008](/img/7f/0ca73446247455ac4d8f9667083a87.png)
[notes] take notes again -- learn by doing Verilog HDL – 008

.NetCore统一认证授权学习——第一次授权(2)

Flume配置3——拦截器过滤

如何设置 Pod 到指定节点运行

Bigder:自动化测试工程师

Regular expression series of mobile phone numbers
随机推荐
关于印发宝安区重点产业项目和总部项目遴选及用地保障实施细则(2022修订版)的通知
CorelDRAW最新24.1.0.360版本更新介绍讲解
Flume configuration 1 - basic case
Startservice() procedure
2021 CCPC 哈尔滨 E. Power and Modulo (思维题)
Defense cornerstone in attack and defense drill -- all-round monitoring
2021 CCPC Harbin E. power and modulo (thinking questions)
Sword finger offer 41 Median in data stream
Union find
A great open source image watermarking solution
Zotero journal Automatic Matching Update Influencing Factors
Oracle保留字查询
SSH命令及使用说明
ASP. Net core creates razor page and uploads multiple files (buffer mode) (Continued)
JMeter BeanShell explanation and thread calling
【摸鱼神器】UI库秒变低代码工具——表单篇(一)设计
How to set a pod to run on a specified node
Go: how to write a correct UDP server
第二章(物理层)
mapbox-gl开发教程(十二):加载面图层数据