针对im输入框的一种处理方式
针对im输入框的一种处理方式
<template>
  <div class="chatInput">
    <!-- 通过contenteditable使普通的div变成富文本模式
 		由于要实现富文本的本地实时搜索,富文本没有change事件,而只能借用compositionstart,compositionend,input三个事件组合模拟出change
		compositionstart:当用户使用拼音输入法开始输入拼音时触发,在键入之前只会触发一次(因此此方法可以判断用户是否在使用拼音输入法)
 		compositionend:中文输入完成时触发
		input:输入什么都会触发
		设置一个变量doing,初始值为false,compositionstart触发将doing值为true,拼音输入完成触发compositionend,此时将doing置为false,开始搜索,input只有在doing为false时触发,即表示此时未使用拼音,直接搜索-->
    <div
      id="edit"
      ref="edit"
      contenteditable
      @compositionstart="doing = true"
      @compositionend="handleSearch"
      @input="handleKey"
      @keyup="carReply"
      @paste.prevent.trim="paste"
    >
      {{ splitContent.trim() }}
    </div>
    <div class="btn_wrap flex-end">
      按Ctrl+Enter换行,按Enter输出
      <el-button @click="send" type="primary"> 发送</el-button>
    </div>
  </div>
</template>
<script setup lang="ts" name="chatInput">
import { ref, onMounted } from "vue";
import { httpRequest } from "../chatUtil";
import { fileUpload } from "@/api/modules/chat";
interface PropsInterface {
  send: any;
}
const props = defineProps<PropsInterface>();
const doing = ref(false);
const edit = ref() as any;
// 这两属性都是控制输入框输入拆分的
let splitContent = "";
// 鼠标光标值
let lastEditRange = null as any;
onMounted(() => {
  // 初始化输入框聚焦,设置初始的选定对象节点
  const editDom = document.querySelector("#edit") as any;
  // 在失去焦点前保留上一次的选定对象节点
  editDom.addEventListener("blur", (e: any) => setSection());
  editDom.focus();
  setSection();
});
const setSection = () => {
  // 获取选定对象
  let selection = getSelection() as any;
  if (selection.getRangeAt && selection.rangeCount) {
    // 设置最后光标对象
    lastEditRange = selection.getRangeAt(0);
  }
};
const handleSearch = () => {
  doing.value = false;
  // 此时搜索
};
const handleKey = () => {
  const e = window.event as any;
  if (!doing.value) {
    // 此时搜索
  }
};
// 按键控制输出
const carReply = () => {
  const e = window.event as any;
  //   ctrl+enter实现换行,实测只有通过execCommened的方式插入换行符才可以实现换行
  if (e.ctrlKey && e.keyCode == 13) {
    document.execCommand("insertText", false, "\n");
  } else if (e.keyCode == 13) {
    send();
  }
};
// 文本框粘贴
const paste = (e: any) => {
  // 获取粘贴板数据
  const clipdata = e.clipboardData || (window as any).clipboardData;
  const files = clipdata.files;
  const text = clipdata.getData("text/plain");
  // 将文本按照html的格式粘贴进输入框中
  text && document.execCommand("insertHTML", false, text);
  if (files.length) {
    Array.from(files).forEach((file: any) => {
      // 只针对图片进行粘贴
      if (file.type.match(/^image\//)) {
        const windowURL = window.URL || window.webkitURL;
        // 将file生成一个blob链接,可用于本地直接打开,也可以用于下载
        const url = windowURL.createObjectURL(file);
        document.execCommand("insertImage", true, url);
      }
    });
  }
};
const send = () => {
  const edit = document.querySelector("#edit") as any;
  if (edit.childNodes.length == 0) {
    // 内容为空,给个提示阻止了
    return;
  }
  const _arr = Array.from(edit.childNodes) as any;
  // 递归遍历文本框内容,取dom元素的最深一级(实测发现富文本的html结构很乱,取到最深一层才能拿到内容)
  contentRevese(_arr);
  splitContent && props.send(splitContent);
  // 输出后清空
  splitContent = "";
  edit.textContent = "";
};
const contentRevese: any = async (_arr: any) => {
  for (let i = 0; i < _arr.length; i++) {
    // 拿最深层
    if (_arr[i].childNodes.length > 0) {
      contentRevese(Array.from(_arr[i].childNodes));
    } else {
      if (_arr[i].tagName == "IMG") {
        // 在取文本内容时,多行的文本应同时取,不能像图片一样,取到了就直接发送
        props.send(splitContent);
        splitContent = "";
        // blob路径转file
        let img = (await httpRequest(_arr[i].src)) as any;
        // 该blob对象用完后,应及时回收资源,节省内存
        const windowURL = window.URL || window.webkitURL;
        windowURL.revokeObjectURL(_arr[i].src);
        const formData = new FormData() as any;
        formData.append("files", img);
        formData.append("type", "3");
        const { data } = (await fileUpload(formData)) as any;
        props.send(`img[${data.url}][${data.file_name}][size:${data.size}]`);
      } else {
        // 匹配下一段文本加换行
        splitContent += _arr[i].textContent + "\n";
      }
    }
  }
};
</script>
												
											针对im输入框的一种处理方式的更多相关文章
- spring笔记--依赖注入之针对不同类型变量的几种注入方式
		
控制反转和依赖注入讲的都是一个概念,只不过是站在了不同的角度,所谓的依赖注入: 是指在运行期,由外部容器动态地将依赖对象注入到组件中.当spring容器启动后,spring容器初始化,创建并管理bea ...
 - IndexFlatL2、IndexIVFFlat、IndexIVFPQ三种索引方式示例
		
上文针对Faiss安装和一些原理做了简单说明,本文针对标题所列三种索引方式进行编码验证. 首先生成数据集,这里采用100万条数据,每条50维,生成数据做本地化保存,代码如下: import numpy ...
 - 巨蟒python全栈开发数据库前端6:事件onclick的两种绑定方式&&onblur和onfocus事件&&window.onload解释&&小米商城讲解
		
1.回顾上节内容(JavaScript) 一.JavaScript概述 1.ECMAScript和JavaScript的关系 2.ECMAScript的历史 3.JavaScript是一门前后端都可以 ...
 - 多对多三种创建方式、forms组件、cookies与session
		
多对多三种创建方式.forms组件.cookies与session 一.多对多三种创建方式 1.全自动 # 优势:不需要你手动创建第三张表 # 不足:由于第三张表不是你手动创建的,也就意味着第三张表字 ...
 - 多对多的三种创建方式-forms相关组件-钩子函数-cookie与session
		
多对多的三种创建方式 1.全自动(推荐使用的**) 优势:第三张可以任意的扩展字段 缺点:ORM查询不方便,如果后续字段增加更改时不便添加修改 manyToManyField创建的第三张表属于虚拟的, ...
 - Web APi之认证(Authentication)两种实现方式【二】(十三)
		
前言 上一节我们详细讲解了认证及其基本信息,这一节我们通过两种不同方式来实现认证,并且分析如何合理的利用这两种方式,文中涉及到的基础知识,请参看上一篇文中,就不再叙述废话. 序言 对于所谓的认证说到底 ...
 - CSS垂直居中的11种实现方式
		
今天是邓呆呆球衣退役的日子,在这个颇具纪念意义的日子里我写下自己的第一篇博客,还望前辈们多多提携,多多指教! 接下来,就进入正文,来说说关于垂直居中的事.(以下这11种垂直居中的实现方式均为笔者在日常 ...
 - Android开发之基本控件和详解四种布局方式
		
Android中的控件的使用方式和iOS中控件的使用方式基本相同,都是事件驱动.给控件添加事件也有接口回调和委托代理的方式.今天这篇博客就总结一下Android中常用的基本控件以及布局方式.说到布局方 ...
 - Web负载均衡的几种实现方式
		
Web负载均衡的几种实现方式摘要:负载均衡(Load Balance)是集群技术(Cluster)的一种应用.负载均衡可以将工作任务分摊到多个处理单元,从而提高并发处理能力.目前最常见的负载均衡应用是 ...
 - http协议(三)几种数据传输方式
		
说说http协议的一些特点: 1)无状态 http协议是一种自身不对请求和响应之间的通信状态进行保存的协议,即无状态协议. 这种设置的好处是:更快的处理更多的请求事务,确保协议的可伸缩性 不过随着we ...
 
随机推荐
- Keil 2032 license 解决方法(keygen)
			
https://pan.baidu.com/s/1nH_KrsHoLEJlJQKhfIoXHA
 - SQL之rand,round,floor,ceiling,cast小数处理函数
			
rand():取随机数,select rand() from T 结果:0.635811742495648 round():保留N位小数,四舍五入 select round(1.0446,N) flo ...
 - Linux 同步分发脚本
			
shell 内容 #!/bin/bash #1. 判断参数个数 if [ $# -lt 1 ] then echo Not Enough Arguement! exit; fi 尚硅谷大数据技术之 H ...
 - QTreewidget树状列表右击事件
			
树状列表右击事件(添加 删除 修改等操作) 思路:首先我们需要一个void contextMenuEvent(QContextMenuEvent * event); 管理Menu事件的一个接口 此接口 ...
 - 修改浏览器搜索引擎:设置网址格式(用“%s”代替搜索字词)
			
浏览器搜索引擎设置,如何填写网址格式(用"%s"代替搜索字词)? 以下收集部分: 综合检索 名称 关键字 网址(用"%s"代替搜索字词) 必应 cn.bing. ...
 - springboot项目 报错No mapping for GET /css/bootstrap.css,前端无法展示样式
			
说来也奇怪,前几天刚写完的项目 写的好好的 现在打开他就加载不了前端的静态资源了 报错No mapping for GET /css/bootstrap.css 解决方法: 新建一个配置类 ,将静态资 ...
 - IndexError: invalid index of a 0-dim tensor. Use tensor.item() to convert a 0-dim tensor to a Python number
			
print('Epoch[{}/{}], loss:{:.6f}'.format(epoch+1,num_epoch,loss.data[0])) 将loss.data[0] 改为loss.item( ...
 - Scala操作Kakfa API
			
如需大数据开发整套视频(hadoop\hive\hbase\flume\sqoop\kafka\zookeeper\presto\spark):请联系QQ:1974983704 由于我使用的是kafk ...
 - LVS简略介绍
			
一.lvs是什么 LVS是 Linux Virtual Server 的简称,也就是Linux虚拟服务器.这是一个由章文嵩博士发起的一个开源项目,它的官方网站是 http://www.linuxvir ...
 - Net6读取AppSettings.json
			
1.创建Helper类 public class AppHelper { private static IConfiguration _config; public AppHelper(IConfig ...