vue2中textArea@功能的实现

290 阅读2分钟

前提提示:项目中的@功能为跳转到用户列表然后回到原来的聊天页面,使用了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;
},