仿微信@功能

原创...2022年5月25日

基于 vue2.x + vant2.x 实现的仿微信@功能(稍作修改即可支持原生 js 和 react),兼容 pc 端和手机端,如有 bug,欢迎提出~

年后分配到一个新项目 - 企业微信论坛。项目涉及到发帖子、评论、留言等功能,刚好和我的个人博客功能类似,不能说一模一样吧,也八九不离十了(内心偷乐 😁😁)。

但是,令人头疼的问题来了,附加了一个@功能,即在评论框中输入@实现通知用户的功能。这个可以说触及到我的的知识盲点了,但其实很多应用都有这类功能了,如:微博、QQ、微信、企业微信... 哎,需求需要,不得不做,静下心来好好研究研究叭~

一、技术方案分析

方案很多,适合自己、适合团队才称得上完美。 这里归纳了几种常用的方案:

  1. textarea、input (例:新浪微博)
  2. contenteditable (例:QQ 空间, 掘金)
  3. 富文本 (例:企业微信 TAPD)

二、效果和功能描述

最终目标:实现仿微信@用户功能。

demo 地址: zugelu - 仿微信@功能open in new window

1. 效果图

mention.gif

2. 主要功能

  1. 在输入框内任何地方输入@,或点击@按钮(demo 中Show @ Menu按钮),弹出选择用户弹框
  2. 人员弹框支持搜索功能、支持异步
  3. 选择人员,在光标后追加@名称,并绑定人员相关数据
  4. @名称作为整体,光标不可游走其中
  5. 删除时,将@名称作为一个整体进行删除

实现以上功能,可以说已经完成了一个高仿微信@功能。

三、准备工作

本案例是基于vue-quill-editor 富文本编辑器open in new window(v3.0.6)和quill-mentionopen in new window(v3.1.0)来实现的。需下载 quill 相关依赖。

npm i vue-quill-editor quill-mmention --save

初始化项目结构

<template>
  <div id="at">
    <!-- editor -->
    <quill-editor
      ref="myQuillEditor"
      v-model="content"
      :options="editorOption"
    />
    <!-- toolbar -->
    <div id="toolbar"></div>
  </div>
</template>

<script>
  import { quillEditor } from "vue-quill-editor";
  import "quill/dist/quill.snow.css";
  import "quill-mention";

  export default {
    name: "at-mention",
    components: {
      quillEditor,
    },
    data() {
      return {
        content: "", // 富文本html
        isClickMention: false, // 是否点击@按钮打开@菜单弹框
        isChineseInputMethod: false, // 是否中文输入法状态
        show: false, // 弹框显示状态
        editorOption: {
          placeholder: "说点什么呢。。。",
          modules: {
            toolbar: {
              // 自定义toolbar,本案例隐藏
              container: "#toolbar",
            },
            mention: {
              // 将 quill-mention 配置传递给 quill
              mentionDenotationChars: ["@"], // 指定哪些字符可以触发@提及
              source: function (searchTerm, renderList, mentionChar) {
                // @提及回调
                console.log(searchTerm, renderList, mentionChar);
                renderList([], searchTerm); // 因为我们要自定义弹框,所以这里把默认的@用户数据置空
              },
            },
          },
        },
      };
    },
    computed: {
      editor() {
        return this.$refs.myQuillEditor.quill;
      },
    },
  };
</script>

<style>
  /* quill-toolbar */
  .ql-toolbar {
    padding: 0 !important;
    border: 0 !important;
  }
</style>

初始化完成,就可以使用基于quill-mention实现的@功能了,但现有功能并不能满足我的需求(@之后从底部弹出用户列表),需要在现有基础上进行改造升级。

提示:

  1. mention -> source 中,renderList([], searchTerm), 因为我们要自定义弹框,所以这里把默认的@用户数据置空。
  2. css 样式中.ql-toolbar ,去除默认边距,目的隐藏toolbar
  3. 后续data所用到的数据,在初始化阶段已全部列出

四、实现步骤

1. 中文输入法的判断

中文模式下输入@要进行判断,如:输入“说点什么呢@”,这个时候不能监听@事件。

