vue 加 websocket 聊天

<template>
  <div style="height: 100%; width: 100%; background-color: #fff">
    <div class="wrap">
      <!-- 头部 -->
      <div class="titleBox">
        <img
          src="@/assets/image/avatar.png"
          style="argin: 10px 20px 10px 20px;width: 40px;height: 40px;"
          class="head_portrait"
        />
        <span style="color: #fff;font-size: 15px;">官方客服</span>

      </div>
      <!-- 底部 -->
      <div class="infoBox">
        <!-- 左边用户列表 -->
        <div class="userList">
          <div class="searchBox">
            <el-input  placeholder="请输入内容" v-model="search" class="input-with-select" size="mini" @input="inquire">
              <i  class="el-icon-search el-input__icon" slot="suffix" @click="handleIconClick"  />
            </el-input>
<!--            <el-button-->
<!--              icon="el-icon-plus"-->
<!--              size="mini"-->
<!--              type="primary"-->
<!--              @click="dialogVisible = true"-->
<!--            ></el-button>-->
          </div>
          <div class="userListBox" ref="scrollUserBox" id="userBox">

            <div v-if="list!=null && list.length > 0"  v-for="(item, index) in list" :key="index"
              @click="getAct(item, index)" :class="item.opposUserId == act ? 'userFlexAct' : 'userFlex'" >
              <el-badge :hidden="item.unreadCount ==0" :value="item.unreadCount" :max="99" class="item">
                <div>
                  <img  :src="item.avatar"   class="head_portrait2"  style="margin-left: 20px ; "  />
                </div>
              </el-badge>
              <div style="margin-right: 10px;"></div>
              <div style="margin-right: 40px">
                <div style="color: #565656" class="nickName">
                  {
  { item.nickName }}
                </div>
                <div class="userInfo" v-if="item.messageType==1" >{
  {item.message}}</div>
                <div class="userInfo" v-if="item.messageType==2" >[商品]</div>
                <div class="userInfo" v-if="item.messageType==3" >[图片]</div>
                <div class="userInfo" v-if="item.messageType==4" >[订单]</div>
              </div>
              <div style="margin-right: 10px; font-size: 14px; color: #ccc">
                {
  { formatDate(item.createTime) }}
              </div>


            </div>
          </div>
        </div>
        <!-- 右边输入框和信息展示 -->
        <div class="infoList">
          <!-- 信息 -->
          <div class="infoTop" ref="scrollBox" id="box">
            <div  v-for="(item, index) in info" :key="index">
              <!-- 显示时间信息 -->
              <div class="chatInfoRight1 " v-if="shouldShowTime(index)">
                {
  { formatDate1(item.createTime) }}
              </div>
              <div :class="(item.fromUserId == item.userId && item.fromUserType != 1) ?   'chatInfoRight' :'chatInfoLeft' ">
<!--                <img :src="item.avatar" alt="头像" class="head_portrait2" />-->
                <img :src="(item.fromUserId == item.userId && item.fromUserType != 1) ?  require('@/assets/image/avatar.png') : item.avatar" class="head_portrait2" style="margin-left: 20px;" />

                <div :class="(item.fromUserId == item.userId && item.fromUserType != 1) ?  'chatRight' : 'chatLeft'">
                  <!-- 文字 -->
                  <div class="text" v-if="item.messageType==1" >{
  {item.message}}</div>
                  <!-- 商品 -->
                  <div v-if="item.messageType==2" class="text">
                    <!--                  @click="openUrl(`/pages/goodsDetail?id=${parseMessage(item.message).productId}`)"-->
                    <div class="goods1"
                         style="width: 200px;height: 70px;margin: 0 auto;background-color: #FFF;display: flex; ">
                      <image-preview :src="item.message" :width="60" :height="60"/>
                      <div class="right1"
                           style="flex: 1;margin: auto 0;height: 60px;margin-left: 10px;">
                        <div style="color: #333;height: 30px;line-height: 30px;     font-size: 14px; " class="right_title">
                          {
  {parseMessage(item.message).productName}}
                        </div>
                        <div style="height: 30px;color: #ff0000;line-height: 30px;    font-size: 12px;">
                          ¥{
  {parseMessage(item.message).merchantPrice}}</div>
                      </div>
                    </div>
                  </div>
                  <!-- 图片 -->
                  <div v-if="item.messageType==3" class="text">
                    <image-preview :src="item.message" :width="70" :height="70"/>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <!-- 输入框 -->
          <div class="infoBottom">
            <div class="infoIcon">
              <mesImg v-if='isshow==1?true:false'  v-model="imgUrl"/>
