|
|
|
@ -1,11 +1,30 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="chat-container">
|
|
|
|
|
<div class="header">当前客服:小爱</div>
|
|
|
|
|
<div class="chat-container" @click="emojiVisible = false">
|
|
|
|
|
<div class="header">
|
|
|
|
|
<p>当前客服:小爱</p>
|
|
|
|
|
<el-button type="text" @click="summaryVisible = !summaryVisible">
|
|
|
|
|
{{ summaryVisible ? '收起' : '统计数据' }}
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="summary">
|
|
|
|
|
<el-table v-show="summaryVisible" border :data="[summary]">
|
|
|
|
|
<el-table-column align="center" header-align="center" label="今日接待次数" prop="todayTimes" />
|
|
|
|
|
<el-table-column align="center" header-align="center" label="今日接待人数" prop="todayCount" />
|
|
|
|
|
<el-table-column align="center" header-align="center" label="历史接待次数" prop="historyTimes" />
|
|
|
|
|
<el-table-column align="center" header-align="center" label="历史接待人数" prop="historyCount" />
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="body">
|
|
|
|
|
<div class="aside">
|
|
|
|
|
<div class="aside-header">近期会话</div>
|
|
|
|
|
<el-scrollbar class="session-list" tag="ul">
|
|
|
|
|
<li v-for="(item, index) in sessionList" :key="index" class="session-item">
|
|
|
|
|
<li
|
|
|
|
|
v-for="(item, index) in sessionList"
|
|
|
|
|
:key="index"
|
|
|
|
|
class="session-item"
|
|
|
|
|
:class="{ active: state.currentIndex === index }"
|
|
|
|
|
@click="handleChangeSession(index)"
|
|
|
|
|
>
|
|
|
|
|
<el-badge class="session-count" :hidden="item.count === 0" :max="99" :value="item.count">
|
|
|
|
|
<el-avatar circle :src="item.avatar" />
|
|
|
|
|
</el-badge>
|
|
|
|
@ -29,34 +48,54 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="content-header-right">
|
|
|
|
|
<el-button>个人订单</el-button>
|
|
|
|
|
<el-button>转移会话</el-button>
|
|
|
|
|
<el-button>
|
|
|
|
|
<el-icon name="clipboard" />
|
|
|
|
|
个人订单
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button>
|
|
|
|
|
<el-icon name="chat-forward" />
|
|
|
|
|
转移会话
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<el-scrollbar class="message-list">
|
|
|
|
|
<el-scrollbar ref="refsMessageList" class="message-list">
|
|
|
|
|
<message-item v-for="(item, index) in sessionMessageList" :key="index" :message="item" />
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
<div class="operation-bar">
|
|
|
|
|
<el-button type="text" @click.stop="emojiVisible = !emojiVisible">
|
|
|
|
|
<el-icon name="chat-smile-3-fill" size="20" />
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button type="text" @click="handlePickImage">
|
|
|
|
|
<el-icon name="image-fill" size="20" />
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button type="text" @click="handlePickVideo">
|
|
|
|
|
<el-icon name="movie-fill" size="20" />
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-scrollbar v-show="emojiVisible" class="emoji-panel" tag="ul">
|
|
|
|
|
<li v-for="(item, index) in emojiList" :key="index" @click="handleAddEmoji(item)">
|
|
|
|
|
{{ entitiestoUtf16(item) }}
|
|
|
|
|
</li>
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
<el-button type="text" @click="emojiVisible = !emojiVisible">
|
|
|
|
|
<el-icon name="chat-smile-3-fill" size="24" />
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button type="text">
|
|
|
|
|
<el-icon name="image-fill" size="24" />
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button type="text">
|
|
|
|
|
<el-icon name="movie-fill" size="24" />
|
|
|
|
|
</el-button>
|
|
|
|
|
<input
|
|
|
|
|
ref="refsImage"
|
|
|
|
|
accept="image/*"
|
|
|
|
|
style="display: none"
|
|
|
|
|
type="file"
|
|
|
|
|
@change="handleSendImage"
|
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
ref="refsVideo"
|
|
|
|
|
accept="video/*"
|
|
|
|
|
style="display: none"
|
|
|
|
|
type="file"
|
|
|
|
|
@change="handleSendVideo"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="input">
|
|
|
|
|
<el-input v-model="state.message" type="textarea" />
|
|
|
|
|
<el-input v-model="state.message" placeholder="请输入要发送的内容" type="textarea" />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="send">
|
|
|
|
|
<el-button type="primary">发送</el-button>
|
|
|
|
|
<el-button type="primary" @click="handleSendMessage">发送</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
@ -66,6 +105,22 @@
|
|
|
|
|
<script setup>
|
|
|
|
|
import { emojiData, entitiestoUtf16 } from '@/utils/emoji.js';
|
|
|
|
|
import MessageItem from './message.vue';
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
|
|
|
|
|
// 统计
|
|
|
|
|
const summaryVisible = ref(true);
|
|
|
|
|
const summary = ref({
|
|
|
|
|
historyTimes: 96,
|
|
|
|
|
historyCount: 88,
|
|
|
|
|
todayTimes: 10,
|
|
|
|
|
todayCount: 8,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 会话
|
|
|
|
|
const state = reactive({
|
|
|
|
|
currentIndex: 0,
|
|
|
|
|
message: '',
|
|
|
|
|
});
|
|
|
|
|
const sessionList = reactive([
|
|
|
|
|
{
|
|
|
|
|
name: '小可爱',
|
|
|
|
@ -89,10 +144,6 @@
|
|
|
|
|
content: '你好,请问这个能有优惠吗?',
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
const state = reactive({
|
|
|
|
|
currentIndex: 0,
|
|
|
|
|
message: '',
|
|
|
|
|
});
|
|
|
|
|
const currentSession = computed(() => {
|
|
|
|
|
return sessionList[state.currentIndex];
|
|
|
|
|
});
|
|
|
|
@ -172,13 +223,80 @@
|
|
|
|
|
name: '小可爱',
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
const handleChangeSession = (index) => {
|
|
|
|
|
state.currentIndex = index;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 表情
|
|
|
|
|
const emojiVisible = ref(false);
|
|
|
|
|
const emojiList = computed(() => emojiData.map((item) => item.list).flat());
|
|
|
|
|
const handleAddEmoji = (data) => {
|
|
|
|
|
let str = ' ' + entitiestoUtf16(data) + ' ';
|
|
|
|
|
state.message += str;
|
|
|
|
|
emojiVisible.value = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 图片
|
|
|
|
|
const refsImage = ref(null);
|
|
|
|
|
const handlePickImage = () => {
|
|
|
|
|
refsImage.value.click();
|
|
|
|
|
};
|
|
|
|
|
const handleSendImage = (e) => {
|
|
|
|
|
const file = e.target.files[0];
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
|
reader.onload = (e) => {
|
|
|
|
|
const data = e.target.result;
|
|
|
|
|
const message = {
|
|
|
|
|
type: 'image',
|
|
|
|
|
content: data,
|
|
|
|
|
};
|
|
|
|
|
handleSendMessage(message);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 视频
|
|
|
|
|
const refsVideo = ref(null);
|
|
|
|
|
const handlePickVideo = () => {
|
|
|
|
|
refsVideo.value.click();
|
|
|
|
|
};
|
|
|
|
|
const handleSendVideo = (e) => {
|
|
|
|
|
const file = e.target.files[0];
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
reader.readAsDataURL(file);
|
|
|
|
|
reader.onload = (e) => {
|
|
|
|
|
const data = e.target.result;
|
|
|
|
|
const message = {
|
|
|
|
|
type: 'video',
|
|
|
|
|
content: data,
|
|
|
|
|
};
|
|
|
|
|
handleSendMessage(message);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 发送消息
|
|
|
|
|
const refsMessageList = ref(null);
|
|
|
|
|
const handleSendMessage = (data) => {
|
|
|
|
|
const message = data || {
|
|
|
|
|
type: 'text',
|
|
|
|
|
content: state.message,
|
|
|
|
|
};
|
|
|
|
|
if (message.content) {
|
|
|
|
|
Object.assign(message, {
|
|
|
|
|
time: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'),
|
|
|
|
|
from: 'employee',
|
|
|
|
|
avatar: 'https://placem.at/people',
|
|
|
|
|
name: '小爱',
|
|
|
|
|
});
|
|
|
|
|
sessionMessageList.push(message);
|
|
|
|
|
if (!data) {
|
|
|
|
|
state.message = '';
|
|
|
|
|
}
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
refsMessageList.value.setScrollTop(refsMessageList.value.scrollbar$.scrollHeight);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
proxy.$message.warning('发送消息不能为空');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
@ -188,22 +306,30 @@
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
.header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: @layout-space 0;
|
|
|
|
|
.el-button {
|
|
|
|
|
margin-left: @layout-space-super;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.summary {
|
|
|
|
|
margin-bottom: @layout-space;
|
|
|
|
|
}
|
|
|
|
|
.body {
|
|
|
|
|
width: 100%;
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
display: flex;
|
|
|
|
|
border: 1px solid #eee;
|
|
|
|
|
border: 1px solid #ebeef5;
|
|
|
|
|
.aside {
|
|
|
|
|
border-right: 1px solid #eee;
|
|
|
|
|
border-right: 1px solid #ebeef5;
|
|
|
|
|
.aside-header {
|
|
|
|
|
height: 60px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: @layout-space;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
|
font-size: @layout-h3;
|
|
|
|
|
font-weight: bolder;
|
|
|
|
|
}
|
|
|
|
@ -218,6 +344,9 @@
|
|
|
|
|
&:hover {
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
&.active {
|
|
|
|
|
background-color: #eee;
|
|
|
|
|
}
|
|
|
|
|
.session-count {
|
|
|
|
|
margin-right: @layout-space;
|
|
|
|
|
:deep(.el-badge__content) {
|
|
|
|
@ -246,7 +375,7 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
+ .session-item {
|
|
|
|
|
border-top: 1px solid #eee;
|
|
|
|
|
border-top: 1px solid #ebeef5;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -261,7 +390,7 @@
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: @layout-space;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
|
.content-header-left {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
@ -291,9 +420,9 @@
|
|
|
|
|
.operation-bar {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: @layout-space;
|
|
|
|
|
border-top: 1px solid #eee;
|
|
|
|
|
border-bottom: 1px solid #eee;
|
|
|
|
|
padding: 0 @layout-space;
|
|
|
|
|
border-top: 1px solid #ebeef5;
|
|
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
|
|
position: relative;
|
|
|
|
|
.emoji-panel {
|
|
|
|
|
position: absolute;
|
|
|
|
@ -319,7 +448,7 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.input {
|
|
|
|
|
height: 100px;
|
|
|
|
|
height: 64px;
|
|
|
|
|
.el-textarea {
|
|
|
|
|
height: 100%;
|
|
|
|
|
:deep(textarea) {
|
|
|
|
|