589 lines
16 KiB
Vue
589 lines
16 KiB
Vue
<template>
|
||
<div class="pc_customerServer_container">
|
||
<!-- 客服头部开始 -->
|
||
<div class="pc_customerServer_container_header">
|
||
<div class="pc_customerServer_container_header_title">
|
||
<img :src="chatServerData.to_user_avatar" alt="" @click="onBackPress">
|
||
<span>{{chatServerData.to_user_nickname}}</span>
|
||
</div>
|
||
<div class="pc_customerServer_container_header_handle" @click="closeIframe" v-if="upperData.noCanClose != '1' && userAgentType === 0">
|
||
<span class="iconfont"></span>
|
||
</div>
|
||
</div>
|
||
<!-- 客服头部结束 -->
|
||
|
||
<!-- 聊天内容开始 -->
|
||
<div class="pc_customerServer_container_content">
|
||
<div class="productMessage_container" v-if="isShowProductModel">
|
||
<div class="productMessage_container_image">
|
||
<img :src="productMessage.image" alt="">
|
||
</div>
|
||
<div class="productMessage_container_content">
|
||
<div class="productMessage_container_content_title">{{productMessage.store_name}}</div>
|
||
<div class="productMessage_container_content_priceOrHandle">
|
||
<div>¥{{productMessage.price}}</div>
|
||
<div @click="sendProduct">发送客服</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<happy-scroll size="1" resize hide-horizontal :scroll-top="scrollTop" @vertical-start="scrollHandler">
|
||
<div class="scroll_content" id="chat_scroll" :class="{ 'pt140': isShowProductModel }">
|
||
<!-- 滑动到容器顶部时,动画加载 -->
|
||
<Spin v-show="isLoad">
|
||
<Icon type="ios-loading" size=18 class="demo-spin-icon-load"></Icon>
|
||
<div>Loading</div>
|
||
</Spin>
|
||
<!-- 动画结束 -->
|
||
|
||
<!-- 聊天内容列表 -->
|
||
<div class="chart_list">
|
||
<div class="chart_list_item" v-for="(item, index) in records" :key="index">
|
||
<div class="chart_list_item_time" v-show="item.show">{{item.time}}</div>
|
||
<div class="chart_list_item_content" :class="{'right-box': item.user_id == chatServerData.user_id}">
|
||
<div class="chart_list_item_avatar">
|
||
<img :src="item.avatar" alt="">
|
||
</div>
|
||
<!-- 文字及表情信息 -->
|
||
<div class="chart_list_item_text" v-if="item.msn_type <= 2">
|
||
<span v-html="replace_em(item.msn)"></span>
|
||
</div>
|
||
<!-- 图片信息 -->
|
||
<div class="chart_list_item_img" v-if="item.msn_type == 3">
|
||
<img v-lazy="item.msn" @load="imageLoad" />
|
||
</div>
|
||
<!-- 图文信息 -->
|
||
<div class="chart_list_item_imgOrText" v-if="item.msn_type == 5">
|
||
<div class="order-wrapper">
|
||
<div class="img-box">
|
||
<img :src="item.other.image" alt="">
|
||
</div>
|
||
<div class="order-info">
|
||
<div class="price-box">
|
||
<div class="num">¥ {{item.other.price}}</div>
|
||
<!-- <a herf="javascript:;" class="more" @click.stop="lookGoods(item)">查看商品 ></a> -->
|
||
</div>
|
||
<div class="name">{{item.other.store_name}}</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
<!-- 聊天内容列表结束 -->
|
||
</div>
|
||
</happy-scroll>
|
||
</div>
|
||
<!-- 聊天内容结束 -->
|
||
|
||
<!-- 内容输入开始 -->
|
||
|
||
<div class="footer_customerServer_container">
|
||
<div class="mobel_customerServer_container_footer">
|
||
<div class="mobel_customerServer_container_footer_uploag_image">
|
||
<span class="iconfont"></span>
|
||
<input type="file" class="file_input" @change="uploadFile">
|
||
</div>
|
||
<div class="mobel_customerServer_container_footer_input">
|
||
<div class="mobel_customerServer_container_footer_input_con">
|
||
<textarea @keyup.enter="sendText" @focus="textareaInput" class="font" @input="textareaChange($event)" v-model='userMessage' placeholder="请输入内容"></textarea>
|
||
<p class="font" v-html='pCont'></p>
|
||
</div>
|
||
<!-- <div class="mobel_customerServer_container_footer_input_send" @click="sendText">
|
||
<span class="iconfont primary_color"></span>
|
||
</div> -->
|
||
</div>
|
||
|
||
<!-- 选择表情 -->
|
||
<div class="mobel_customerServer_container_footer_emoji" @click="selectEmoji">
|
||
<span class="iconfont "></span>
|
||
</div>
|
||
<!-- 发送消息 -->
|
||
<div class="sendMessage" :class="{'sendMessage-primary': userMessage}">
|
||
<div @click="sendText">发送</div>
|
||
</div>
|
||
</div>
|
||
<!-- 表情及图片容器 -->
|
||
<div class="mobel_customerServer_container_footer_emojiList" :class="{'canSelectemoji': inputConType == 2}">
|
||
<div class="emoji-item" v-for="(emoji, index) in emojiList" :key="index" v-if="inputConType == 2">
|
||
<i class="em" :class="emoji" @click.stop="select(emoji)"></i>
|
||
</div>
|
||
</div>
|
||
<!-- 表情及图片容器结束 -->
|
||
</div>
|
||
<!-- 内容输入结束 -->
|
||
|
||
</div>
|
||
</template>
|
||
<script>
|
||
import { HappyScroll } from 'vue-happy-scroll'
|
||
import emojiList from "@/utils/emoji";
|
||
import socketServer from './minix/socketServer';
|
||
import '@/assets/js/uniapp.js';
|
||
export default {
|
||
components: {
|
||
HappyScroll
|
||
},
|
||
mixins: [socketServer],
|
||
data() {
|
||
return {
|
||
isLoad: false,
|
||
scrollTop: 0,
|
||
emojiList: emojiList,
|
||
pCont: '',
|
||
deviceType:'mobile',
|
||
}
|
||
},
|
||
mounted() {
|
||
if (this.userAgentType !== 0) {
|
||
window.addEventListener('popstate', this.handlePopstate)
|
||
}
|
||
},
|
||
beforeDestroy() {
|
||
if (this.userAgentType !== 0) {
|
||
window.removeEventListener('popstate', this.handlePopstate)
|
||
}
|
||
},
|
||
computed: {
|
||
records() {
|
||
return this.chatServerData.serviceList.map((item, index) => {
|
||
item.time = this.$moment(item.add_time * 1000).format('MMMDo H:mm')
|
||
if(index) {
|
||
if(
|
||
item.add_time -
|
||
this.chatServerData.serviceList[index - 1].add_time >=
|
||
300
|
||
) {
|
||
item.show = true;
|
||
} else {
|
||
item.show = false;
|
||
}
|
||
} else {
|
||
item.show = true;
|
||
}
|
||
return item;
|
||
});
|
||
}
|
||
},
|
||
methods: {
|
||
onBackPress(){
|
||
alert(uniWeb)
|
||
uniWeb.webView.navigateBack({
|
||
delta: history.length,
|
||
fail(err){
|
||
alert(err)
|
||
},
|
||
});
|
||
},
|
||
handlePopstate() {
|
||
alert(history.length);
|
||
uniWeb.webView.navigateBack({
|
||
delta: history.length,
|
||
});
|
||
history.pushState(null, null, document.URL);
|
||
},
|
||
textareaChange(e) {
|
||
let strCont = e.target.value.replace(/\n\s(\s)*/gi, '\n') // 将多个回车换行合并为 1个
|
||
strCont = strCont.replace(/^\s*/gi, '') // 清除首行的 空格与换行
|
||
|
||
let strHtml = strCont.replace(/</gi, '<') // 将所有的 < 转义为 < 防止html标签被转义
|
||
strHtml = strCont.replace(/\n(\n)*/gi, '<br>') // 回车换行替换为 <br>
|
||
strHtml = strHtml.replace(/\s+/gi, ' ') // 一个或过个空格 替换为
|
||
|
||
strCont = strHtml.replace(/ /gi, ' ') // 逆向处理
|
||
strCont = strCont.replace(/<br>/gi, '\n') // 逆向处理
|
||
strCont = strCont.replace(/</gi, '<')
|
||
|
||
this.userMessage = strCont
|
||
/** 如果 p 标签最后的字符为 <br> 并不会单独另起一行, 会导致与 textarea 的高度相差一行,
|
||
* 所以在最后添加一个字符, 这样就能保证 P 标签的高度与 textarea 的高度一致
|
||
*/
|
||
this.pCont = strHtml + '.'
|
||
},
|
||
getScrollTop() {
|
||
console.log(123);
|
||
},
|
||
getScrollEnd() {
|
||
console.log(321);
|
||
},
|
||
|
||
|
||
// 聊天表情转换
|
||
replace_em(str) {
|
||
str = str.replace(/\[em-([\s\S]*)\]/g, "<span class='em em-$1'/></span>");
|
||
return str;
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
<style lang="less" scoped>
|
||
.pc_customerServer_container {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
background: #f5f5f5;
|
||
&_header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
background: linear-gradient(270deg, #1890ff 0%, #3875ea 100%);
|
||
padding: 7px 14px;
|
||
font-size: 16px;
|
||
color: #fff;
|
||
&_title {
|
||
display: flex;
|
||
align-items: center;
|
||
img {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
margin-right: 10px;
|
||
}
|
||
}
|
||
|
||
&_handle {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
|
||
&_content {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
.scroll_content {
|
||
width: 98%;
|
||
height: 100%;
|
||
overflow-y: auto;
|
||
padding-bottom: 20px;
|
||
box-sizing: border-box;
|
||
.chart_list {
|
||
position: relative;
|
||
z-index: 2;
|
||
&_item {
|
||
&_content {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 8px;
|
||
}
|
||
|
||
&_avatar {
|
||
width: 33px;
|
||
height: 33px;
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
margin-right: 10px;
|
||
align-self: flex-start;
|
||
img {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
&_text {
|
||
max-width: 60%;
|
||
word-wrap: break-word;
|
||
background: #fff;
|
||
padding: 9px 14px;
|
||
font-size: 15px;
|
||
border-radius: 6px;
|
||
}
|
||
&_img {
|
||
max-width: 60%;
|
||
img {
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
}
|
||
.chart_list_item_imgOrText {
|
||
background: #fff;
|
||
padding: 4px 10px;
|
||
border-radius: 8px;
|
||
width: 226px;
|
||
box-sizing: border-box;
|
||
.order-wrapper {
|
||
.img-box {
|
||
width: 100%;
|
||
img {
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
}
|
||
.order-info {
|
||
.price-box {
|
||
color: #ff0000;
|
||
font-size: 18px;
|
||
}
|
||
.name {
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
&_time {
|
||
text-align: center;
|
||
margin: 10px auto;
|
||
}
|
||
.right-box {
|
||
flex-direction: row-reverse;
|
||
.chart_list_item_avatar {
|
||
margin-left: 10px;
|
||
}
|
||
.chart_list_item_text {
|
||
text-align: right;
|
||
background: #cde0ff;
|
||
color: #000;
|
||
}
|
||
.chart_list_item_img {
|
||
text-align: right;
|
||
background: #fff;
|
||
img {
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
}
|
||
.chart_list_item_imgOrText {
|
||
background: #fff;
|
||
padding: 10px;
|
||
border-radius: 8px;
|
||
width: 226px;
|
||
box-sizing: border-box;
|
||
.order-wrapper {
|
||
.img-box {
|
||
width: 100%;
|
||
img {
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
}
|
||
.order-info {
|
||
.price-box {
|
||
color: #ff0000;
|
||
font-size: 18px;
|
||
}
|
||
.name {
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
&_text {
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.mobel_customerServer_container_footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
border-top: 1px solid #ececec;
|
||
padding: 8px 15px;
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
.crmchat_link {
|
||
text-align: center;
|
||
width: 98%;
|
||
transition: 0.3s;
|
||
z-index: 1;
|
||
cursor: pointer;
|
||
position: absolute;
|
||
bottom: 60px;
|
||
span {
|
||
color: #ccc;
|
||
}
|
||
|
||
span:hover {
|
||
color: #007aff;
|
||
}
|
||
}
|
||
.iconfont {
|
||
font-size: 24px;
|
||
}
|
||
.mobel_customerServer_container_footer_uploag_image {
|
||
font-size: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
position: relative;
|
||
}
|
||
.file_input {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
opacity: 0;
|
||
}
|
||
&_input {
|
||
flex: 1;
|
||
margin: 0 9px;
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
box-sizing: border-box;
|
||
// line-height: 28px;
|
||
|
||
&_con {
|
||
flex: 1;
|
||
position: relative;
|
||
min-height: 32px;
|
||
display: flex;
|
||
align-items: center;
|
||
.font {
|
||
// font-size: 14px;
|
||
padding: 4px 8px;
|
||
}
|
||
textarea {
|
||
display: block;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 10;
|
||
resize: none;
|
||
outline: none;
|
||
background: none;
|
||
color: rgba(0, 0, 0, 0.7);
|
||
border: none;
|
||
width: 100%;
|
||
height: 100%;
|
||
font-size: 15px;
|
||
}
|
||
p {
|
||
width: 100%;
|
||
display: block;
|
||
min-height: 20px;
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
&_send {
|
||
align-self: flex-start;
|
||
padding-right: 8px;
|
||
}
|
||
}
|
||
|
||
&_emoji {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 24px;
|
||
}
|
||
.sendMessage {
|
||
background: #ccc;
|
||
padding: 4px 12px;
|
||
border-radius: 4px;
|
||
margin-left: 8px;
|
||
color: #fff;
|
||
font-size: 14px;
|
||
}
|
||
.sendMessage-primary {
|
||
background: #3875ea;
|
||
}
|
||
&_emojiList {
|
||
width: 100%;
|
||
height: 0;
|
||
// padding: 10px;
|
||
display: grid;
|
||
grid-template-columns: repeat(9, 1fr);
|
||
grid-row-gap: 20px;
|
||
overflow-y: auto;
|
||
background: #fff;
|
||
transition: 0.3s;
|
||
}
|
||
}
|
||
}
|
||
.canSelectemoji {
|
||
height: 165px !important;
|
||
padding: 10px;
|
||
}
|
||
|
||
.demo-spin-icon-load {
|
||
animation: ani-demo-spin 1s linear infinite;
|
||
}
|
||
|
||
.primary_color {
|
||
color: #3875ea;
|
||
}
|
||
.productMessage_container {
|
||
// height: 94px;
|
||
width: 100%;
|
||
padding: 12px;
|
||
box-sizing: border-box;
|
||
background: #fff;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
&_image {
|
||
margin-right: 12px;
|
||
img {
|
||
width: 77px;
|
||
height: 77px;
|
||
}
|
||
}
|
||
&_content {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
&_title {
|
||
font-size: 14px;
|
||
color: #333;
|
||
height: 42px;
|
||
font-weight: 800;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
text-align: left !important;
|
||
}
|
||
&_priceOrHandle {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
> div:nth-child(1) {
|
||
font-size: 18px;
|
||
color: #e93323;
|
||
text-align: left;
|
||
}
|
||
> div:nth-child(2) {
|
||
width: 65px;
|
||
height: 25px;
|
||
background: #e83323;
|
||
opacity: 1;
|
||
border-radius: 62px;
|
||
color: #fff;
|
||
font-size: 12px;
|
||
text-align: center;
|
||
line-height: 25px;
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.pt140 {
|
||
padding-bottom: 140px !important;
|
||
}
|
||
@keyframes ani-demo-spin {
|
||
from {
|
||
transform: rotate(0deg);
|
||
}
|
||
|
||
50% {
|
||
transform: rotate(180deg);
|
||
}
|
||
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
/deep/ .happy-scroll-content {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
</style>
|