如何判断中文输入? 当用户使用中文输入法开始输入中文时,compositionstart事件就会被触发。当文中文输入完成或取消时,compositionend事件将被触发。利用这个机制我们就可以判断是否中文状态。

<template>
  <div id="at">
    <!-- editor -->
    <quill-editor
      v-model="content"
      @compositionstart.native="onCompositionstart"
      @compositionend.native="onCompositionend"
    />
  </div>
</template>

<script>
  export default {
    data () {
      return {
        isChineseInputMethod: false, // 是否中文输入法状态
      }
    },
    methods: {
      // 中文输入触发
      onCompositionstart () {
        this.isChineseInputMethod = true
      },
      // 中文输入关闭
      onCompositionend () {
        this.isChineseInputMethod = false
      }
      ...
    }
  }
</script>

2. @事件的监听

判断设备类型(适配电脑和手机端)

// 判断设备是否为 Mobile (提示:请勿在浏览器调试模式下,切换手机模式,否则会导致@功能失效)
isMobile () {
  return navigator.userAgent.match(/(iPhone|iPod|Android|ios|iOS|iPad|Backerry|WebOS|Symbian|Windows Phone|Phone)/i)
},

根据终端判断用户是否输入@符号

// 判断是否输入 @符号
isAtCode (e) {
  return this.isMobile() ?
    ((e.keyCode === 229 || e.keyCode === 50) && e.code === 'Digit2' && e.key === '@')
    : (((e.keyCode === 50 && e.key === '@') || (e.keyCode === 229 && e.code === 'Digit2')) && e.shiftKey)
}

监听富文本 keydown 事件,判断是否为非中文状态下键入的@符号,如是,则打开用户列表弹框

// 键盘按下
onKeyDown (e) {
  // 判断状态非中文输入法,并且监听到了@的事件
  if (!this.isChineseInputMethod && this.isAtCode(e)) {
    // 输入@打开@菜单弹框
    this.show = true
  }
},

PS:本案例用户列表的代码就不贴了,可根据需求自定义,或前往at-mentionopen in new window获取 demo 代码。

3. 点击用户生成@标签

使用quill-mention提供的 API - insertItem来实现。 当点击@列表中的想要@的用户,则触发selectItem事件,把@的内容插入到对应位置。

// 选择要@的用户
selectItem (item) {
  this.insertItem(item)
  this.show = false
},
// 插入@内容,item即为用户信息
insertItem (item) {
  const mention = this.editor.getModule('mention')
  mention.insertItem({ id: item.id, value: item.name, denotationChar: '@' }, true)
}

这样会有一个问题,@用户的时候多了一个@符号,原因是insertItem会默认帮我们添加一个@符号,加上手动输入的@符号就多出一个@。 所以这里需要特殊处理一下,调用history.undo()撤销 API(需要额外配置下history参数):

editorOption: {
  modules: {
    ...
    history: { // history 配置(History模块负责处理Quill的撤销和重做)
      delay: 0 // 将延迟设置为0,那么几乎每次输入字符都会被记录成一个变化,然后,撤销动作就会一次撤销一个字符
    }
  }
},
// 选择要@的用户
selectItem (item) {
  // 如果不是通过@按钮打开的弹框,则需要删除多余的@符号
  if (!this.isClickMention) {
    // 撤销一步,删除手动输入的@符号(需在modules中配置history: { delay: 0 })
    this.undo()
  }
  this.insertItem(item)
  this.show = false
},
// 撤销
undo () {
  this.editor.history.undo();
},

4. 点击外部的@按钮插入@标签

非常简单,直接调用 API - insertItem 来插入@标签即可。(会默认添加@符号)

五、总结

这个功能我自己测试没问题,兼容性也不错(并未进行详细测试、可能会存在其他问题),如果使用中遇到问题,欢迎提出反馈,希望能和大家一起进步~

本文首发于足各路的博客open in new window,后续会同步更新到掘金open in new window知乎open in new windowCSDNopen in new window关注足各路、前端不迷路!

上次编辑于: 2022/8/5 09:41:10
评论
Powered by Waline v2.6.2