vue 加 websocket 聊天

news2024/11/23 21:48:19

<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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《数据结构学习笔记---第九篇》---循环队列的实现

文章目录 1.循环队列的定义 2.循环队列的判空判满 3.创建队列并初始化 4.入队和出队 5. 返回队尾队首元素 6.释放循环队列 1.循环队列的定义 定义&#xff1a;存储队列元素的表从逻辑上被视为一个环。 我们此次实现的循环队列&#xff0c;采用顺序表 typedef struct {int…

Scikit-Learn K均值聚类

Scikit-Learn K均值聚类 1、K均值聚类1.1、K均值聚类及原理1.2、K均值聚类的优缺点 2、Scikit-Learn K均值聚类2.1、Scikit-Learn K均值聚类API2.2、K均值聚类案例 1、K均值聚类 K-均值&#xff08;K-Means&#xff09;是一种聚类算法&#xff0c;属于无监督学习。K-Means在机器…

Linux学习笔记————C 语言版 LED 灯实验

这里写目录标题 一、实验程序编写二、 汇编部分实验程序编写三、C 语言部分实验程序编写四、编译下载验证 汇编 LED 灯实验中&#xff0c;我们讲解了如何使用汇编来编写 LED 灯驱动&#xff0c;实际工作中是很少用到汇编去写嵌入式驱动的&#xff0c;毕竟汇编太难&#xff0c;而…

鸿蒙原OS开发实例:【ArkTS类库单次I/O任务开发】

Promise和async/await提供异步并发能力&#xff0c;适用于单次I/O任务的场景开发&#xff0c;本文以使用异步进行单次文件写入为例来提供指导。 实现单次I/O任务逻辑。 import fs from ohos.file.fs; import common from ohos.app.ability.common;async function write(data:…

【MySQL】多表查询全解-【多表关系/内外自连接/子查询/多表查询案例链接】(可cv代码&案例演示)

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

深入理解 Vue3 中使用 v-model技术实现双向数据绑定

引言 在 Vue3 中&#xff0c;v-model 是一个非常有用的指令&#xff0c;它提供了一种简洁的方式来实现组件之间的双向数据绑定。本文将深入探讨 Vue3 中的 v-model 技术&#xff0c;包括它的工作原理、使用场景以及如何在自定义组件中应用 v-model。 一、v-model 的工作原理 在…

STM32 M3内核寄存器概念

内容主要来自<<M3内核权威指南>> 汇编程序中的最低有效位&#xff08;Least Significant Bit&#xff09;。LSB是二进制数中最右边的位&#xff0c;它代表了数值中的最小单位。在汇编程序中&#xff0c;LSB通常用于表示数据的最小精度或者作为标志位。 ---------…

基于MiniExcel的三种常用导出Excel方法(固定列导出、动态列导出、按模板导出)

为了方便代码编写和测试&#xff0c;把很多代码都放在一个class里面&#xff0c;实际开发根据需要放到对应的目录下即可。 1.使用nuget下载安装miniexcel&#xff1b; 2.编写对应的测试接口&#xff0c;具体代码如下: using Microsoft.AspNetCore.Authorization; using Micr…

sql中如何添加数据?

添加 在 SQL 中添加数据通常使用INSERT语句。INSERT语句用于将新的数据行插入到数据库表中。 基本的INSERT语句语法如下&#xff1a; INSERT INTO table_name (column1, column2,...) VALUES (value1, value2,...);其中&#xff1a; INSERT INTO&#xff1a;指定要插入数据…

探索设计模式的魅力:AI大模型如何赋能C/S模式,开创服务新纪元

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 AI大模型如何赋能C/S模式&#xff0c;开创服务新纪元 数字化飞速发展的时代&#xff0c;AI大模型…

从第三方数据集成工具迁移到Apache SeaTunnel的实操经验分享

在如今这个数据至上的商业环境中&#xff0c;构建一个高效、可靠的数据仓库对企业来说不仅是一项基础性工作&#xff0c;更是推动业务洞察、决策支持和创新的关键&#xff0c;而数据集成技术在此发挥着至关重要的作用&#xff0c;其时效性和准确性直接影响着下游业务的效率和产…

连接Redis不支持集群错误,ERR This instance has cluster support disabled,解决方案

1. 问题背景 调整redis的配置后&#xff0c;启动程序时&#xff0c; 会报如下错误&#xff1a; [redis://172.16.0.8xxx]: ERR This instance has cluster support disabledSuppressed: io.lettuce.core.RedisCommandExecutionException: ERR This instance has cluster supp…

电商技术揭秘四:电商平台的物流管理系统

文章目录 引言一、物流管理系统的功能与架构1.1 物流管理系统在电商平台中的作用概述保障订单的及时配送优化库存管理控制运营成本提升客户服务水平支持数据驱动的决策应对市场变化 1.2 订单处理功能分析自动化处理流程订单分配与履行错误检测与处理机制实时订单状态更新订单数…

群晖配置FTP服务结合内网穿透实现公网访问本地NAS中储存文件

文章目录 1. 群晖安装Cpolar2. 创建FTP公网地址3. 开启群晖FTP服务4. 群晖FTP远程连接5. 固定FTP公网地址6. 固定FTP地址连接 本文主要介绍如何在群晖NAS中开启FTP服务并结合cpolar内网穿透工具&#xff0c;实现使用固定公网地址远程访问群晖FTP服务实现文件上传下载。 Cpolar内…

微信小程序开发学习笔记——4.7 api中navigate路由接口与组件的关系

>>跟着b站up主“咸虾米_”学习微信小程序开发中&#xff0c;把学习记录存到这方便后续查找。 一、跳转 1、方法一&#xff1a;组件 组件-导航-navigator <navigator url"/pages/demo/demo?id123" open-type"reLaunch">go demo page <…

windows搭建ftp实现局域网共享文件

一、开启ftp服务 1.使用 win Q 键&#xff0c;快捷打开搜索框 2.搜索框内搜索 “控制面板” 3. 进入控制面板内选择 ”程序“ 4. 单击进入 “启用或关闭windows功能” 5. 找到并展开“internet information services”、 6. 建议展开后全选 “FTP服务器” 和 “web管理工…

Mysql的MHA高可用集群部署及故障切换

目录 一、MHA概念 1、MHA的组成 2、MHA的特点 二、搭建MysqlMHA 1、实验思路 1.1 MHA架构 1.2 故障模拟 2、实验环境、安装包 3、服务搭建 3.1 所有服务器&#xff0c;关闭系统防火墙和安全机制 3.2 修改master &#xff08;192.168.170.111&#xff09;、slave1&am…

ATFX汇市:美国3月ISM制造业PMI为50.3,远高于前值47.8,经济景气度高

ATFX汇市&#xff1a;虽然国际市场的主流预期是美联储将于今年六月份开启首次降息&#xff0c;但降息的前提条件——美国经济显著衰退——似乎远未到来。此前公布的美国非农就业报告显示&#xff0c;新增非农就业人口27.5万人&#xff0c;处于近12个月的数据峰值水平&#xff1…

Modbus转Profinet网关解决主从设备间通信数据丢失难题

在接到现场关于Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;配置时出现信不稳定或数据丢失的问题的反馈后。对于现场反馈的Modbus转Profinet网关配置问题&#xff0c;特出专项答疑。 解决Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;通信不稳定或数据丢…

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

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