1 实现效果
2 组件代码
1. 模板代码 template
<template>
<template v-if="!noColor">
<ColorPicker v-model="colorData.color" size="small" :colors="colorData.colors"/>
<Button size="small" ghost type="primary" @click.native="addColorTag">配色</Button>
</template>
<Button v-if="hasHyperLink" class="mr-5" size="small" ghost type="primary" @click.native="addHyperLink">超链接</Button>
<Button v-if="!noPreview" class="mr-5" size="small" ghost type="primary" @click.native="previewClick">预览</Button>
<slot></slot>
<Input v-if="!inputSelector" ref="editorRef" type="textarea" :value="content" placeholder="请输入内容" :autosize="{ minRows:3, maxRows: 8 }" @input="inputChange"></Input>
</template>
2. 处理应用程序逻辑 ColorDataCls类
2.1 邮件预设颜色常量 MAIL
const MAIL = {
defaultColor: '#9d1802',
createPresetColors: function(type) {
switch (type) {
case 'notice':
return ['#9d1802', '#00800f', '#964129'];
default:
return ['#9d1802', '#00800f', '#e57c00', '#3ef672', '#964129'];
}
}
}
2.2 构造者函数+基本功能
class ColorDataCls {
constructor(props) {
this.color = MAIL.defaultColor; // 目前颜色
this.colors = MAIL.createPresetColors(); // 颜色列表
props && Object.assign(this, props);
}
// 静态方法 为选中文字添加颜色标签
static addColorTag(inputEle, inputContent, color) {}
// 静态方法 为选中文字添加超链接
static addHyperLink(inputEle, inputContent) {}
// 静态方法 标签转换
static previewContentTransform(inputEle) {}
}
3. 父组件可传递属性 props
export default {
let idNum = 0;
createId() {
return `CWID${idNum++}`;
},
props: {
content: String,
// 去掉配色
noColor: Boolean,
// +超链接
hasHyperLink: Boolean,
// 去掉预览按钮
noPreview: Boolean,
// css选择器,存在:不用组件的文本框
// 注意:谨防选择器重复,比如 Modal 会提升到 body,跳转标签时会继续存在
inputSelector: String,
// 颜色数据,默认用邮件色值
colorData: {
type: Object,
default() {
return new ColorDataCls();
}
}
}
}
4. 事件处理程序
4.1 配色
addColorTag() {
let element =
this.inputSelector ?
document.querySelector(this.inputSelector) : this.$refs.editorRef.$refs.textarea;
this.$emit(
'update:content',
ColorDataCls.addColorTag(element, this.content, this.colorData.color)
);
this.$emit('addColor', this.colorData.color);
}
4.2 预览
previewClick() {
this.$emit(
'preview',
ColorDataCls.previewContentTransform(this.content)
);
}
4.3 监听输入框事件
inputChange(value) {
this.$emit('update:content', value);
}
5. 处理应用程序逻辑具体实现
5.1 为选中文字添加颜色标签
static addColorTag(inputEle, inputContent, color) {
let rangeData = {
text: '',
start: 0,
end: 0
};
if(inputEle.setSelectionRange) {
rangeData.start = inputEle.selectionStart;
rangeData.end = inputEle.selectionEnd;
rangeData.text =
rangeData.start != rangeData.end ?
inputContent.slice(rangeData.start, rangeData.end) : '';
return `${inputContent.slice(0, rangeData.start)}<color=${color}>${rangeData.text}</color>${inputContent.slice(rangeData.end)}`;
}
}
5.2 标签转换
static previewContentTransform(inputEle) {
return inputContent
.replace(/\n/g, '<br/>')
.replace(/\s/g, ' ')
.replace(/(size=|color=)/g, 'font $1')
.replace(/(size>|color>)/g, 'font>')
.replace(/<a\ /g, "<a style='font-size:22px; color:#379a61; text-decoration:underline' target='_blank' ")
}
5.3 有限层标签嵌套
static changeColorRegs(content) {
const reg = /\[color=(.*?)\]((\[color=(.*?)\](.*?)\[\/color\]|.)*?)\[\/color\]/g
return content.replace(reg, (match, p1, p2) => {
let str = `<span style="color: ${p1}">`
if(reg.test(p2)) {
return str + this.changeColorRegs(p2) + '</span>'
} else {
return str + p2 + '</span>'
}
})
}
5.4 找到闭合标签的前一个同类型起始标签实现配对
static changeColorRegs(content, contentFlage) {
const regBegin = /\[color=(.*?)\]/ // 起始标签
const regFindLast = /(.*)\[color=(.*?)\](.*)/g // 最后一个匹配的起始标签
const regContent = /(\[color=.*?\][\s\S]*?)(?=\[\/color\])\[\/color\]/g // 找到闭合标签的前一个起始标签
return content.replace(regContent, (match, p1) => {
if(regBegin.test(p1)) {
return p1.replace(regFindLast, (inner_match, inner_p1, inner_p2, inner_p3) => {
if(regBegin.test(inner_p1)) {
contentFlage.colorFlage = true
}
return `${inner_p1}<span style="color: ${inner_p2}">${inner_p3}</span>`
})
}
return match
})
}
static previewContentTransform(inputContent) {
let content = inputContent
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\n/g, '<br/>')
.replace(/\s/g, ' ')
let contentFlage = {
colorFlage: false,
sizeFlage: false,
bFlage: false,
}
do {
contentFlage.colorFlage = false
content = this.changeColorRegs(content, contentFlage)
} while(contentFlage.colorFlage)
return content
}
3 应用
3.1 配色+预览
<color-wrapper :content.sync="pepper.data.content" @preview="mailPreviewOpen">
<span class="text-danger">{money}替换档位金额数</span>
</color-wrapper>
<!-- v-html -->
<mail-preview :data="mailPreviewData"></mail-preview>
mailPreviewOpen(content) {
let molData = this.pepper.data;
this.mailPreviewData.title = molData.title;
// 内容
this.mailPreviewData.content = content;
this.mailPreviewData.isDisplay = true;
}