xw_admin/src/views/service/infoQuery.vue
2024-11-28 23:14:45 +08:00

697 lines
19 KiB
Vue

<template>
<el-container class="mainNoBack userBox">
<el-aside class="userAside" width="220px">
<el-container>
<el-main class="nopadding" v-loading="contactsLoading" element-loading-text="加载中...">
<el-scrollbar>
<div class="contacts">
<div class="contactsTitle">联系人</div>
<div class="bodyView">
<div class="itemCol" :class="{'itemColActive':item.from_user && item.from_user.id === to_user_id}" v-for="(item,index) in contactsList" :key="index" @click="contactsActive(item)">
<div class="avatar">
<el-avatar :size="36" :src="item.from_user && item.from_user.avatar" fit="contain">
<img src="https://cube.elemecdn.com/e/fd/0fc7d20532fdaf769a25683617711png.png"/>
</el-avatar>
</div>
<div class="viewRight">
<div class="title">{{item.from_user && item.from_user.name==""?'匿名':item.from_user && item.from_user.name}}</div>
<div class="msg" v-if="item.to_message && item.to_message.send_message_type === 1">{{item.to_message.to_message}}</div>
<div class="msg" v-if="item.to_message && item.to_message.send_message_type === 2">
{{item.to_message_list[0].client_file_name?item.to_message_list[0].client_file_name:'未命名'}}
</div>
</div>
</div>
</div>
</div>
</el-scrollbar>
</el-main>
</el-container>
</el-aside>
<el-main class="userMain userMainPadding_0" style="border-top-right-radius: 0;border-bottom-right-radius: 0;padding: 0;">
<div class="mainBody">
<div class="mainTitle">{{contactsInfo.from_user && contactsInfo.from_user.name?contactsInfo.from_user.name:"匿名"}}</div>
<div class="mainView" v-loading="msgDataLoading" element-loading-text="加载中...">
<el-scrollbar ref="scrollbar" @scroll="handleScroll">
<div class="msgList">
<div class="msgItem" :class="user_id != item.to_user_id?'msgRightItem':''" v-for="(item,index) in msgList" :key="index">
<div class="avatar" v-if="user_id === item.to_user_id">
<el-avatar :size="36" :src="item.from_user.avatar" fit="contain">
<img src="https://cube.elemecdn.com/e/fd/0fc7d20532fdaf769a25683617711png.png"/>
</el-avatar>
</div>
<div class="msgText">
<div class="msgTitle">
<span class="leftTime" v-if="user_id != item.to_user_id">{{item.created_at}}</span>
{{item.from_user && item.from_user.name?item.from_user.name:'匿名'}}
<span class="time" v-if="user_id === item.to_user_id">{{item.created_at}}</span>
</div>
<div class="textCom" v-if="item.send_message_type === 1">
<div v-html="item.to_message"></div>
</div>
<div class="textCom textComNoneBack" v-if="item.send_message_type === 2">
<div :class="user_id != item.to_user_id?'fileRightItem':''" v-for="(em,ind) in item.to_message_list" :key="ind">
<span class="imgView" :class="user_id != item.to_user_id?'imgViewRight':''" v-if="['png','jpg','gif','jpeg'].indexOf(em.file_type || em.extension) !=-1">
<el-icon class="loadingIcon" size="30" v-if="user_id != item.to_user_id && em.showLoading" style="color:#333;padding: 4px;"><el-icon-Loading/></el-icon>
<el-image style="width: 240px;height: 200px;" fit="contain" lazy :src="em.file?em.file:em" preview-teleported :preview-src-list="[em.file?em.file:em]"></el-image>
<el-icon class="loadingIcon" size="30" v-if="user_id === item.to_user_id && em.showLoading" style="color:#333;padding: 4px;"><el-icon-Loading/></el-icon>
</span>
<span class="fileView" v-if="['png','jpg','gif','jpeg'].indexOf(em.file_type || em.extension) ===-1" @click="linkFile(em)">
<fileType size="26px" :fileType="em.extension || em.file_type" />
<span class="fileName">{{em.client_file_name?em.client_file_name:'未命名'}}</span>
</span>
<span class="progressLine" v-if="['png','jpg','gif','jpeg'].indexOf(em.file_type || em.extension) ===-1 && em.showLoading">
<el-progress class="exportPopover" :text-inside="true" :show-text="false" :stroke-width="4" :percentage="em.rate" />
</span>
</div>
</div>
</div>
<div class="avatar" v-if="user_id != item.to_user_id">
<el-avatar :size="36" :src="item.from_user.avatar" fit="contain">
<img src="https://cube.elemecdn.com/e/fd/0fc7d20532fdaf769a25683617711png.png"/>
</el-avatar>
</div>
</div>
</div>
</el-scrollbar>
</div>
<div class="mainFooter">
<div class="tagList">
<div class="tagItem" @click="uploadFile">
<uploadFile ref="uploadFile" @uploadFileSuccess="uploadFileSuccess" title="上传附件"></uploadFile>
</div>
<div class="tagItem" @click="recordsShow"><el-icon size="18"><sc-icon-Record/></el-icon></div>
<el-dropdown trigger="click" ref="dropdownDown">
<div class="tagItem"><el-icon size="18"><sc-icon-Expression/></el-icon></div>
<template #dropdown>
<ul class="emojiView">
<li class="emojiCol" v-for="(item,index) in emojiJson" :key="index" @click.stop="emojiChange(item)">{{item}}</li>
</ul>
</template>
</el-dropdown>
</div>
<div class="sendView">
<el-input @keydown="handleKeydown" v-model="params.to_message" class="customTextarea" autofocus ref="messageInput" type="textarea" placeholder="Shift+Enter换行 请输入.." :rows="3" resize="none"></el-input>
<div class="saveBtn" @click="sendCustomer" :class="params.to_message!=''?'saveActive':''">
<el-icon size="22"><sc-icon-SendCustom /></el-icon>
</div>
</div>
</div>
</div>
</el-main>
<el-aside class="userAside" width="260px">
<el-container>
<el-main class="nopadding" style="border-left: 1px solid #f4f4f4;border-top-left-radius: 0;border-bottom-left-radius: 0;">
<div class="contactsUserInfo">
<div class="contactsTitle">
<el-avatar :size="56" :src="contactsInfo.from_user && contactsInfo.from_user.avatar" fit="contain">
<img src="https://cube.elemecdn.com/e/fd/0fc7d20532fdaf769a25683617711png.png"/>
</el-avatar>
</div>
<div class="bodyView">
<div class="itemCol">
<div class="name">昵称</div>
<div class="text">{{contactsInfo.from_user && contactsInfo.from_user.name?contactsInfo.from_user.name:"匿名"}}</div>
</div>
<div class="itemCol">
<div class="name">手机号</div>
<div class="text">{{contactsInfo.from_user && contactsInfo.from_user.mobile}}</div>
</div>
<div class="itemCol">
<div class="name">邮箱</div>
<div class="text">{{ contactsInfo.from_user && contactsInfo.from_user.email }}</div>
</div>
</div>
</div>
</el-main>
</el-container>
</el-aside>
</el-container>
<customer-records-dialog v-if="dialog.show" ref="recordsMsgDialog" @closed="dialog.show=false"></customer-records-dialog>
</template>
<script>
import {eventBus} from "@/utils/eventBus";
import customerRecordsDialog from "@/views/service/components/customerRecords"
import uploadFile from "@/views/service/components/attachmentUpload";
import fileType from "@/views/docsManager/fileType"
const emoji = require("@/views/service/components/emoji.json");
export default {
name: "",
components:{
customerRecordsDialog,
uploadFile,
fileType
},
data(){
return{
contactsList:[],
contactsInfo:{},
msgDataLoading:false,
contactsLoading:false,
msgList:[],
emojiJson:[],
user_id:0,
to_user_id:0,
page:1,
params:{
client:1, // 1 管理后台 2 客服端
to_user_id:"",
to_message:"",
},
dialog:{
show:false
},
mesList:true,
user_chat_params:{},
}
},
created() {
},
mounted() {
const userInfo = this.$TOOL.data.get("USER_INFO");
this.user_id = userInfo.id;
setTimeout(()=>{
this.getContactList();
},1000)
// 获取新消息
eventBus.$on('sockBack', this.getWsResult);
this.emojiJson = emoji.data.split(',');
document.addEventListener('click', this.closeDropdown);
// 获取路由参数
let user_chat_info = this.$TOOL.data.get("CHAT_PARAMS");
if(user_chat_info){
this.user_chat_params = JSON.parse(user_chat_info);
}
},
unmounted() {
document.removeEventListener('click', this.closeDropdown);
},
methods:{
getWsResult(res){
if(res.data && (res.data.type == 12 || res.data.type == 35 || res.data.type == 36 || res.data.type == 38)){
switch(res.data.type) {
case 12:
this.msgList.forEach(item=>{
if(item.to_message_list && item.to_message_list.length>0){
item.to_message_list.forEach(em=>{
if(em.uuid && em.uuid === res.data.file_unique_id){
em.showLoading = res.data.rate !== 100;
em.rate = res.data.rate;
}
})
}
})
break;
case 35:
if(res.data.send_message_type === 2 && res.data.data.to_message){
res.data.data.to_message_list = JSON.parse(res.data.data.to_message);
}
if(res.data.data){
let obj = {
created_at: res.data.from_time,
send_message_type: res.data.send_message_type ? res.data.send_message_type : 1,
to_user_id: res.data.to_user_id,
to_message: res.data.data.to_message,
to_message_list: res.data.data.to_message_list,
from_user: {
avatar: res.data.from_avatar,
name: res.data.from_user,
}
};
this.msgList.push(obj);
setTimeout(()=>{
this.scrollDown();
},100);
}
break;
case 36:
res.data.rows.forEach((item)=>{
if(item.send_message_type == 2){
item.to_message_list = JSON.parse(item.to_message);
}
})
this.msgList = res.data.rows.concat(this.msgList);
this.page = res.data.page;
setTimeout(()=>{
if(res.data.page === res.data.pageSize){
this.scrollDown();
}
},100);
break;
case 38:
if(res.data.contact_list && res.data.contact_list.length>0){
res.data.contact_list.forEach(item=>{
if(item.to_message && item.to_message.send_message_type === 2){
item.to_message_list = JSON.parse(item.to_message.to_message);
}
})
}
this.contactsList = res.data.contact_list;
// 判断当前联系人列表是否存在了这个人不存在就插入进去
if(this.user_chat_params && this.user_chat_params.id){
let ids = this.contactsList.map(em=>em.from_user_id);
if(ids.indexOf(this.user_chat_params.id)===-1){
this.contactsList.unshift({
created_at:this.$TOOL.dateFormat(new Date()),
from_user:{
avatar:this.user_chat_params.avatar,
email:this.user_chat_params.email,
id:this.user_chat_params.id,
mobile:this.user_chat_params.mobile,
name:this.user_chat_params.name
},
from_user_id:this.user_chat_params.id,
id:0,
to_message:{},
to_user_id:""
})
}
}
if(this.contactsList.length>0){
if(this.mesList){
this.to_user_id = this.contactsList[0].from_user.id;
this.params.to_user_id = this.contactsList[0].from_user.id;
this.contactsInfo = this.contactsList[0];
this.getCustomerMsgList();
this.mesList = false;
}
}
break;
default:
break;
}
}
},
async getContactList() {
this.contactsLoading = true;
await this.$API.customer.contactList.post();
this.contactsLoading = false;
},
contactsActive(item){
this.contactsInfo = item;
this.to_user_id = item.from_user.id;
this.params.to_user_id = item.from_user.id;
this.msgList = [];
this.getCustomerMsgList(false);
this.$nextTick(()=>{
this.$refs.messageInput.focus();
})
},
async getCustomerMsgList(isPage=false) {
let params = {}
if(!isPage){
this.msgDataLoading = true;
params = {
to_user_id:this.to_user_id,
pageSize:30
}
}else{
params = {
to_user_id:this.to_user_id,
page:this.page,
pageSize:30
}
}
await this.$API.customer.list.post(params);
this.msgDataLoading = false;
},
handleKeydown(event){
if(event.key === 'Enter' && !event.shiftKey){
event.preventDefault(); // 阻止输入框的默认行为
this.sendCustomer();
}else if(event.key === 'Enter' && event.shiftKey){
this.params.to_message += '';
}
},
async sendCustomer() {
if(this.params.to_message =="") return
const res = await this.$API.customer.send.post(this.params);
if(res.code == 200){
const userInfo = this.$TOOL.data.get("USER_INFO");
this.msgList.push({
to_user_id:-1,
send_message_type:1,
created_at:this.$TOOL.dateFormat(new Date()),
from_user:{
name:userInfo.name,
avatar:userInfo.avatar
},
to_message:this.params.to_message
});
this.params.to_message = "";
this.scrollDown();
}
},
emojiChange(item){
let text = this.$TOOL.objCopy(this.params.to_message);
this.params.to_message = text + item;
},
closeDropdown(event){
if(this.$refs.dropdownDown && this.$refs.dropdownDown.$el){
const dropdownElement = this.$refs.dropdownDown.$el;
if (!dropdownElement.contains(event.target)) {
this.$refs.dropdownDown.handleClose();
}
}
setTimeout(()=>{
this.$nextTick(()=>{
if(this.$refs.messageInput){
this.$refs.messageInput.focus();
}
})
},10)
},
// 上传附件
uploadFile(){
this.$nextTick(()=>{
this.$refs.uploadFile.importFile(this.params);
})
},
uploadFileSuccess(data){
const userInfo = this.$TOOL.data.get("USER_INFO");
this.msgList.push({
to_user_id:-1,
send_message_type:2,
created_at:this.$TOOL.dateFormat(new Date()),
from_user:{
name:userInfo.name,
avatar:userInfo.avatar
},
to_message_list:data,
to_message:''
});
setTimeout(()=>{
this.scrollDown();
},100)
},
recordsShow(){
this.dialog.show = true;
this.$nextTick(() => {
this.$refs.recordsMsgDialog.open('show').setData(this.contactsInfo);
})
},
linkFile(em){
window.location.href = em.file;
},
handleScroll(event){
const { scrollTop } = event;
if(scrollTop === 0 && this.msgList.length>0 && this.page >1){
this.page = this.page -1;
this.getCustomerMsgList(true)
}
},
scrollDown() {
this.$nextTick(() => {
const wrap = this.$refs.scrollbar;
if(wrap){
const e = wrap.$el.querySelector('.el-scrollbar__wrap');
if(e){
e.scrollTop = e.scrollHeight;
}
}
})
},
}
}
</script>
<style scoped lang="scss">
::v-deep .noBorderRadius{border-radius: 0;}
.contacts{
height: 100%;
display: flex;
flex-direction: column;
.contactsTitle{
padding: 5px 10px;
font-size: 14px;
}
.bodyView{
flex: 1;
display: flex;
flex-direction: column;
padding: 0 5px;
.itemCol{
padding: 10px 8px;
display: flex;
align-items: flex-start;
.viewRight{
margin-left: 10px;
flex: 1;
overflow: hidden;
.title{
font-size: 13px;
font-weight: 500;
margin-bottom: 4px;
}
.msg{
width: 100%;
color: #888888;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
.itemCol:hover{
background: #F6FBFF;
border-radius: 4px;
cursor: pointer;
}
.itemColActive{
background: #F6FBFF;
border-radius: 4px;
}
}
}
.contactsUserInfo{
display: flex;
flex-direction: column;
padding: 0 10px;
.contactsTitle{
padding: 20px 10px;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1px solid #F6F6F6;
}
.bodyView{
flex:1;
display: flex;
flex-direction: column;
padding: 10px 5px;
.itemCol{
display: flex;
justify-content: space-between;
margin-bottom: 15px;
.name{
color: #888;
}
}
}
}
.mainBody{
height: 100%;
padding: 15px 0;
display: flex;
flex-direction: column;
.mainTitle{
border-bottom: 1px solid #F6F6F6;
padding-bottom: 10px;
margin: 0 10px;
font-weight: 500;
font-size: 14px;
}
.mainView{
flex: 1;
overflow: hidden;
padding-bottom: 10px;
.msgList{
padding: 10px;
.msgItem{
display: flex;
margin-bottom: 12px;
padding-right: 40px;
.avatar{
padding-right: 10px;
}
.msgText{
.msgTitle{
padding:0 0 8px 0;
color: #555;
.time{
margin-left: 8px;
font-size: 12px;
color: #888;
}
.leftTime{
margin-right: 8px;
font-size: 12px;
color: #888;
}
}
.textCom{
background: #f5f5f5;
border-radius: 4px;
padding: 10px;
display: inline-block;
text-align: left;
.imgView{
height: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 5px;
.loadingIcon{
animation: rotate 1s linear infinite;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
}
.imgViewRight{
justify-content: flex-end;
}
.fileView{
cursor: pointer;
color: var(--el-color-primary);
display: flex;
align-items: center;
border: 1px solid #f4f4f4;
padding: 10px;
border-radius: 4px;
background: #f8f6f6;
margin-bottom: 5px;
}
}
.el-image{
border-radius: 4px;
}
.textComNoneBack{
background: none;
padding: 10px 0;
.fileRightItem{
text-align: right;
}
}
}
}
.msgRightItem{
justify-content: flex-end;
padding:0 0 0 40px;
.avatar{
padding-left: 10px;
padding-right: 0;
}
.msgText{
text-align: right;
.msgTitle{
text-align: right;
}
.textCom{
background: var(--el-color-primary);
color: var(--el-color-white);
text-align: left;
}
.textComNoneBack{
background: none;
.el-image{
border-radius: 4px;
}
}
}
}
}
}
.mainFooter{
margin: 0 10px 5px 10px;
padding: 10px 0;
border: 1px solid #F6F6F6;
border-radius: 4px;
position: relative;
.tagList{
display: flex;
align-items: center;
border-bottom: 1px solid #F6F6F6;
padding-bottom: 10px;
.tagItem{
display: flex;
align-items: center;
justify-content: center;
margin-left: 15px;
cursor: pointer;
}
}
.customTextarea{
font-size:13px;
::v-deep .el-textarea__inner{
box-shadow: none;
border: 0;
padding:10px 35px 10px 10px;
}
::v-deep .el-textarea__inner::-webkit-scrollbar{
width: 0;
}
}
.saveBtn{
position: absolute;
bottom:20px;
right: 10px;
z-index: 10;
color: #A8ABB2;
border-radius: 3px;
cursor: not-allowed;
display: flex;
align-items: center;
justify-content: center;
}
.saveActive{
cursor: pointer;
color: var(--el-color-primary);
}
}
}
.emojiView{
width: 650px;
padding: 10px;
display: flex;
flex-wrap: wrap;
.emojiCol{
padding: 5px;
cursor: pointer;
font-size: 16px;
list-style: none;
}
}
.exportPopover{
margin-bottom: 8px;
}
.exportPopover ::v-deep .el-progress-bar__innerText{
height: 100%;
display: flex;align-items: center;justify-content: flex-end;
font-size: 10px;
}
</style>