1
This commit is contained in:
parent
0a793caf80
commit
a33b1bff7e
@ -80,4 +80,8 @@ public class BusiChatMain extends DlBaseEntity
|
||||
/** 未读消息数量 */
|
||||
@TableField(exist = false)
|
||||
private Integer unreadCount;
|
||||
|
||||
/** 产品名称 */
|
||||
@TableField(exist = false)
|
||||
private String prodName;
|
||||
}
|
||||
|
@ -118,6 +118,8 @@ public class BusiChatMainServiceImpl extends ServiceImpl<BusiChatMainMapper,Busi
|
||||
for (BusiChatMain session : sessions) {
|
||||
session.setUnreadCount(busiChatItemService.selectUnreadCount(session.getId(),DATA_FROM_CUSTOMER));
|
||||
}
|
||||
//对会话未读消息数量进行倒叙排列
|
||||
sessions.sort((o1, o2) -> o2.getUnreadCount() - o1.getUnreadCount());
|
||||
return sessions;
|
||||
}
|
||||
|
||||
|
@ -5,15 +5,15 @@ spring:
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
# 主库数据源-点亮开发库
|
||||
# master:
|
||||
# url: jdbc:mysql://82.156.161.160:3306/dl_site_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: site
|
||||
# password: 123456
|
||||
#主库数据源-客户测试服务器
|
||||
master:
|
||||
url: jdbc:mysql://127.0.0.1:3306/dl_site_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
url: jdbc:mysql://82.156.161.160:3306/dl_site_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: site
|
||||
password: 123456
|
||||
#主库数据源-客户测试服务器
|
||||
# master:
|
||||
# url: jdbc:mysql://127.0.0.1:3306/dl_site_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: site
|
||||
# password: 123456
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
|
@ -134,13 +134,15 @@
|
||||
</select>
|
||||
<select id="selectByServiceId" resultType="com.ruoyi.busi.domain.BusiChatMain">
|
||||
SELECT
|
||||
*
|
||||
dbcm.*,
|
||||
dbpn.title AS prodName
|
||||
FROM
|
||||
dl_busi_chat_main
|
||||
dl_busi_chat_main dbcm
|
||||
LEFT JOIN dl_busi_prod_new dbpn ON dbcm.prod_id = dbpn.id
|
||||
WHERE
|
||||
user_id = #{serviceId}
|
||||
AND `status` = 1
|
||||
AND del_flag = '0'
|
||||
dbcm.user_id =#{serviceId}
|
||||
AND dbcm.`status` = 1
|
||||
AND dbcm.del_flag = '0'
|
||||
</select>
|
||||
<select id="selectActiveSession" resultType="com.ruoyi.busi.domain.BusiChatMain">
|
||||
SELECT
|
||||
|
@ -14,4 +14,4 @@ VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||
VUE_APP_WEBSOCKET = 'ws://localhost:8099/ws/asset/'
|
||||
|
||||
# 产品、文章预览
|
||||
VUE_APP_PREVIEW = 'http://www.lighting-it.cn/admin-preview/'
|
||||
VUE_APP_PREVIEW = 'http://192.168.1.13:3001/admin-preview/'
|
||||
|
@ -5,10 +5,10 @@ VUE_APP_TITLE = 成事达管理平台
|
||||
ENV = 'production'
|
||||
|
||||
# 成事达管理平台/生产环境
|
||||
VUE_APP_BASE_API = 'http://114.132.197.85:8099'
|
||||
VUE_APP_BASE_API = 'http://1.92.99.15:8099'
|
||||
|
||||
# websocket
|
||||
VUE_APP_WEBSOCKET = 'ws://114.132.197.85:8099/ws/asset/'
|
||||
VUE_APP_WEBSOCKET = 'ws://1.92.99.15:8099/ws/asset/'
|
||||
|
||||
# 产品、文章预览
|
||||
VUE_APP_PREVIEW = 'http://www.lighting-it.cn/admin-preview/'
|
||||
|
@ -1,39 +1,57 @@
|
||||
<template>
|
||||
<!-- 选择产品对话框 -->
|
||||
<el-dialog @close="close" :title="title" :visible.sync="open" width="800px" append-to-body>
|
||||
<div class="dl-chat-box" >
|
||||
<template v-for="(item,index) in messages">
|
||||
<div v-if="item.dataFrom=='customer'" class="dl-customer-dom">
|
||||
<div class="dl-customer-photo">
|
||||
<img src="@/assets/images/customer.jpg" >
|
||||
</div>
|
||||
<div class="dl-customer-right">
|
||||
<div class="dl-customer-time">{{item.createTime}}</div>
|
||||
<div class="dl-customer-content">{{ item.content }}</div>
|
||||
<el-dialog @close="close" :title="title" :visible.sync="open" width="800px" append-to-body class="customer-service-dialog">
|
||||
<!-- 自定义标题栏 -->
|
||||
<div slot="header" class="dialog-header">
|
||||
<div class="service-info">
|
||||
<h3 class="service-name">{{ title }}</h3>
|
||||
</div>
|
||||
<button class="close-btn" @click="close" aria-label="关闭对话框">×</button>
|
||||
</div>
|
||||
<div class="dl-prod-box" @click="goProdDetail">咨询产品:<span>{{session.prodName}}</span></div>
|
||||
<!-- 聊天内容区域 -->
|
||||
<div class="chat-container">
|
||||
<!-- 消息列表(内部滚动) -->
|
||||
<div class="message-list-wrapper">
|
||||
<div class="message-list" ref="messageList">
|
||||
<!-- 消息项 -->
|
||||
<div
|
||||
v-for="(msg, index) in messages"
|
||||
:key="index"
|
||||
:class="['message-item', msg.dataFrom=='platform' ? 'self-message' :'other-message']"
|
||||
>
|
||||
<div class="message-content">
|
||||
<div class="message-text">{{ msg.content }}</div>
|
||||
<div class="message-time">{{ formatTime(msg.createTime) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.dataFrom=='platform'" class="dl-platform-dom">
|
||||
</div>
|
||||
|
||||
<div class="dl-platform-right">
|
||||
<div class="dl-platform-time">{{item.createTime}}</div>
|
||||
<div class="dl-platform-content">{{ item.content }}</div>
|
||||
</div>
|
||||
<div class="dl-platform-photo">
|
||||
<img src="@/assets/images/customer.jpg" >
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 输入区域 -->
|
||||
<div class="input-area">
|
||||
<textarea
|
||||
v-model="text"
|
||||
class="message-input"
|
||||
placeholder="按Enter发送"
|
||||
@keydown.enter="sendToServer"
|
||||
:disabled="isInputDisabled || readOnly"
|
||||
></textarea>
|
||||
<el-button type="primary"
|
||||
icon="el-icon-s-promotion"
|
||||
@click="sendToServer"
|
||||
:disabled="!text.trim() || isInputDisabled || readOnly"
|
||||
>
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary"
|
||||
:disabled="readOnly"
|
||||
@click="closeCurrentSession"
|
||||
>结束会话
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-input type="textarea"
|
||||
class="inputT"
|
||||
placeholder="按 Enter 发送" v-model="text"
|
||||
@keyup.enter.native="sendToServer"
|
||||
></el-input>
|
||||
<el-button type="primary" icon="el-icon-s-promotion" @click="sendToServer"></el-button>
|
||||
<el-button type="warning" @click="closeCurrentSession">结束会话</el-button>
|
||||
</div>
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
@ -44,6 +62,9 @@ export default {
|
||||
name: 'chatForm',
|
||||
data() {
|
||||
return {
|
||||
//只读
|
||||
readOnly:false,
|
||||
isInputDisabled: false,
|
||||
picPrex:process.env.VUE_APP_BASE_API,
|
||||
// 弹出层标题
|
||||
title: "聊天记录",
|
||||
@ -53,7 +74,9 @@ export default {
|
||||
messages: [],
|
||||
//发送的消息
|
||||
text:'',
|
||||
session: null,
|
||||
session: {
|
||||
prodName:""
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -86,9 +109,9 @@ export default {
|
||||
scrollBottom(){
|
||||
// 确保在对话框打开后滚动到底部
|
||||
this.$nextTick(() => {
|
||||
const chatBox = this.$el.querySelector('.dl-chat-box');
|
||||
if (chatBox) {
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
const messageList = this.$refs.messageList;
|
||||
if (messageList) {
|
||||
messageList.scrollTop = messageList.scrollHeight;
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -131,105 +154,369 @@ export default {
|
||||
},
|
||||
//来新消息
|
||||
addNewMsg(msg){
|
||||
this.messages.push(msg)
|
||||
this.scrollBottom()
|
||||
console.log(msg,"msg")
|
||||
// this.messages.push(msg)
|
||||
setTimeout(()=>{
|
||||
this.scrollBottom()
|
||||
},0.5)
|
||||
},
|
||||
close(){
|
||||
this.open = false;
|
||||
//回调父组件方法
|
||||
this.$emit("closeForm")
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 格式化时间显示
|
||||
* @param {string|number} time - 时间戳或时间字符串
|
||||
* @returns {string} 格式化后的时间字符串 HH:mm
|
||||
*/
|
||||
formatTime(time) {
|
||||
if (!time) return '';
|
||||
|
||||
try {
|
||||
const date = new Date(time);
|
||||
if (isNaN(date.getTime())) return ''; // 检查日期是否有效
|
||||
|
||||
return `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||
} catch (error) {
|
||||
console.error('时间格式化错误:', error);
|
||||
return '';
|
||||
}
|
||||
},
|
||||
goProdDetail(){
|
||||
this.$router.push({path:'/product/prodForm',query:{id:this.session.prodId}})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dl-chat-box{
|
||||
width: 100%;
|
||||
background: #F1F3F7;
|
||||
max-height:500px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
justify-content: start;
|
||||
overflow-y: scroll;
|
||||
padding: 10px 20px;
|
||||
/deep/.el-dialog__body{
|
||||
padding: 10px 20px !important;
|
||||
}
|
||||
.dl-customer-dom{
|
||||
padding: 15px 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
.customer-service-dialog {
|
||||
--primary-color: #409eff;
|
||||
--primary-light: #e8f3ff;
|
||||
--primary-dark: #337ecc;
|
||||
--bg-color: #f7f9fc;
|
||||
--text-color: #303133;
|
||||
--text-light: #909399;
|
||||
--border-radius: 12px;
|
||||
--shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.dl-customer-photo{
|
||||
width: 60px;
|
||||
/* 标题栏样式 */
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.dl-customer-photo img{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.dl-customer-right{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
justify-content: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.dl-customer-time{
|
||||
font-size: 10px;
|
||||
color: #878B90;
|
||||
}
|
||||
.dl-customer-content{
|
||||
margin-top: 10px;
|
||||
width: auto;
|
||||
background-color: white;
|
||||
color: black;
|
||||
border-radius: 5px;
|
||||
padding: 12px;
|
||||
.service-info {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.dl-platform-dom{
|
||||
padding: 15px 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
.service-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dl-platform-photo{
|
||||
.service-status {
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 60px;
|
||||
}
|
||||
.dl-platform-photo img{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.dl-platform-right{
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: end;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.dl-platform-time{
|
||||
font-size: 10px;
|
||||
color: #878B90;
|
||||
.online-indicator {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: #52c41a;
|
||||
box-shadow: 0 0 0 3px rgba(82, 196, 26, 0.15);
|
||||
}
|
||||
.dl-platform-content{
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
color: var(--text-light);
|
||||
cursor: pointer;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
.dl-prod-box{
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.dl-prod-box span{
|
||||
color: rgb(64, 158, 255);
|
||||
cursor: pointer;
|
||||
}
|
||||
/* 聊天容器 */
|
||||
.chat-container {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 550px;
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
/* 消息列表容器 */
|
||||
.message-list-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 消息列表(内部滚动) */
|
||||
.message-list {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
scroll-behavior: smooth;
|
||||
background-color: var(--bg-color);
|
||||
background-image:
|
||||
radial-gradient(var(--primary-color) 0.5px, transparent 0.5px),
|
||||
radial-gradient(var(--primary-color) 0.5px, var(--bg-color) 0.5px);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 10px 10px;
|
||||
background-attachment: local;
|
||||
}
|
||||
|
||||
/* 自定义滚动条 */
|
||||
.message-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.message-list::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.message-list::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(150, 150, 150, 0.3);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.message-list::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(150, 150, 150, 0.5);
|
||||
}
|
||||
|
||||
/* 系统消息 */
|
||||
.system-message {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
background-color: rgba(255, 255, 255, 0.85);
|
||||
padding: 6px 14px;
|
||||
border-radius: 14px;
|
||||
margin: 0 auto 20px;
|
||||
display: inline-block;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
/* 消息项 */
|
||||
.message-item {
|
||||
margin-bottom: 18px;
|
||||
max-width: 70%;
|
||||
display: inline-block;
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.self-message {
|
||||
float: right;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.other-message {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--border-radius);
|
||||
line-height: 1.6;
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
/* 消息气泡样式 */
|
||||
.self-message .message-text {
|
||||
background-color: var(--primary-color);
|
||||
color: #fff;
|
||||
border-top-right-radius: 4px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.self-message .message-text:hover {
|
||||
background-color: var(--primary-dark);
|
||||
}
|
||||
|
||||
.other-message .message-text {
|
||||
background-color: #fff;
|
||||
color: var(--text-color);
|
||||
border-top-left-radius: 4px;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.other-message .message-text:hover {
|
||||
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* 消息时间 */
|
||||
.message-time {
|
||||
font-size: 11px;
|
||||
color: var(--text-light);
|
||||
margin-top: 5px;
|
||||
text-align: right;
|
||||
margin-top: 10px;
|
||||
width: auto;
|
||||
background-color: white;
|
||||
color: black;
|
||||
border-radius: 5px;
|
||||
padding: 12px;
|
||||
white-space: nowrap;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.message-item:hover .message-time {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.other-message .message-time {
|
||||
text-align: left;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
/* 正在输入指示器 */
|
||||
.typing-indicator {
|
||||
margin-bottom: 18px;
|
||||
max-width: 70%;
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.typing-dots {
|
||||
background-color: #fff;
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--border-radius);
|
||||
border-top-left-radius: 4px;
|
||||
box-shadow: var(--shadow);
|
||||
display: inline-flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.typing-dots span {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--text-light);
|
||||
border-radius: 50%;
|
||||
animation: typing 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.typing-dots span:nth-child(1) { animation-delay: -0.32s; }
|
||||
.typing-dots span:nth-child(2) { animation-delay: -0.16s; }
|
||||
|
||||
/* 输入区域 */
|
||||
.input-area {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.message-input {
|
||||
flex: 1;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: var(--border-radius);
|
||||
padding: 12px 15px;
|
||||
min-height: 42px;
|
||||
max-height: 120px;
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
transition: all 0.2s;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.message-input:focus {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px var(--primary-light);
|
||||
}
|
||||
|
||||
.message-input:disabled {
|
||||
background-color: #f5f5f5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 发送按钮 */
|
||||
.send-button {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.send-button:hover {
|
||||
background-color: var(--primary-dark);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.send-button:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.send-button:disabled {
|
||||
background-color: #b3d8ff;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes typing {
|
||||
0% { transform: scale(0); }
|
||||
40% { transform: scale(1); }
|
||||
80% { transform: scale(0); }
|
||||
100% { transform: scale(0); }
|
||||
}
|
||||
</style>
|
||||
|
@ -1,33 +1,37 @@
|
||||
<template>
|
||||
<div class="session-list">
|
||||
<div class="search-box">
|
||||
<el-input placeholder="搜索会话..." v-model="searchKeyword" clearable size="small"></el-input>
|
||||
<div class="toggle-button" @click="toggleCollapse" :title="isCollapsed?'点击展开':'点击收起'">
|
||||
<i :class="isCollapsed ? 'el-icon-d-arrow-left' : 'el-icon-d-arrow-right'"></i>
|
||||
</div>
|
||||
<div class="session-item"
|
||||
v-for="session in filteredSessions"
|
||||
:key="session.id"
|
||||
:class="{ 'active': currentSessionId === session.id, 'unread': session.unreadCount > 0 }"
|
||||
@click="switchSession(session)"
|
||||
>
|
||||
<div class="avatar">
|
||||
<img :src="session.userAvatar || '/img/user.png'" alt="用户头像">
|
||||
<div v-show="!isCollapsed" class="session-content">
|
||||
<div class="search-box">
|
||||
<el-input placeholder="搜索会话..." v-model="searchKeyword" clearable size="small"></el-input>
|
||||
</div>
|
||||
<div class="session-info">
|
||||
<div class="session-header">
|
||||
<span class="user-name">{{ session.userName || '匿名用户' }}</span>
|
||||
<span class="time">{{ formatTime(session.lastTime) }}</span>
|
||||
</div>
|
||||
<div class="last-message">
|
||||
<span>{{ session.lastMessage || '暂无消息' }}</span>
|
||||
<span class="unread-count" v-if="session.unreadCount > 0">{{ session.unreadCount }}</span>
|
||||
<div v-show="filteredSessions.length>0" class="session-item"
|
||||
v-for="session in filteredSessions"
|
||||
:key="session.id"
|
||||
:class="{ 'active': currentSessionId === session.id, 'unread': session.unreadCount > 0 }"
|
||||
@click="switchSession(session)"
|
||||
>
|
||||
<div class="session-info">
|
||||
<div class="session-header">
|
||||
<span class="user-name">来自{{ session.oceania || '未知洲' }}-{{session.national||'未知国家'}}的会话</span>
|
||||
<span class="time">{{ formatTime(session.lastTime) }}</span>
|
||||
</div>
|
||||
<div class="last-message">
|
||||
<span>{{ session.lastMessage || '暂无消息' }}</span>
|
||||
<span class="unread-count" v-if="session.unreadCount > 0">{{ session.unreadCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="filteredSessions.length==0" style="text-align: center;color: rgb(90, 94, 102);padding-top: 5px;font-size: 14px">暂无会话</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getByServiceId, closeSession } from '@/api/busi/chatMain'
|
||||
|
||||
export default {
|
||||
name: 'messageList',
|
||||
data() {
|
||||
@ -49,11 +53,18 @@ export default {
|
||||
// 输入的消息
|
||||
inputMessage: '',
|
||||
// WebSocket连接
|
||||
websocket: null
|
||||
websocket: null,
|
||||
// 是否收起
|
||||
isCollapsed: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadSessions()
|
||||
// 从本地存储获取收起状态
|
||||
const collapsed = localStorage.getItem('messageListCollapsed')
|
||||
if (collapsed) {
|
||||
this.isCollapsed = JSON.parse(collapsed)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
searchKeyword(val) {
|
||||
@ -62,10 +73,18 @@ export default {
|
||||
(session.lastMessage && session.lastMessage.includes(val))
|
||||
})
|
||||
},
|
||||
// 监听收起状态变化,保存到本地存储
|
||||
isCollapsed(val) {
|
||||
localStorage.setItem('messageListCollapsed', JSON.stringify(val))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 切换收起/展开状态
|
||||
toggleCollapse() {
|
||||
this.isCollapsed = !this.isCollapsed
|
||||
},
|
||||
//结束当前会话
|
||||
closeCurrentSession(){
|
||||
closeCurrentSession() {
|
||||
closeSession(this.currentSessionId).then(() => {
|
||||
this.$message.success('会话已结束')
|
||||
// 通知用户会话已结束
|
||||
@ -75,7 +94,7 @@ export default {
|
||||
content: '客服已结束会话',
|
||||
sessionId: this.currentSessionId
|
||||
}
|
||||
this.$store.dispatch('websocket_send',JSON.stringify(wsMsg));
|
||||
this.$store.dispatch('websocket_send', JSON.stringify(wsMsg))
|
||||
// 切换会话列表
|
||||
this.currentSessionId = null
|
||||
this.currentSession = {}
|
||||
@ -83,13 +102,13 @@ export default {
|
||||
// 重新加载会话列表
|
||||
this.loadSessions()
|
||||
// 关闭会话
|
||||
this.$emit("closeForm")
|
||||
this.$emit('closeForm')
|
||||
})
|
||||
},
|
||||
//关闭聊天窗口了
|
||||
closeForm(){
|
||||
this.currentSessionId=null
|
||||
this.currentSession= {}
|
||||
closeForm() {
|
||||
this.currentSessionId = null
|
||||
this.currentSession = {}
|
||||
},
|
||||
// 加载会话列表
|
||||
loadSessions() {
|
||||
@ -102,10 +121,10 @@ export default {
|
||||
switchSession(session) {
|
||||
this.currentSessionId = session.id
|
||||
this.currentSession = session
|
||||
this.$emit("switchSession",session)
|
||||
this.$emit('switchSession', session)
|
||||
},
|
||||
//更新会话未读状态
|
||||
updateSessionRead(sessionId){
|
||||
updateSessionRead(sessionId) {
|
||||
const session = this.sessions.find(s => s.id === sessionId)
|
||||
if (session) {
|
||||
session.unreadCount = 0
|
||||
@ -127,11 +146,11 @@ export default {
|
||||
let newMsg = {
|
||||
id: new Date().getTime(), // 临时ID,实际应该从消息中获取
|
||||
mainId: message.sessionId,
|
||||
dataFrom: "customer", // 用户发送
|
||||
dataFrom: 'customer', // 用户发送
|
||||
senderId: message.fromUserId,
|
||||
receiverId: this.serviceId,
|
||||
content: message.content,
|
||||
createTime: new Date().toLocaleString('zh-CN', {
|
||||
createTime: new Date().toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
@ -143,7 +162,7 @@ export default {
|
||||
isRead: 1 // 已读
|
||||
}
|
||||
//调用父组件方法传递过去
|
||||
this.$emit("addNewMag",newMsg)
|
||||
this.$emit('addNewMag', newMsg)
|
||||
} else {
|
||||
// 不是当前查看的会话,更新未读数量
|
||||
const session = this.sessions.find(s => s.id === message.sessionId)
|
||||
@ -151,10 +170,14 @@ export default {
|
||||
session.unreadCount = (session.unreadCount || 0) + 1
|
||||
session.lastMessage = message.content
|
||||
session.lastTime = new Date()
|
||||
let str = message.content
|
||||
if(str.length>15){
|
||||
str = str.substring(0,15)+'...'
|
||||
}
|
||||
// 提示有新消息
|
||||
this.$notify.info({
|
||||
title: '新消息',
|
||||
message: `来自 ${session.userName || '匿名用户'} 的消息`,
|
||||
message: str,
|
||||
duration: 3000
|
||||
})
|
||||
} else {
|
||||
@ -192,25 +215,65 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.session-list {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
max-width: 350px;
|
||||
max-height: 400px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 84px;
|
||||
background-color: white;
|
||||
padding: 5px 0 5px 5px;
|
||||
background-color: white;
|
||||
border: 1px solid #c0c0c0;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: background-color 1s;
|
||||
}
|
||||
|
||||
.session-content {
|
||||
width: 300px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
padding-right: 8px;
|
||||
flex-direction: column;
|
||||
animation: slideIn 0.3s ease-out forwards;
|
||||
}
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 当元素隐藏时添加过渡效果 */
|
||||
.session-content[v-if] {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.toggle-button {
|
||||
width: 10px;
|
||||
max-height: 400px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.toggle-button:hover {
|
||||
color:rgb(64, 158, 255) ;
|
||||
}
|
||||
.search-box {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.session-item {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 8px;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
@ -238,7 +301,7 @@ export default {
|
||||
}
|
||||
|
||||
.session-info {
|
||||
margin-left: 10px;
|
||||
margin-left: 5px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user