feat: 消息列表对接

feature/task1.0.0__0514__ch
向文可 2 years ago
parent 78628cc8a7
commit 1d807ab18f

@ -7,25 +7,37 @@ const state = () => ({
queue: [],
task: [],
sessionData: {},
currentSession: null,
messageList: [],
messageType: ['', 'text'],
});
const getters = {};
const getters = {
parseTime: () => {
return (timestamp) => {
dayjs(new Date(timestamp)).format('MM-DD HH:mm:ss');
};
},
parseContent: () => {
return (payload) => {
payload = JSON.parse(payload);
if ('text' in payload) {
payload = payload.text;
} else {
payload = '[未知消息]';
}
return payload;
};
},
};
const mutations = {
setSocket: (state, data) => (state.socket = data),
setHeart: (state, data) => (state.heart = data),
setTask: (state, data) => (state.task = data),
addTask: (state, data) => state.task.push(data),
delTask: (state, data) => state.task.splice(data, 1),
receive: (state, { code, traceType, content }) => {
if (code === 200) {
switch (traceType) {
case 27:
state.sessionData = content;
break;
default:
break;
}
}
},
setCurrentSession: (state, data) => (state.currentSession = data),
setSessionData: (state, data) => (state.sessionData = data),
setMessageList: (state, data) => (state.messageList = data),
};
const actions = {
/**
@ -36,17 +48,7 @@ const actions = {
if (window.WebSocket) {
const socket = new WebSocket('ws://192.168.10.93:8090/ws?client=' + token);
socket.onmessage = ({ data }) => {
data = JSON.parse(data);
if (data.traceType !== 0) {
let index = state.task.findIndex((item) => item.traceId === data.traceId);
if (index !== -1) {
commit('delTask', index);
commit('receive', data);
console.info('[chat] msg', data);
} else {
console.info('[chat] deprecated', data);
}
}
dispatch('receive', data);
};
socket.onopen = () => {
commit(
@ -110,7 +112,47 @@ const actions = {
}
},
/**
* 查询回话列表
* 接收数据
*/
receive: ({ state, commit, dispatch }, data) => {
data = JSON.parse(data);
if (data.traceType !== 0) {
let index = state.task.findIndex((item) => item.traceId === data.traceId);
if (index !== -1) {
commit('delTask', index);
dispatch('handle', data);
console.info('[chat] msg', data);
} else {
console.info('[chat] deprecated', data);
}
}
},
handle: ({ state, commit, dispatch }, { code, traceType, content, session }) => {
if (code === 200) {
switch (traceType) {
// 收到消息
case 25:
if (session.id === state.currentSession) {
commit('setMessageList', [...state.messageList, content.content]);
} else {
dispatch('querySession');
}
break;
// 会话列表
case 27:
commit('setSessionData', content);
break;
// 消息列表
case 28:
commit('setMessageList', content);
break;
default:
break;
}
}
},
/**
* 查询会话列表
*/
querySession: ({ dispatch }) => {
dispatch('invoke', {
@ -118,6 +160,15 @@ const actions = {
content: { storeId: 1 },
});
},
/**
* 查询会话消息列表
*/
querySessionMessage: ({ state, dispatch }) => {
dispatch('invoke', {
traceType: 28,
content: { sessionId: state.currentSession, size: 100, topMessageId: null },
});
},
};
export default {
state,

@ -1,6 +1,5 @@
<template>
<div class="chat-container" @click="emojiVisible = false">
{{ $store.state.chat }}
<div class="header">
<p>当前客服小爱</p>
<el-button type="text" @click="summaryVisible = !summaryVisible">
@ -23,8 +22,8 @@
v-for="(item, index) in sessionList"
:key="index"
class="session-item"
:class="{ active: state.currentIndex === index }"
@click="handleChangeSession(index)"
:class="{ active: currentSessionId === item.id }"
@click="handleChangeSession(item.id)"
>
<el-badge
class="session-count"
@ -37,20 +36,24 @@
<div class="session-info">
<div class="row">
<div class="session-name">{{ item.fromNickname }}</div>
<div class="session-time">{{ parseTime(item.lastMessage.createTimeStamp) }}</div>
<div class="session-time">
{{ store.getters['chat/parseTime'](item.lastMessage.createTimeStamp) }}
</div>
</div>
<div class="row">
<div class="session-content">{{ parseContent(item.lastMessage.payload) }}</div>
<div class="session-content">
{{ store.getters['chat/parseContent'](item.lastMessage.payload) }}
</div>
</div>
</div>
</li>
</el-scrollbar>
</div>
<div class="content">
<div v-if="currentSession" class="content">
<div class="content-header">
<div class="content-header-left">
<div class="name sex-1">
{{ currentSession?.name }}
<div class="name" :class="{ [`sex-` + currentSession?.fromSex]: true }">
{{ currentSession?.fromNickname }}
</div>
</div>
<div class="content-header-right">
@ -65,7 +68,12 @@
</div>
</div>
<el-scrollbar ref="refsMessageList" class="message-list">
<message-item v-for="(item, index) in sessionMessageList" :key="index" :message="item" />
<message-item
v-for="(item, index) in sessionMessageList"
:key="index"
:message="item"
:session="currentSession"
/>
</el-scrollbar>
<div class="operation-bar">
<el-button type="text" @click.stop="emojiVisible = !emojiVisible">
@ -104,6 +112,7 @@
<el-button type="primary" @click="handleSendMessage"></el-button>
</div>
</div>
<div v-else class="content empty">请点击左侧会话列表与买家进行聊天</div>
</div>
</div>
</template>
@ -125,106 +134,18 @@
});
//
const currentSessionId = computed(() => store.state.chat.currentSession);
const state = reactive({
currentIndex: 0,
message: '',
});
const sessionList = computed(() => {
return store.state.chat.sessionData?.sessionVOS || [];
});
const currentSession = computed(() => {
return sessionList[state.currentIndex];
});
//
const parseTime = (timestamp) => dayjs(new Date(timestamp)).format('MM-DD HH:mm:ss');
const parseContent = (payload) => {
payload = JSON.parse(payload);
if ('text' in payload) {
payload = payload.text;
} else {
payload = '[未知消息]';
}
return payload;
};
const sessionMessageList = reactive([
{
type: 'text',
content: '你好,请问这个能有优惠吗?',
time: '2019-12-12',
from: 'customer',
avatar: 'https://placem.at/people',
name: '小可爱',
},
{
type: 'notify',
content: '小马 将该会话转移给 小艾,并留言:发货问题',
time: '2019-12-12',
from: 'system',
},
{
type: 'text',
content: '没有',
time: '2019-12-12',
from: 'employee',
avatar: 'https://placem.at/people',
name: '小爱',
},
{
type: 'product',
content: `{
"id": 18,
"name": "柏战K20青轴机械键盘 混光机械键盘青轴电竞网吧游戏键盘电脑键盘",
"categoryId": 51,
"startingPrice": 79.0,
"totalStock": 1200,
"mainPicture": "https://msb-edu-dev.oss-cn-beijing.aliyuncs.com/mall-product/productO1CN011nFEoP1DrheUNhNgU_!!3435280270-0-cib.jpg",
"remoteAreaPostage": 10.0,
"singleBuyLimit": 0,
"isEnable": true,
"remark": "柏战K20青轴机械键盘 混光机械键盘青轴电竞网吧游戏键盘电脑键盘"
}`,
time: '2019-12-12',
from: 'customer',
avatar: 'https://placem.at/people',
name: '小可爱',
},
{
type: 'order',
content: `{
"orderId": 392016,
"orderNo": "6391f99d063e",
"userId": 513,
"userPhone": "17683712911",
"payAmount": 859.0,
"orderStatus": 2,
"orderStatusDesc": "已关闭",
"payType": 1,
"payTypeDesc": "未支付",
"orderSource": 2,
"orderSourceDesc": "安卓端APP",
"submitTime": "2022-05-10 20:36:59",
"product": {
"id": 18,
"name": "柏战K20青轴机械键盘 混光机械键盘青轴电竞网吧游戏键盘电脑键盘",
"categoryId": 51,
"startingPrice": 79.0,
"totalStock": 1200,
"mainPicture": "https://msb-edu-dev.oss-cn-beijing.aliyuncs.com/mall-product/productO1CN011nFEoP1DrheUNhNgU_!!3435280270-0-cib.jpg",
"remoteAreaPostage": 10.0,
"singleBuyLimit": 0,
"isEnable": true,
"remark": "柏战K20青轴机械键盘 混光机械键盘青轴电竞网吧游戏键盘电脑键盘"
}
}`,
time: '2019-12-12',
from: 'customer',
avatar: 'https://placem.at/people',
name: '小可爱',
},
]);
const handleChangeSession = (index) => {
state.currentIndex = index;
const currentSession = computed(() => sessionList.value.find((item) => item.id === currentSessionId.value));
const sessionMessageList = computed(() => store.state.chat.messageList);
const handleChangeSession = (id) => {
store.commit('chat/setCurrentSession', id);
store.dispatch('chat/querySessionMessage');
};
//
@ -384,6 +305,11 @@
flex: 1;
display: flex;
flex-direction: column;
&.empty {
justify-content: center;
align-items: center;
color: #999;
}
.content-header {
height: 60px;
display: flex;

@ -2,16 +2,16 @@
<div
class="message-item"
:class="{
[`--${props.message.type}`]: true,
'--self': props.message.from === 'employee',
[`--${messageType[props.message.type]}`]: true,
'--self': props.message.fromId === props.session.fromId,
}"
>
<div v-if="props.message.avatar" class="avatar">
<el-avatar :src="props.message.avatar" />
<div class="avatar">
<el-avatar :src="props.session.fromAvatar" />
</div>
<div class="message-body">
<div class="name">
{{ props.message.name }}
{{ props.session.fromNickname }}
</div>
<el-card v-if="props.message.type === 'product'" class="shadow">
<template #header>
@ -59,23 +59,54 @@
<el-button>查看详情</el-button>
</div>
</el-card>
<div v-else class="content" :class="{ shadow: props.message.type === 'text' }">
{{ props.message.content }}
<div v-else class="content" :class="{ shadow: messageType[props.message.type] === 'text' }">
{{ store.getters['chat/parseContent'](props.message.payload) }}
</div>
</div>
<div v-if="props.message.type !== 'notify'" class="time">
{{ props.message.time }}
{{ store.getters['chat/parseTime'](props.message.createTimeStamp) }}
</div>
</div>
</template>
<script setup>
/**
* 消息体结构
{
"code": 200,
"content": {
"createTimeStamp": "消息的创建时间戳",
"fromId": "消息发送人id",
"id": "消息id",
"payload": "{'text':'你好我是客服小王'}",
"traceId": "uuid",
"traceType": 23,
"sessionId": "消息所属会话id",
"messageIndex": "会话中消息的顺序",
"type": "消息的类型"
},
"session": {
"fromAvatar": "消息所属会话的对面人头像",
"fromId": "消息所属会话的对面人id",
"fromNickname": "消息所属会话的对面人昵称",
"id": "消息id",
"sysId": "系统id",
"type": "消息类型"
}
}
*/
const store = useStore();
const props = defineProps({
session: {
type: Object,
required: true,
},
message: {
type: Object,
required: true,
},
});
const messageType = computed(() => store.state.chat.messageType);
const product = computed(() => {
return props.message.type === 'product' ? JSON.parse(props.message.content) : {};
});

Loading…
Cancel
Save