需求:店铺需要跟用户对话
我直接上代码
<template>
<div class="message-detail message-detail-list">
<section class="chat-section">
<a-card class="chat-card">
<div class="chat-title">
<div>
<p class="te-im">
{{ queryParams.userName }}
<img src="@/assets/userImg/info-blue.svg" />
p>
<p>{{ queryParams.recruitTime }} {{ queryParams.title }}p>
div>
<a-button @click="toUserDetail(queryParams)">{{ $t('evaluation.detail') }}a-button>
div>
<a-card class="chat-section chat-card1">
<div id="chat-box1" ref="msg-box" class="box">
<div v-for="(item, index) in listData" :key="index">
<div class="chart-timer">{{ item.createTime }}div>
//recruit_app判断用户发过来的信息,展示在左侧
<div v-show="item.businessType === 'recruit_app'" class="item chart-left">
<div v-if="!imgUrl" class="default-avatar">
<svg-icon icon-class="store" />
div>
<img class="header-img" :src="imgUrl" />
<span class="message">{{ item.message }}span>
div>
//recruit_enterprise判断客服(自己发过去)发过来的信息,展示在右侧侧
<div v-show="item.businessType == 'recruit_enterprise'" class="item chart-right">
<div v-if="!user.avatar" class="default-avatar">
<svg-icon icon-class="store" />
div>
<img v-else class="header-img" :src="user.avatar" />
<span class="message">{{ item.message }}span>
div>
div>
div>
a-card>
<div class="input-box">
<a-input
ref="sendMsg"
v-model="contentText"
type="text"
:placeholder="$t('evaluation.pleaseFillIn')"
@keyup.enter.native="sendText"
/>
<a-button v-if="contentText" class="btn" @click="sendText">
<img src="@/assets/userImg/send-wirte.svg" />
{{ $t('evaluation.send') }}
a-button>
<a-button v-else class="btn1">
<img src="@/assets/userImg/send-wirte.svg" />
{{ $t('evaluation.send') }}
a-button>
div>
a-card>
section>
div>
template>
import { mapState } from 'vuex';
import { getToken } from '@/utils/auth';
import { listHistory } from '@/api/manage/message';
export default {
name: 'MessageDetail',
data() {
return {
queryParams: {},
// table/搜索框
tableLoading: false,
// 分页数据(默认第一页):
pageSizeOptions: ['10', '20', '30', '40', '50'],
current: 1,
pageSize: 10,
total: 0,
socket: '',
ws: null,
contentText: '',
// 历史分页参数
historyParams: {
pageSize: 10, // 加载多少页到多少页的数据
pageNum: 1, // 加载第几页的数据
},
oppositeData: {},
weData: {},
listData: [
// { 'appUserId': 1, 'appUserName': '国航', 'message': '服务器消息', 'recruitId': 22, 'type': 'server' },
// { 'appUserId': 1, 'appUserName': '国航', 'message': '服务器消1息31232', 'recruitId': 22 },
], // 聊天记录的数组
isShow: false,
isShow1: false,
chatList: [],
isFirst: 1,
lockReconnect: true, // 避免重复连接
imgUrl: null,
page: 0,
};
},
computed: {
...mapState(['settings', 'user', 'mode']),
},
created() {
this.queryParams = JSON.parse(localStorage.getItem('queryParamsMessage'));
this.imgUrl = this.queryParams.imgUrl;
this.getListHistory();
// 初始化
this.$nextTick(() => {
this.initWebSocket();
});
},
destroyed() {
// 销毁监听
window.removeEventListener('mousewheel', this.onScroll, false);
},
mounted() {
this.$nextTick(() => {
window.addEventListener('mousewheel', this.onScroll, true);
});
},
methods: {
getListHistory() {
const params = {
id: this.queryParams.id,
userId: this.queryParams.userId,
isFirst: this.isFirst,
recruitId: this.queryParams.recruitId,
page: this.page,
// ...this.historyParams,
};
listHistory(params).then((res) => {
if (res.code === 200) {
this.$store.dispatch('SetUnread');
if (res.data.length < 1) {
if (this.page === -1) {
this.isShow = true;
} else {
this.isShow1 = true;
}
} else {
this.queryParams.id = res.data[0].id;
}
this.listData = [...this.listData, ...res.data];
this.listData = this.listData.sort(function (a, b) {
return a.createTime > b.createTime ? 1 : -1;
});
}
});
},
// 获取滚动条当前的位置
getScrollTop() {
var scrollTop = 0;
// const dom = document.getElementById('chat-box1');
if (document.documentElement && document.documentElement.scrollTop) {
scrollTop = document.documentElement.scrollTop;
} else if (document.body) {
scrollTop = document.body.scrollTop;
}
return scrollTop;
},
// 获取当前可视范围的高度
getClientHeight() {
var clientHeight = 0;
if (document.body.clientHeight && document.documentElement.clientHeight) {
clientHeight = Math.min(document.body.clientHeight, document.documentElement.clientHeight);
} else {
clientHeight = Math.max(document.body.clientHeight, document.documentElement.clientHeight);
}
return clientHeight;
},
// 获取文档完整的高度
getScrollHeight() {
return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
},
// 滚动事件触发下拉加载
onScroll(e) {
const direction = e.deltaY > 0 ? 'down' : 'up'; // 该语句可以用来判断滚轮是向上滑动还是向下
if (direction === 'up') {
this.page = -1;
// console.log('isShow:' + this.isShow);
if (!this.isShow) {
this.$nextTick(() => {
this.getListHistory();
});
}
} else {
this.page = 1;
// console.log('isShow1:' + this.isShow1);
if (!this.isShow1) {
this.$nextTick(() => {
this.getListHistory();
});
}
}
if (this.getScrollHeight() - this.getClientHeight() - this.getScrollTop() <= 0) {
// if (this.status === 1) {
// this.status = 0;
// 页码,分页用,默认第一页
// this.deliverParams.page += 1;
// 调用请求函数
// alert('触发!!!');
// }
}
},
/** pageSize 变化的回调 */
onShowSizeChange(current, pageSize) {
this.pageSize = pageSize;
this.current = current;
this.getList();
},
/** 页码改变的回调 */
currentPageChange(current, pageSize) {
this.pageSize = pageSize;
this.current = current;
this.getList();
},
toDetailMessage() {
this.$router.push({ path: '/message-detail/detail' });
},
returnClick() {
this.$router.go(-1);
},
toScan() {
this.$router.push({ path: '/attendance' });
},
toAddUser() {
this.$router.push({ path: '/recruit-temp/index' });
},
toUserDetail(record) {
this.$router.push({ path: '/recruit/detail', query: { id: record.recruitId }});
},
// 滚动条到底部
scrollBottom() {
const el = this.$refs['msg-box'];
el.scrollTop = el.scrollHeight;
},
// 发送聊天信息
sendText() {
const _this = this;
_this.$refs['sendMsg'].focus();
if (!_this.contentText) {
return;
}
const param = this.queryParams;
this.chatList = {
appUserId: param.userId,
appUserName: param.userName,
recruitId: param.recruitId,
message: _this.contentText,
};
if (_this.ws.readyState === 1) {
_this.ws.send(JSON.stringify(this.chatList)); // 调用WebSocket send()发送信息的方法
this.listData.push({ ...this.chatList, businessType: 'recruit_enterprise' });
}
_this.contentText = '';
setTimeout(() => {
_this.scrollBottom();
}, 500);
},
destroyed() {
// 销毁监听
// this.over();
this.ws.close();
},
// 进入页面创建websocket连接
initWebSocket() {
const _this = this;
const NODE_ENV = process.env.NODE_ENV;
let ws = null;
// 判断页面有没有存在websocket连接
if (window.WebSocket) {
if (NODE_ENV === 'development') {
// 此处的 :8181 端口号 要与后端配置的一致
ws = new WebSocket('ws://13.230.192.185:81/msg;' + getToken());
} else if (NODE_ENV === 'production') {
ws = new WebSocket('ws://13.230.192.185:81/msg;' + getToken());
} else {
ws = new WebSocket('ws://13.230.192.185:81/msg;' + getToken());
}
_this.ws = ws;
ws.onopen = function (e) {
console.log('服务器连接成功');
};
ws.onclose = function (e) {
console.log('服务器连接关闭');
};
ws.onerror = function (e) {
if (_this.lockReconnect) {
setTimeout(() => {
_this.initWebSocket();
_this.$message.error(_this.$t('evaluation.again'));
}, 5000);
}
console.log('服务器连接出错');
};
ws.onmessage = function (e) {
// 接收服务器返回的数据
if (e.data.indexOf('{') !== -1) {
// 接收对方数据
const data = JSON.parse(e.data);
if (_this.queryParams.recruitId === data.recruitId) {
_this.listData.push({ ...data, businessType: 'recruit_app' });
}
}
};
_this.$router.afterEach(function (e) {
if (e.name === 'DetailMessage') {
_this.lockReconnect = true;
} else {
_this.lockReconnect = false;
}
});
}
},
},
<style lang="scss" scoped>
.chat-load {
display: flex;
align-items: center;
justify-content: center;
color: #409eff;
margin: -11px 0 20px 0;
}
.message-detail {
padding: 10px 48px;
max-width: 1200px;
.chat-card1 {
background: #f5f5f5ff;
border-radius: 10px;
height: 600px;
overflow: auto;
}
.chat-section {
margin-top: 40px;
padding: 20px 20px 60px 20px;
.chat-card {
border-radius: 10px;
padding: 20px 20px 60px 20px;
.chat-title {
display: flex;
justify-content: space-between;
align-items: center;
.te-im {
display: flex;
align-items: center;
img {
width: 16px;
margin-left: 10px;
}
}
}
}
/*
聊天item采用flex布局
*/
.item {
display: flex;
margin-bottom: 20px;
}
.chart-left {
flex-direction: row;
.message {
border-radius: 0 10px 10px 10px;
background: #fff;
align-items: center;
color: #333333ff;
margin-left: 10px;
}
}
.chart-right {
flex-direction: row-reverse;
.message {
border-radius: 10px 0 10px 10px;
background: #26d4e0ff;
color: #fff;
margin-right: 10px;
}
}
.header-img {
width: 42px;
height: 42px;
border-radius: 100px;
}
.message {
display: flex;
min-height: 25px;
padding: 9px 10px;
align-items: center;
word-break: break-all;
max-width: 41%;
}
.input-box {
position: absolute;
bottom: 0px;
left: 0;
right: 0;
display: flex;
padding: 15px 40px;
box-sizing: border-box;
}
.input-box input {
flex: 1;
border-radius: 3px;
border: 1px #cecece solid;
padding: 20px;
outline: none;
}
.btn {
background: #26d4e0ff;
}
.btn1 {
background: #ccccccff;
}
.input-box button {
width: 80px;
border-radius: 3px;
border: 1px #fffa solid;
color: #ffffff;
margin: 0px 6px;
outline: none;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
img {
width: 14.71px;
margin-right: 5px;
}
}
button:active {
}
.chart-timer {
text-align: center;
color: #616161;
font-size: 13px;
}
}
}
.chat-box {
margin: 0 auto;
background: #fafafa;
position: absolute;
height: 100%;
width: 100%;
max-width: 700px;
header {
position: fixed;
width: 100%;
height: 3rem;
background: #409eff;
max-width: 700px;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
color: white;
font-size: 1rem;
}
.msg-box {
position: absolute;
height: calc(100% - 6.5rem);
width: 100%;
margin-top: 3rem;
overflow-y: scroll;
.msg {
width: 95%;
min-height: 2.5rem;
margin: 1rem 0.5rem;
position: relative;
display: flex;
justify-content: flex-start !important;
.user-head {
min-width: 2.5rem;
width: 20%;
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
background: #f1f1f1;
display: flex;
justify-content: center;
align-items: center;
.head {
width: 1.2rem;
height: 1.2rem;
}
// position: absolute;
}
.user-msg {
width: 80%;
// position: absolute;
word-break: break-all;
position: relative;
z-index: 5;
span {
display: inline-block;
padding: 0.5rem 0.7rem;
border-radius: 0.5rem;
margin-top: 0.2rem;
font-size: 0.88rem;
}
.left {
background: white;
animation: toLeft 0.5s ease both 1;
}
.right {
background: #53a8ff;
color: white;
animation: toright 0.5s ease both 1;
}
@keyframes toLeft {
0% {
opacity: 0;
transform: translateX(-10px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
@keyframes toright {
0% {
opacity: 0;
transform: translateX(10px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
}
}
}
.input-box {
padding: 0 0.5rem;
position: absolute;
bottom: 0;
width: 100%;
height: 3.5rem;
background: #fafafa;
box-shadow: 0 0 5px #ccc;
display: flex;
justify-content: space-between;
align-items: center;
input {
height: 2.3rem;
display: inline-block;
width: 100%;
padding: 0.5rem;
border: none;
border-radius: 0.2rem;
font-size: 0.88rem;
}
.btn {
height: 2.3rem;
min-width: 4rem;
background: #e0e0e0;
padding: 0.5rem;
font-size: 0.88rem;
color: white;
text-align: center;
border-radius: 0.2rem;
margin-left: 0.5rem;
transition: 0.5s;
}
.btn-active {
background: #409eff;
}
}
}
.default-avatar {
display: inline-flex;
align-items: center;
justify-content: center;
width: 42px;
height: 42px;
background-color: #fff;
color: #999999;
border-radius: 50%;
font-size: 50px;
padding: 6px;
}
style>