前提提示:项目中的@功能为跳转到用户列表然后回到原来的聊天页面,使用了vuex。这里主要提供思路 转载请标明出处
完成实现:
- 标记后续删除
- 标记文字中间删除
- 可以直接输入@
主要思路为:
- 监听返回的@人物 添加@ 数据
- 基于@和特殊字符的文字标记 对文本框进行处理 加上一个特殊字符【⠀】 这个不是空格 注意注意
- 利用Map记录位置 也是从光标位置进行添加位置
- 在输入中进行比对 添加还是删除
详细解析
选择@用户后返回页面进行构建Map
watch: {
chooseChannelMember:{
handler(val, olval){
if(val){
// 监听返回的@人物 添加@ 数据
// 先丢到val 里面去 利用set去重
console.log('监听到@回来了', val)
// this.atUserInfo.push(val)
// 然后对文本框进行处理 加上一个特殊字符【⠀】 这个不是空格 注意注意
// ====这里插入数据应该是在光标的位置 不然会有问题 ====
// 获取光标位置
let position = this.navigateToPos;
// console.log(position)
// 也是从光标位置进行添加位置
// 这里记录@功能的开始位置和结束位置
let addStr = val.nick + '⠀'
// 开始位置 结束位置
let atIndex = position - 1
let lastIndex = position + addStr.length - 1;
// 记录AT 长度
this.atMap[val.nick+'-'+ val.userID] = {
index: atIndex,
tagIndex: lastIndex,
len: addStr.length+1,
userId: val.userID
}
const beforeStr = this.chatVal.substring(0, position)
const afterStr = this.chatVal.substring(position, this.chatVal.length)
this.chatVal = beforeStr + addStr + afterStr;
}
},
deep:true
},
}
完成输入框的监听和diff
// 在watch方法中中
chatVal(val,oldval){
// console.log('onwatchInputFn')
// 先对比是增加还是减少
const isAdd = oldval.length < val.length
this.isAdd = isAdd
// console.log('chatVal isAdd', isAdd)
// 如果是减少的 需要处理最后一个@
// 如果pos 不在最后一个
// 需要重构数据
// 如果输入光标在 中间 那么需要 rebuild
const reskey = this.judgePos
if(reskey){
this.rebuildIndex(reskey)
}
if(!isAdd){
// 并且里面有@
// 这里要区分是本来就想输入@ 还是想到AT人
// 遍历输入数据 找到@和特殊标识的位置
let delIndex = null
let keys = Object.keys(this.atMap)
for(let index in keys){
const key = keys[index]
let left = this.atMap[key].index
let right = this.atMap[key].tagIndex
let len = this.atMap[key].len
if(this.chatVal[left]!='@' || this.chatVal[right]!='⠀'){
// 删除
const beforeStr = val.substring(0, left)
// this.atIndex[index] + this.atNickLen[index] - 1
const afterStr = val.substring(right, val.length)
this.chatVal = beforeStr+ afterStr
delete this.atMap[key]
break
}
}
this.rebuildIndex()
}else{
// 增加 这里不在 @input 里面判断了
let pos = this.getPositionForTextAreaTest()
this.navigateToPos = pos
this.navigateToType = true
let isAt = this.chatVal[pos-1]
let atPreChar = null;
let canNavTo = false
// 这里是处理 @ 逻辑
if(this.chatVal.length == 1 || pos == 1){
canNavTo = true
}else{
atPreChar = this.chatVal[pos-2]
canNavTo = atPreChar==' ' || atPreChar == '⠀'
}
if (isAt == '@' && canNavTo) {
// 跳转选择用户的页面
}
}
if(val.length == 0){
this.atMap = {}
}
}
判断当前位置方法
judgePos(){
let keys = Object.keys(this.atMap)
let res = null;
for(let index in keys){
const key = keys[index]
let left = this.atMap[key].index
let right = this.atMap[key].tagIndex
if(left< pos && right> pos){
res = key
break
}
}
return res
}
重新建立位置关系
// rebuild 只处理删除 at用户后和添加输入的时候
rebuildIndex(){
const pos = this.getPositionForTextAreaTest();
let ctrl = this.$refs.textarea.$el.querySelector('textarea')
// 这里要处理 就是空格加@的数据 这几个数据一定要长度保持一致
let val = this.chatVal
let tagIndex =[];
let atIndex = [];
for(let index = 0; index < val.length; index++){
let s = val[index]
switch (s) {
case '⠀':
tagIndex.push(index)
break;
default:
break;
}
}
// 找到应该对应第几个 map
let keys = Object.keys(this.atMap)
let tagMap = {}
for(let key of keys){
const item = this.atMap[key]
tagMap[item.tagIndex] = key
}
let mapval = Object.keys(tagMap)
mapval.sort((a,b)=>{
return a-b
})
for(let ti in tagIndex){
// 如果 找到的tag 标记和 原来的tag标记不一样 说明变动了
if(tagIndex[ti] != mapval[ti]){
let key = tagMap[mapval[ti]]
this.atMap[key].tagIndex = tagIndex[ti]
let leftStrIndex = tagIndex[ti]- this.atMap[key].len + 1
if(this.chatVal[leftStrIndex] != '@'){
// 说明这个数据 不对 移除这个map 和特殊标记
let arr = this.chatVal.split('')
arr[tagIndex[ti]] = ' '
this.chatVal = arr.join('')
delete this.atMap[key]
// this.$nextTick(()=>{
// ctrl.setSelectionRange(leftStrIndex,leftStrIndex);
// })
}else{
this.atMap[key].index = leftStrIndex
}
}
}
// console.log('rebuildIndex',this.atMap)
},
获取光标位置
// 获取光标位置
getPositionForTextAreaTest(){
// console.log(this.$refs.textarea.$el.querySelector('textarea'))
let ctrl = this.$refs.textarea.$el.querySelector('textarea')
let CaretPos = 0;
if(ctrl.selectionStart || ctrl.selectionStart == '0'){
CaretPos = ctrl.selectionStart;
}
return CaretPos;
},