最初是为了实现需求,网上富文本编辑器不少,但是那些强大的工具条我的需求都用不到,我们只需要实现带有序列表或者无序列表文本编辑就可以了,如图:
自己动手封装,测试完善功能是麻烦了点,但是更加可控,才一百多行,上代码:
<!-- 无序、有序文本框 -->
<template>
<div class="serial-selecter">
<div class="serial-toolbar">
<el-tooltip effect="dark" content="无序列表" placement="top">
<img class="toolImg" src="./img/icon_li.png" alt="无序列表" @click="changeType('li')"/>
</el-tooltip>
<el-tooltip effect="dark" content="有序列表" placement="top">
<img class="toolImg" src="./img/icon_ol.png" alt="有序列表" @click="changeType('ol')"/>
</el-tooltip>
</div>
<div class="ipt-box">
<el-input type="textarea" ref="txtatea" v-model="serialtxt" :maxlength="maxlength" show-word-limit :placeholder="placeholder" :disabled="disabled" @keyup.native.enter="enterHandle" @input="inputHandle" :autosize="{ minRows: 3, maxRows: 6}"></el-input>
</div>
</div>
</template>
<script>
export default {
props: {
value: { default: ''}, // 必须要使用value
maxlength: { default: 100 },
placeholder: { default: '请输入' },
disabled: { default: false },
},
data () {
return {
liststr: '●',
type: '',
serialtxt: '',
}
},
watch: {
value: {
immediate: true,
handler(val) {
this.serialtxt = val;
}
}
},
methods: {
changeType(type) {
if(this.disabled) return
this.type = type;
this.enterHandle();
},
enterHandle() {
// console.log(this.$refs.txtatea.$el.firstChild);
let inputDom = this.$refs.txtatea.$el.firstChild;
let mousePos = inputDom.selectionStart; // 光标位置
let txtBeforeAll = this.serialtxt.slice(0, mousePos); // 光标之前的所有文本
let txtAfterAll = this.serialtxt.slice(mousePos); // 光标之后的所有文本
let lastIndex = txtBeforeAll.lastIndexOf('\n'); // 文本最后一个换行符位置
let curtxt = txtBeforeAll.slice(lastIndex+1); // 文本最后一个换行符位置到光标之间的文本(光标当前行的文本)
if(this.type == 'li') {
if(txtBeforeAll.slice(lastIndex+1,lastIndex+2) == this.liststr) { // 如果已有排序,则是取消排序
if(txtBeforeAll.slice(lastIndex+2,lastIndex+3) == ' ') {
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + txtBeforeAll.slice(lastIndex+3) + txtAfterAll;
}else {
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + txtBeforeAll.slice(lastIndex+2) + txtAfterAll;
}
this.type = '';
} else { // 否则,添加排序
// 如果已存在有序排序,则替换为无序
if((parseInt(curtxt) > 0) && (txtBeforeAll.slice(lastIndex+parseInt(curtxt).toString().length+1,lastIndex+parseInt(curtxt).toString().length+2) == '.')) { // 如果已有排序,则是取消排序
let numLength = parseInt(curtxt).toString().length;
if(txtBeforeAll.slice(lastIndex+numLength+2,lastIndex+numLength+3) == ' ') {
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + this.liststr+' ' + txtBeforeAll.slice(lastIndex+numLength+3) + txtAfterAll;
}else {
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + this.liststr+' ' + txtBeforeAll.slice(lastIndex+numLength+2) + txtAfterAll;
}
} else {
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + this.liststr+' ' + txtBeforeAll.slice(lastIndex+1) + txtAfterAll;
}
}
}else if (this.type == 'ol') { // 有序
if((parseInt(curtxt) > 0) && (txtBeforeAll.slice(lastIndex+parseInt(curtxt).toString().length+1,lastIndex+parseInt(curtxt).toString().length+2) == '.')) { // 如果已有排序,则是取消排序
// console.log(parseInt(curtxt).toString());
let numLength = parseInt(curtxt).toString().length;
if(txtBeforeAll.slice(lastIndex+numLength+2,lastIndex+numLength+3) == ' ') {
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + txtBeforeAll.slice(lastIndex+numLength+3) + txtAfterAll;
}else {
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + txtBeforeAll.slice(lastIndex+numLength+2) + txtAfterAll;
}
this.type = '';
} else { // 否则,添加排序
// 如果已存在无序排序,则替换为有序
if(txtBeforeAll.slice(lastIndex+1,lastIndex+2) == this.liststr) {
if(txtBeforeAll.slice(lastIndex+2,lastIndex+3) == ' ') {
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + '1.'+' ' + txtBeforeAll.slice(lastIndex+3) + txtAfterAll;
}else {
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + '1.'+' ' + txtBeforeAll.slice(lastIndex+2) + txtAfterAll;
}
}else {
// 根据上一行数字得出本行排序数字
let seIndex = txtBeforeAll.slice(0, lastIndex).lastIndexOf('\n') // 上一个换行符出现的位置
if(seIndex < 0 && !(parseInt(txtBeforeAll.slice(seIndex+1,seIndex+2)) > 0 && (txtBeforeAll.slice(seIndex+2,seIndex+3) == '.'))) { // 上一行未出现换行符且第一个未出现数字,则是第一个
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + '1.'+' ' + txtBeforeAll.slice(lastIndex+1) + txtAfterAll;
} else {
// 获取第一出现‘.’的位置
let dotIndex = txtBeforeAll.slice(seIndex).indexOf('.');
// console.log(dotIndex);
this.serialtxt = txtBeforeAll.slice(0, lastIndex+1) + (parseInt(txtBeforeAll.slice(seIndex+1,seIndex+dotIndex))+1)+'. ' + txtBeforeAll.slice(lastIndex+1) + txtAfterAll;
}
}
}
}
// 始终设置光标聚焦
inputDom.focus();
this.$emit('input', this.serialtxt);
},
inputHandle() {
this.$emit('input', this.serialtxt);
}
}
}
</script>
<style lang='scss' scoped>
.serial-selecter {
.serial-toolbar {
height: 30px;
border: 1px solid #eee;
border-bottom: none;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
background-color: #f8f9fb;
padding: 5px 20px;
.toolImg {
margin: 0 5px;
cursor: pointer;
height: 20px;
}
}
.ipt-box {
>>> .el-textarea__inner{
border-top-right-radius: 0;
border-top-left-radius: 0;
}
}
}
</style>
注意
- 有序、无序两个小图标我是让我们的UI给的,你们自行替换哦
- 我们项目用的scss,如果你们用的less,改一下就好