<!--              <i @click="extend('发送商品')" class="el-icon-sell"></i>-->
<!--              <i @click="extend('设置')" class="el-icon-setting"></i>-->
<!--              <i @click="extend('聊天记录')" class="el-icon-chat-dot-round"></i>-->
<!--              <i @click="extend('更多选项')" class="el-icon-more-outline"></i>-->
            </div>
            <textarea   maxlength="255"
                      show-word-limit
              type="textarea"
              class="infoInput"
              v-model="textarea"
              @keydown.enter.exact="handlePushKeyword($event)"
              @keyup.ctrl.enter="lineFeed"
              :disabled='isshow==1?false:true'
            />
            <div class="fasong" @click="setUp(1)" v-show="isshow==1?true:false">发送</div>
          </div>
        </div>
      </div>
    </div>
    <!-- 搜索框边 + 号弹框 -->
    <el-dialog
      title="选择需要添加的联系人"
      :visible.sync="dialogVisible"
      width="30%"
      :modal="false"
    >
      <span>自定义页面,还没想好写什么功能</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false"
        >确 定</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>

<script>
import {getMesList,getMesInfo} from "@/api/ums/umsUser";
import axios from 'axios'
export default {
  watch: {
    imgUrl(newVal, oldVal) {
      if (newVal) {
        this.textarea =this.$constants.baseURL + this.imgUrl;
        this.setUp(3);
      }
    },

  },
  data() {
    return {
      socket: null,
      imgUrl: "",
      queryParams:{
        pageNum: 1,
        pageSize: 10,
        userId:this.$store.getters.userId,
        userType:2,
      },
      queryParamsUser:{
        pageNum: 1,
        pageSize: 10,
        userId:this.$store.getters.userId,
        userType:2,
      },
      // 在线状态
      state: 1,
      //搜索用户
      search: "",
      user: "",
      info: [],
      list:[],
      total:0,
      userIdserTotal:0,

      //用户点击选中变色
      act: Number,
      // 加号弹框
      dialogVisible: false,
      //历史信息
      userInfoList: [],
      //输入框
      textarea: "",
      //滚动条距离顶部距离
      scrollTop: 0,
      //滚动条距离顶部距离
      scrollUserTop: 0,
      //发送和输入显隐
      isshow:0
    };
  },
  created() {
    this.socket = new WebSocket('ws://192.168.1.140:9092/front/websocket/2:'+this.$store.getters.userId); // 替换成你的WebSocket服务器地址
    this.socket.onmessage = this.handleMessage;
    this.handleMesList()
      // this.setUserPageScrollTo()
  },
  methods: {
    // 计算是否显示时间信息的函数
    shouldShowTime(index) {
      if (index === 0) {
        return true; // 第一条消息肯定要显示时间信息
      }
      const currentItem = this.info[index];
      const prevItem = this.info[index - 1];
      const currentTime = new Date(currentItem.createTime);
      const prevTime = new Date(prevItem.createTime);
      const timeDiff = currentTime - prevTime; // 计算时间差,单位为毫秒
      const minutesDiff = Math.floor(timeDiff / 1000 / 60); // 转换为分钟
      return minutesDiff >= 3; // 如果时间差大于等于3分钟,则显示时间信息
    },
    // 解析消息字符串为对象
    parseMessage(message) {
      try {
        return JSON.parse(message);
      } catch (error) {
        console.error("Error parsing message:", error);
        return {}; // 返回空对象以避免渲染错误
      }
    },
    handleMessage(event) {
      try {
        const message = JSON.parse(event.data);
        // 判断发的信息是不是当前会话
        if (this.user.opposUserId == message.userId){
          this.getAct(this.user);
        }else {
          this.queryParams.pageNum = 1
          this.handleMesList();
        }
        // 处理收到的消息
        // 例如,将消息添加到相应的聊天记录中
      } catch (error) {
        // console.error('Received message is not in JSON format:', event.data);
      }
    },
    // 左侧列表
    handleMesList(){
      getMesList(this.queryParams).then(response => {
        this.list = response.rows
        this.total = response.total
      });
      // 直接调用不生效:因为你历史数据刚给,渲染的时候盒子高度还没有成型,所以直接调用拿不到,用个定时器让他在下一轮循环中调用,盒子就已经生成了
      this.$nextTick(() => { // 一定要用nextTick
        this.setUserPageScrollTo();
        //页面滚动条距离顶部高度等于这个盒子的高度
        this.$refs.scrollUserBox.scrollUserTop = this.$refs.scrollUserBox.scrollHeight;
      })
    },
    //切换客服状态
    uploadState(state) {
      if (state !== 4) {
        this.state = state;
      } else {
        this.$confirm("是否退出登录?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        })
          .then(() => {
            this.$message({
              type: "success",
              message: "退出成功!",
            });
          })
          .catch(() => {
            this.$message({
              type: "info",
              message: "已取消退出",
            });
          });
      }
    },
    //搜索icon
    handleIconClick() {
      alert("搜索");
      console.log(1);
    },
    //点击用户
    getAct(val) {
      console.log(val,11)
      this.isshow=1
      // 点击用户切换数据时先清除监听滚动事件,防止出现没有历史数据的用户,滚动条为0,会触发滚动事件
      this.$refs.scrollBox.removeEventListener("scroll", this.srTop);
      //点击变色
      this.act = val.opposUserId;
      //清空消息数组
      // this.info = [];
      this.queryParamsUser.toUserId = val.opposUserId
      this.queryParamsUser.pageNum = 1
      getMesInfo(this.queryParamsUser).then(response => {
        this.info = response.rows
        this.userTotal = response.total
        this.queryParams.pageNum = 1
        this.handleMesList()
        // 直接调用不生效:因为你历史数据刚给,渲染的时候盒子高度还没有成型,所以直接调用拿不到,用个定时器让他在下一轮循环中调用,盒子就已经生成了
        this.$nextTick(() => { // 一定要用nextTick
          this.setPageScrollTo();
          //页面滚动条距离顶部高度等于这个盒子的高度
          this.$refs.scrollBox.scrollTop = this.$refs.scrollBox.s

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/511589.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

windows部署Jenkins并远程部署tomcat

目录 1、Jenkins官网下载Jenkins 2、安装Jenkins 3、修改Home directory 4、插件安装及系统配置 5、Tomcat安装及配置 5.1、修改配置文件,屏蔽以下代码 5.2、新增登录用户 5.3、编码格式修改 5.4、启动tomcat 6、Jenkins远程部署war包 6.1、General配置 6.2、Sourc…

构建开源可观测平台

企业始终面临着确保 IT 基础设施和应用程序全年可用的压力。现代架构&#xff08;容器、混合云、SOA、微服务等&#xff09;的复杂性不断增长&#xff0c;产生大量难以管理的日志。我们需要智能应用程序性能管理 (APM) 和可观察性工具来实现卓越生产并满足可用性和正常运行时间…

ddres( ) 组站星双差方程和设计矩阵

1 ddres( )参数介绍 rtklib中进行的单频解算 双差观测值&#xff0c;单差的模糊度 单频点双差 DD (double-differenced) phase/code residuals ------------------------------ x 模糊度 P 方差-协方差阵 sat 共识卫星列表 ns 共识卫星数量 y…

python爬虫———urllibd的基本操作(第十二天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

鸿蒙实战开发-如何使用Stage模型卡片

介绍 本示例展示了Stage模型卡片提供方的创建与使用。 用到了卡片扩展模块接口&#xff0c;ohos.app.form.FormExtensionAbility 。 卡片信息和状态等相关类型和枚举接口&#xff0c;ohos.app.form.formInfo 。 卡片提供方相关接口的能力接口&#xff0c;ohos.app.form.for…

monitor link 联合smart link配合应对复杂的网络

monitor link关键词&#xff1a;上行和下行端口&#xff0c;当上行端口异常&#xff0c;下行端口立即down掉&#xff0c;也就是一种联动机制 如果上行端口里面是smart link方式&#xff0c;则当主从端口都出问题时候&#xff0c;下行端口才会down掉 monitor link 配置步骤 1创…

前端三剑客 —— HTML (下)

目录 HTML 多媒体标签 Img*** a标签*** 第一种用法&#xff1a;超链接 第二种用法&#xff1a;锚点 audio标签 video标签 表格标签 带标题的表格 跨行跨列标签 表格嵌套 列表标签 ul --- 它是无序列表标签 ol --- 它是有序列表 dl --- 它是数据列表 表单标签***…

51单片机学习笔记13 红外遥控接收

51单片机学习笔记13 红外遥控接收 一、红外遥控1. **发射原理**2. **接收原理**3. **发射、接收示例** 二、编码、解码1. **编码方式分类**&#xff1a;&#xff08;1&#xff09;Pulse Distance Modulation (PDM) 脉冲距离调制&#xff1a;&#xff08;2&#xff09;Pulse Wid…

python coding with ChatGPT 专题2| 全解递归算法

文章目录 递归与栈的关系如何思考递归汉诺塔 经典题目入门&#xff1a;斐波那契数列分治法&#xff1a;归并排序树的递归遍历组合问题&#xff1a;子集搜索问题&#xff1a;N皇后 拓展阶乘的迭代法斐波那契数列迭代法青蛙跳 参考文献 掌握递归是解决许多编程问题的关键&#xf…

VBA数据库解决方案第九讲:把数据库的内容在工作表中显示

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…

如何使用极狐GitLab 启用自动备份功能

本文作者&#xff1a;徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了如何极狐GitLab 自…

HarmonyOS 和 OpenHarmony

HarmonyOS 和 OpenHarmony 支持的 shell 命令不同&#xff0c;因此有时候需要做一做区分&#xff0c;目前有些文档上没有标注&#xff0c;因此可能产生歧义。 HarmonyOS 支持 getprop&#xff1a; getprop hw_sc.build.os.apiversion # 查看API版本OpenHarmony 上支持 param…

2024年NAND价格市场继续上涨

TrendForce发布了最新的NAND闪存市场价格走势预测。根据其报告&#xff0c;在2024年第二季度&#xff0c;NAND闪存合同价格将进一步呈现两位数的增长&#xff0c;叠加前一季度的增长。不过&#xff0c;客户端SSD的价格涨幅预计在第二季度将不超过15%&#xff0c;相比于2024年第…

破解密码:掌握2024年的营销归因

Cracking the Code: Mastering Marketing Attribution in 2024 营销归因是识别哪些营销渠道和触及点有助于销售或转化的过程。随着消费者继续通过多个渠道与品牌互动&#xff0c;掌握营销归因对企业来说变得越来越重要。在这篇文章中&#xff0c;我们将探讨破解代码和有效衡量…

PW1503限流芯片:可达3A限流,保障USB电源管理安全高效

在电源管理领域&#xff0c;开关的性能直接关系到设备的稳定性和安全性。今天&#xff0c;我们将详细解析一款备受关注的超低RDS&#xff08;ON&#xff09;开关——PW1503。它不仅具有可编程的电流限制功能&#xff0c;还集成了多项保护机制&#xff0c;为各类电子设备提供了高…

vue两个特性和什么是MVVM

一、什么是vue 1.构建用户界面 用vue往html页面中填充数据&#xff0c;非常的方便 2.框架 框架是一套线成的解决方案 vue的指令、组件&#xff08;是对ui结构的复用&#xff09;、路由、vuex 二、vue的特性 1.数据驱动视图 2.双向数据绑定 1.数据驱动视图 数据的变化会驱动…

基于tensorflow和kereas的孪生网络推理图片相似性

一、环境搭建 基础环境&#xff1a;cuda 11.2 python3.8.13 linux ubuntu18.04 pip install tensorflow-gpu2.11.0 验证&#xff1a;# 查看tensorflow版本 import tensorflow as tf tf.__version__ # 是否能够成功启动GPU from tensorflow.python.client import device_lib pr…

Navicat for MySQL 15免费注册方法

一、效果图如下&#xff1a; 注&#xff1a;此方法仅用于非商业用途&#xff0c;请勿传播&#xff0c;否则后果自负。 二、下载安装 下载安装包&#xff0c;分为32位和6位&#xff0c;下载文件名&#xff1a;Navicat for MySQL 15.zip&#xff08;https://download.csdn.net/…

Prometheus+grafana环境搭建redis(docker+二进制两种方式安装)(四)

由于所有组件写一篇幅过长&#xff0c;所以每个组件分一篇方便查看&#xff0c;前三篇 Prometheusgrafana环境搭建方法及流程两种方式(docker和源码包)(一)-CSDN博客 Prometheusgrafana环境搭建rabbitmq(docker二进制两种方式安装)(二)-CSDN博客 Prometheusgrafana环境搭建m…

Nginx反向代理和缓存

一、Nginx反向代理 1.调度和代理的区别&#xff1a; 1.调度基于内核层面&#xff0c;代理基于应用层面 2.代理必须实现一手托两家 3.调度不需要监听任何端口&#xff0c;不需要工作任何应用程序&#xff0c;代理需要工作和上游服务器一模一样的进程 4.调度没有并发上限&am…