<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