Files
h5-uniapp/pages/kefu/kefu.vue
T
li 5f40e4e1b5 feat: 前端全量更新 - 含所有本次需求
- 移除秒合约页面及UI
- 合约页Tab改为Position Held/Transaction Record
- 存款/提款/转账页增加BTC/ETH
- 资产页重构(用户信息+盈亏+多语言)
- 注册页改版(匹配新设计稿)
- 日文全面翻译+首页菜单多语言
- 代理专属注册链接支持(?code=)
- 10个locale文件同步更新
2026-03-30 20:14:20 +08:00

1511 lines
39 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- FastAdmin在线客服插件不是开源产品所有文字图片样式风格等版权归在线客服作者所有如有复制仿冒抄袭盗用FastAdmin和在线客服作者将追究法律责任 -->
<template>
<view :style="'background-color: '+theme.bg+';color: '+theme.text" style="height: 100vh;">
<myNav :title="i18n.mine.help.kefu"></myNav>
<view class="kefu_container">
<view v-if="errorTips" class="error_tips">{{errorTips}}</view>
<!-- <view v-if="config.announcement && errorTips == ''" class="notice">{{config.announcement}}<text @tap="close_notice"
class="close_notice">不再显示</text></view> -->
<!--留言板-->
<view v-if="showLeaveMessage" class="leave_message" style="padding-top: 100rpx;">
<form method="get" action="">
<view class="form-group">
<label for="c-name" class="control-label">{{i18n.mine.help.name}}</label>
<input type="text" style="height: 60rpx;" @input="leave_message" name="name" :value="leaveMessage.name" class="form-control" id="c-name"
:placeholder="i18n.mine.help.p0">
</view>
<view class="form-group">
<label for="c-contact" class="control-label">{{i18n.mine.help.lianxi}}</label>
<input type="text" style="height: 60rpx;" @input="leave_message" name="contact" :value="leaveMessage.contact" class="form-control" id="c-contact"
:placeholder="i18n.mine.help.p1">
</view>
<view class="form-group">
<label for="c-message" class="control-label">{{i18n.mine.help.neirong}}</label>
<textarea rows="5" @input="leave_message" name="message" :value="leaveMessage.message" class="form-control" id="c-message"
:placeholder="i18n.mine.help.p2"></textarea>
</view>
<button @tap="post_leave_message" type="button" class="leave_success">{{i18n.mine.help.queren}}</button>
</form>
</view>
<!-- 留言板end -->
<!-- 主界面 -->
<!-- #ifdef H5 -->
<view class="content_wrapper" v-if="!showLeaveMessage" :style="{height:'calc(100vh - var(--window-top) - ' + writeBottom + 'px)'}">
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="content_wrapper padding-t-100" v-if="!showLeaveMessage" :style="{height:'calc(100vh - ' + writeBottom + 'px)'}">
<!-- #endif -->
<view :style="{height:'calc(100% - ' + wrapperHeight + 'px)'}" class="chat_wrapper">
<scroll-view @scrolltoupper="wrapper_scrolltoupper" @tap="tap_scroll_view" :scroll-y="true" :scroll-with-animation="true"
:scroll-into-view="scrollIntoFooter">
<block v-for="(item, index) in messageList" :key="index">
<view class="status"><text>{{item.datetime}}</text></view>
<block v-for="message in item.data" :key="message.id">
<view v-if="message.message_type == 3" class="status"><text>{{message.message}}</text></view>
<view v-if="message.message_type != 3" class="bubble" :class="message.sender == 'me' ? 'me':'you'">
<!-- 除了商品/订单卡片和图片其他的都使用富文本 -->
<jyf-parser @imgtap="message_img_preview" :tag-style="{img:'width:60px;height:60px;'}" v-if="message.message_type == 0" :html="message.message"></jyf-parser>
<jyf-parser v-if="message.message_type == 2" :html="'<a target=_blank href=' + message.message + '>' + message.message + '</a>'"></jyf-parser>
<image @tap="preview_img(message.message)" v-if="message.message_type == 1" :src="message.message" mode="widthFix"></image>
<view v-if="message.message_type == 4 || message.message_type == 5" class="project_item">
<image :src="message.message.logo"></image>
<view class="project_item_body">
<view class="project_item_title">{{message.message.subject}}</view>
<view v-if="message.message.note" class="project_item_note">{{message.message.note}}</view>
<view class="project_item_price">
<text v-if="message.message.price">{{message.message.price}}</text>
<text v-if="message.message.number">x{{message.message.number}}</text>
</view>
</view>
</view>
</view>
</block>
</block>
<view id="wrapper_footer"></view>
</scroll-view>
</view>
<!-- 消息输入 -->
<view class="write" :style="{bottom: writeBottom + 'px',background:theme.fu_bg}">
<view class="widget_textarea" :style="'background-color: '+theme.fu_bg+';color: '+theme.text">
<textarea :adjust-position="false" :show-confirm-bar="false" :fixed="true" :focus="kefuMessageFocus" :auto-height="true"
maxlength="-1" @blur="kefu_message_blur" @input="textarea_input" :cursor-spacing="14" @focus="textarea_focus"
:value="kefuMessage" class="kefu_message"></textarea>
</view>
<view class="write_right" :style="{flex:showSendButton ? 3:2}">
<i v-if="config.toolbar && config.toolbar.expression" :style="{background: 'url(' + config.toolbar.expression.icon_image + ') no-repeat','background-size':'100% 100%'}"
class="toolbar_icon smiley" @tap="show_tool('smiley')"></i>
<button class="send_btn" @tap="send_message(kefuMessage, 0)" hover-class="send_btn_hover" v-if="showSendButton">{{i18n.mine.help.fasong}}</button>
<i class="toolbar_icon attach" :style="{background: 'url(' + attachBackground + ') no-repeat','background-size':'100% 100%'}"
@tap="show_tool('more')" v-if="!showSendButton && attachBackground"></i>
</view>
</view>
<view v-if="showTool" class="footer_div">
<!-- 表情 -->
<block v-if="showTool == 'smiley'" v-for="(item, index) in expressionData" :key="index">
<image :src="item.src" @tap="select_expression(item.title)"></image>
</block>
<!-- 表情end -->
<!-- 更多 -->
<view v-if="showTool == 'more'" class="toolbar">
<view @tap="show_select_model('order')" v-if="config.toolbar && config.toolbar.order" class="toolbar_item">
<image :src="config.toolbar.order.icon_image"></image>
<view>{{config.toolbar.order.title}}</view>
</view>
<view @tap="show_select_model('goods')" v-if="config.toolbar && config.toolbar.goods" class="toolbar_item">
<image :src="config.toolbar.goods.icon_image"></image>
<view>{{config.toolbar.goods.title}}</view>
</view>
<view @tap="upload_file" v-if="config.toolbar && config.toolbar.file" class="toolbar_item">
<image :src="config.toolbar.file.icon_image"></image>
<view>{{config.toolbar.file.title}}</view>
</view>
</view>
<!-- 更多end -->
</view>
</view>
<!-- 订单和商品选择 -->
<view v-if="selectModel" class="select_model">
<view @tap="show_select_model(false)" class="close_select">{{i18n.mine.help.guanbi}}</view>
<view class="project_list">
<radio-group v-if="selectModelData.length > 0" @change="select_done">
<label v-for="(item, index) in selectModelData" :key="index" class="project_item">
<image :src="item.logo"></image>
<view class="project_item_body">
<view class="project_item_title">{{item.subject}}</view>
<view v-if="item.note" class="project_item_note">{{item.note}}</view>
<view class="project_item_price">
<text v-if="item.price">{{item.price}}</text>
<text v-if="item.number">x{{item.number}}</text>
</view>
</view>
<radio :value="index.toString()" :checked="false" />
</label>
</radio-group>
<view class="select_model_no_data" v-if="selectModelData.length <= 0"><text>{{i18n.mine.help.nomore}}</text></view>
</view>
</view>
</view>
</view>
</template>
<script>
import Config from "./config.js"; // 本地配置数据
import jyfParser from "@/components/jyf-parser/jyf-parser"; // 富文本解析
var token = ''; // 用户身份标识
var fixedCsr = ''; // 指定客服
var innerAudioContext = null;
export default {
components: {
jyfParser
},
data() {
return {
leaveMessage: {
name: '',
contact: '',
message: ''
},
attachBackground: '',
showSendButton: false,
showTool: false,
showLeaveMessage: false,
expressionData: [],
scrollIntoFooter: '',
config: [],
tokenList: [],
kefuMessage: '',
wrapperHeight: 46,
ws: {
SocketTask: null,
Timer: null, // 计时器
ErrorMsg: [], // 发送失败的消息
MaxRetryCount: 3, // 最大重连次数
CurrentRetryCount: 0,
socketOpen: false,
pageHideCloseWs: true,
unloadCloseWs: false // ws关闭状态码兼容性不好,手动标记页面卸载关闭ws链接
},
csr: '', // 当前用户的客服代表ID
sessionId: 0, // 会话ID
sessionUser: "", // 带身份标识的用户ID
kefuMessageFocus: false,
messageList: [],
chatRecordPage: 1,
errorTips: '',
selectModel: false,
selectModelData: [],
writeBottom: 0,
lang:"",
theme: '',
}
},
onLoad(opt) {
this.errorTips = this.i18n.mine.help.p9
token = uni.getStorageSync('token')
fixedCsr = opt.fixed_csr ? opt.fixed_csr : fixedCsr
this.lang = 'en-us'
if(uni.getStorageSync('Language')){
this.lang = uni.getStorageSync('Language')
}
if(this.lang=="en-US"){
this.lang="en-us"
}
if(this.lang=="zh-CN"){
this.lang="zh-cn"
}
// 微信小程序端onshow时再链接,并在onhide时关闭链接
// #ifndef MP-WEIXIN
this.load()
// #endif
},
onShow() {
if (uni.getStorageSync("theme") == "dark") {
this.theme = this.$store.state.darkTheme
} else {
this.theme = this.$store.state.lightTheme
}
if(!this.ws.pageHideCloseWs){
this.ws.pageHideCloseWs = true;
}
// #ifdef MP-WEIXIN
this.load()
// #endif
},
onHide() {
// #ifdef MP-WEIXIN
if (this.ws.SocketTask && this.ws.socketOpen && this.ws.pageHideCloseWs) {
console.log('微信小程序页面hide主动关闭链接')
uni.closeSocket();
}
// #endif
},
onUnload() {
if (this.ws.SocketTask && this.ws.socketOpen) {
console.log('页面卸载主动关闭链接')
this.ws.unloadCloseWs = true
uni.closeSocket();
}
},
computed: {
i18n() {
return this.$t('message');
},
},
methods: {
load: function() {
var that = this
var kefu_tourists_token = '';
// 初始化请求
try {
var kefu_tourists_token = uni.getStorageSync('kefu_tourists_token');
if (!kefu_tourists_token) {
kefu_tourists_token = ''
}
} catch (e) {
console.error(e)
}
uni.request({
url: that.build_url('initialize') + "?lang=" + this.lang,
data: {
token: token,
kefu_tourists_token: kefu_tourists_token,
fixed_csr: fixedCsr
},
success: (res) => {
if (res.data.code == 1) {
// 保存游客token
if (res.data.data.token_list.kefu_tourists_token) {
try {
uni.setStorageSync('kefu_tourists_token', res.data.data.token_list.kefu_tourists_token);
} catch (e) {
console.error(e)
}
}
// 公告
var kefu_notice = ''
try {
kefu_notice = uni.getStorageSync('kefu_notice');
} catch (e) {
console.log(e)
}
if (kefu_notice == res.data.data.config.announcement) {
res.data.data.config.announcement = '';
}
// 配置
that.config = res.data.data.config;
that.tokenList = res.data.data.token_list;
// 来信提示音初始化
innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.src = that.build_url('message_prompt');
// 新消息提示
if (res.data.data.new_msg) {
that.new_message_prompt()
}
// 初始化表情
var protocol = Config.https_switch ? 'https://' : 'http://';
var expression = {};
for (let i in Config.expression) {
expression[i] = {
'src': protocol + Config.baseURL + '/assets/addons/kefu/img' + Config.expression[i].src,
'title': Config.expression[i].title
};
}
that.expressionData = expression;
// 杂项资源
that.attachBackground = that.build_url('res', '/img/more.png');
// 链接ws
that.connect_socket();
} else {
uni.showModal({
content: res.data.msg,
showCancel: false,
success: res => {
if (res.confirm) {
uni.navigateBack({
delta: 1
});
}
}
})
}
},
fail: res => {
uni.showModal({
content: that.i18n.mine.help.p21,
showCancel: false
})
that.errorTips = that.i18n.mine.help.p10;
}
});
},
switch_show_tool: function(value){
if(!value){
this.showTool = false;
this.writeBottom = 0;
} else {
this.showTool = value;
this.writeBottom = 170;
this.scroll_into_footer(200);
}
},
close_notice: function() {
uni.setStorageSync('kefu_notice', this.config.announcement);
this.config.announcement = '';
},
select_done: function(e) {
let index = e.detail.value;
var message = '';
for (let i in this.selectModelData[index]) {
message += i + '=' + this.selectModelData[index][i] + '#';
}
this.send_message(message, (this.selectModel == 'goods') ? 4 : 5);
this.selectModel = false
this.switch_show_tool(false)
},
show_select_model: function(name) {
this.selectModel = name
if (!name) {
return;
}
uni.showLoading({
title: ''
})
var data_api_url = this.config.toolbar[name].data_api
var reg = new RegExp("(^https?:\/\/)", "i");
if (data_api_url.search(reg) === -1) {
data_api_url = (Config.https_switch ? 'https://' : 'http://') + Config.baseURL + data_api_url
}
this.selectModelData = [];
uni.request({
url: data_api_url + "?lang=" + this.lang,
data: {
token: token
},
success: res => {
if (res.data.code == 1) {
this.selectModelData = res.data.data
} else {
uni.showModal({
content: res.data.msg,
showCancel: false
})
}
},
fail: res => {
uni.showModal({
content: this.i18n.mine.help.p22,
showCancel: false
})
},
complete: res => {
uni.hideLoading()
}
})
},
parseUrl: function(data) {
var reg = new RegExp("(https?:\/\/)([0-9a-z.]+)([\?0-9a-z&=]+)?(#[0-9-a-z]+)?", "g");
return data.replace(reg, '<a target="_blank" title="$&" class="link" href="$&">$&</a>');
},
message_img_preview:function(e){
this.ws.pageHideCloseWs = false; // 页面hide不关闭链接
},
preview_img: function(src) {
this.ws.pageHideCloseWs = false; // 页面hide不关闭链接
uni.previewImage({
current: src,
urls: [src]
});
},
upload_file: function() {
var that = this
that.ws.pageHideCloseWs = false; // 页面hide不关闭链接
uni.chooseImage({
success: res => {
const tempFilePaths = res.tempFilePaths;
uni.showLoading({
title: ''
})
const uploadTask = uni.uploadFile({
url: that.build_url('upload'),
// #ifdef APP-PLUS || H5
file: res.tempFiles[0],
// #endif
filePath: tempFilePaths[0],
name: 'file',
formData: {
token: token,
},
success: (res) => {
uni.hideLoading()
that.ws.pageHideCloseWs = true;
res = JSON.parse(res.data);
if (res.code == 1) {
that.send_message(res.data.url, 1)
that.switch_show_tool(false)
that.kefuMessageFocus = true;
} else {
uni.showModal({
content: res.msg,
showCancel: false
})
}
},
complete: res => {
uni.hideLoading()
}
});
// #ifndef APP-PLUS
uploadTask.onProgressUpdate((res) => {
uni.showLoading({
title: res.progress + '%'
})
});
// #endif
}
})
},
connect_socket: function() {
var that = this
if(this.ws.SocketTask && this.ws.socketOpen){
console.log('无需链接')
return ;
}
console.log(this.build_url('ws'))
// 开始链接
that.ws.SocketTask = uni.connectSocket({
url: this.build_url('ws'),
header: {
'content-type': 'application/json'
},
complete: res => {
var msg = {
msgtype : 'initialize',
data:{
new_msg:""
}
}
that.domsg(msg);
}
});
// 收到消息
uni.onSocketMessage(function(res) {
console.log("收到消息")
console.log(res)
let msg = JSON.parse(res.data)
that.domsg(msg);
})
// 链接打开
uni.onSocketOpen(function(res) {
console.log('链接已打开')
that.ws.socketOpen = true
that.ws.CurrentRetryCount = 0;
// 重新发送所有出错的消息
for (let i in that.ws.ErrorMsg) {
that.ws_send(that.ws.ErrorMsg[i]);
}
that.ws.ErrorMsg = [];
that.errorTips = '';
if (that.ws.Timer != null) {
clearInterval(that.ws.Timer);
}
// setTimeout(function(){
// var msg = {
// msgtype : 'initialize',
// data:{
// new_msg:""
// }
// }
// that.domsg(msg);
// },20)
// that.ws.Timer = setInterval(that.ws_send, 28000); //定时发送心跳
});
// 出错
uni.onSocketError(function(res) {
that.ws.socketOpen = false;
that.errorTips = that.i18n.mine.help.p11
console.log('链接出错', res)
});
// 链接关闭
uni.onSocketClose(function(res) {
// 工具上是1000,真机上测试是10000
console.log('链接已关闭', res)
that.ws.socketOpen = false;
if (res.code == 1000 || res.code == 10000 || that.ws.unloadCloseWs) {
return;
}
if (that.ws.Timer != null) {
clearInterval(that.ws.Timer);
}
that.ws.socketOpen = false;
if (that.errorTips.indexOf('重连') === -1) {
that.errorTips = that.i18n.mine.help.p12
}
if (that.ws.MaxRetryCount) {
that.ws.Timer = setInterval(that.retry_websocket, 3000); //每3秒重新连接一次
}
});
},
// 重连ws
retry_websocket: function() {
var that = this
if (that.ws.CurrentRetryCount < that.ws.MaxRetryCount) {
that.ws.CurrentRetryCount++;
that.connect_socket();
that.errorTips = that.i18n.mine.help.p13 + that.ws.CurrentRetryCount + that.i18n.mine.help.p14
} else {
if (that.ws.Timer != null) {
clearInterval(that.ws.Timer);
}
that.errorTips = that.i18n.mine.help.p15
that.ws.Timer = setInterval(that.connect_socket, 10000); //每10秒重新连接一次
}
},
// 发送ws消息
ws_send: function(message) {
var that = this
if (!message) {
message = {
c: 'Message',
a: 'ping'
};
}
console.log(that.ws)
// if (that.ws.SocketTask && that.ws.socketOpen) {
// console.log(message)
// uni.sendSocketMessage({
// data: JSON.stringify(message)
// });
// } else {
// console.log('消息发送出错', message, that.ws.SocketTask, that.ws.socketOpen)
// that.ws.ErrorMsg.push(message);
// }
uni.sendSocketMessage({
data: JSON.stringify(message)
});
},
send_message: function(message, message_type) {
var that = this
if (message == '') {
uni.showToast({
title: this.i18n.mine.help.p3,
icon:'none'
})
return;
}
// 检查 websocket 是否连接
// if (!that.ws.SocketTask || !that.ws.socketOpen) {
// uni.showToast({
// title: this.i18n.mine.help.p4,
// icon:'none'
// })
// return;
// }
if (!that.sessionId) {
uni.showToast({
title: this.i18n.mine.help.p5,
icon:'none'
})
return;
}
if (message_type == 0) {
// 处理链接
message = that.parseUrl(message);
// 处理表情
var reg = /\[(.+?)\]/g; // [] 中括号
var reg_match = message.match(reg);
if (reg_match) {
for(let i in reg_match){
var emoji_item = that.find_emoji(reg_match[i]);
message = message.replace(emoji_item.title, that.build_chat_img(emoji_item.src, '', 'emoji'));
}
}
}
var load_message = {
c: 'Message',
a: 'sendMessage',
data: {
message: message,
message_type: message_type,
session_id: that.sessionId,
modulename: 'index'
}
};
that.ws_send(load_message);
let message_data = that.messageList[that.messageList.length - 1].data;
let message_id = message_data[message_data.length - 1].id + 1;
var data = {
id: message_id,
sender: 'me',
message: message,
message_type: message_type
}
var messageListIndex = that.messageList.length - 1
if (that.messageList[messageListIndex] && that.messageList[messageListIndex].datetime == that.i18n.mine.help.p20) {
that.messageList[messageListIndex].data = that.messageList[messageListIndex].data.concat(that.format_message([
data
]))
} else {
that.messageList = that.messageList.concat({
datetime: that.i18n.mine.help.p20,
data: that.format_message([data])
});
}
that.kefuMessage = ''
that.kefu_message_change();
that.scroll_into_footer(300)
},
find_emoji:function(emoji_title){
for(let i in this.expressionData){
if(this.expressionData[i].title == emoji_title){
return this.expressionData[i];
}
}
return false;
},
build_chat_img: function(filename, facename, class_name = 'emoji') {
var protocol = Config.https_switch ? 'https://' : 'http://';
if (class_name == 'emoji') {
return '<img class="emoji" src="' + filename + '" />';
} else {
return '<img class="' + class_name + '" src="' + filename + '" />';
}
},
// 记录用户填写的留言数据,方便后续清空输入等处理
leave_message: function(e) {
if (e.currentTarget.id == 'c-name') {
this.leaveMessage.name = e.detail.value
} else if (e.currentTarget.id == 'c-contact') {
this.leaveMessage.contact = e.detail.value
} else if (e.currentTarget.id == 'c-message') {
this.leaveMessage.message = e.detail.value
}
},
post_leave_message: function() {
if (!this.leaveMessage.contact) {
uni.showToast({
title: this.i18n.mine.help.p6,
icon: 'none'
})
return false;
}
var leave_message = {
c: 'Message',
a: 'leaveMessage',
data: this.leaveMessage
};
this.ws_send(leave_message);
},
domsg: function(msg) {
var that = this
if (msg.msgtype == 'initialize') {
if (msg.data.new_msg) {
that.new_message_prompt()
}
// 分配/获取客服->获取聊天记录
var user_initialize = {
c: 'Message',
a: 'userInitialize',
data: {
fixed_csr: that.fixedCsr
}
};
that.ws_send(user_initialize);
} else if (msg.msgtype == 'user_initialize') {
// 用户客服分配结束
if (msg.code == 1) {
if (msg.data.session.user_tourists) {
that.send_message = function() {
uni.showToast({
title: this.i18n.mine.help.p7,
icon:'none'
})
}
}
that.csr = msg.data.session.csr;
that.sessionId = msg.data.session.id;
that.showLeaveMessage = false
uni.setNavigationBarTitle({
title: that.i18n.mine.help.p17 + msg.data.session.nickname + that.i18n.mine.help.p18
})
} else if (msg.code == 302) {
if (!that.csr) {
// 打开留言板
that.csr = 'none';
that.showLeaveMessage = true
uni.setNavigationBarTitle({
title: that.i18n.mine.help.p19
})
} else {
uni.showToast({
title: this.i18n.mine.help.p8,
icon:'none'
})
}
}
} else if (msg.msgtype == 'show_msg') {
uni.showToast({
title: msg.msg,
icon:'none'
})
} else if (msg.msgtype == 'leave_message') {
uni.showToast({
title: msg.msg,
icon:'none'
})
this.leaveMessage = {
name: '',
contact: '',
message: ''
}
} else if (msg.msgtype == 'clear') {
if (msg.msg) {
uni.showToast({
title: msg.msg,
icon:'none'
})
}
var clear = {
c: 'Message',
a: 'clear'
};
that.ws_send(clear);
that.retry_webSocket = function() {
clearInterval(that.ws.Timer)
};
} else if (msg.msgtype == 'offline') {
if (msg.user_id == that.csr) {
this.errorTips = this.i18n.mine.help.p16
}
} else if (msg.msgtype == 'online') {
// 来自 admin 的用户上线
if (msg.modulename == 'admin') {
if (msg.user_id == that.csr) {
this.errorTips = '';
} else if (that.csr == 'none') {
// 重新为用户分配客服代表
var user_initialize = {
c: 'Message',
a: 'userInitialize'
};
that.ws_send(user_initialize);
}
}
} else if (msg.msgtype == 'csr_change_status') {
if (that.csr == msg.data.csr) {
if (msg.data.csr_status != 3) {
that.errorTips = that.i18n.mine.help.p17 + that.get_csr_status(msg.data.csr_status)
} else {
that.errorTips = '';
}
}
} else if (msg.msgtype == 'transfer_done') {
that.csr = msg.data.csr;
uni.setNavigationBarTitle({
title: that.i18n.mine.help.p17 + msg.data.nickname + that.i18n.mine.help.p18
})
} else if (msg.msgtype == 'new_message') {
that.new_message_prompt();
if (msg.data.session_id == that.sessionId) {
var messageListIndex = that.messageList.length - 1
msg.data.id = msg.data.record_id
if (that.messageList[messageListIndex].datetime == that.i18n.mine.help.p20) {
that.messageList[messageListIndex].data = that.messageList[messageListIndex].data.concat(that.format_message([
msg.data
]))
} else {
that.messageList = that.messageList.concat({
datetime: that.i18n.mine.help.p20,
data: that.format_message([msg.data])
});
}
var load_message = {
c: 'Message',
a: 'readMessage',
data: {
record_id: msg.data.record_id
}
};
that.ws_send(load_message);
that.scroll_into_footer(800);
}
} else if (msg.msgtype == 'chat_record') {
// 下一页
that.chatRecordPage = msg.data.next_page;
for (let i in msg.data.chat_record) {
msg.data.chat_record[i].data = that.format_message(msg.data.chat_record[i].data)
}
if (msg.data.page == 1) {
that.messageList = msg.data.chat_record;
// 滑动到最后一条消息
that.scroll_into_footer(800);
} else {
for (let i = msg.data.chat_record.length - 1; i >= 0; i--) {
msg.data.chat_record[i].data.reverse()
that.messageList.unshift(msg.data.chat_record[i]);
}
}
} else if(msg.ping){
// console.log("3333")
// console.log(msg)
var message = {"pong":msg.ping}
that.ws_send(message)
}else{
console.log("3333")
console.log(msg)
}
},
get_csr_status: function(status) {
switch (parseInt(status)) {
case 0:
status = '离线';
break;
case 1:
status = '繁忙';
break;
case 2:
status = '离开';
break;
case 3:
status = '在线';
break;
case 99:
status = '断链';
break;
default:
status = '未知';
break;
}
return status;
},
build_url: function(type = 'ws', res_path) {
var that = this
var protocol = Config.https_switch ? 'https://' : 'http://';
if (type == 'ws') {
let token = '&token=' + (that.tokenList.kefu_token ? that.tokenList.kefu_token : '');
let kefu_tourists_token = that.tokenList.kefu_tourists_token ? '&kefu_tourists_token=' + that.tokenList.kefu_tourists_token : '';
protocol = parseInt(that.config.wss_switch) == 1 ? 'wss://' : 'ws://';
return (protocol + Config.baseURL + ':' + that.config.websocket_port + '/?modulename=index' + token + kefu_tourists_token).replace(
/\|/g, '~');
} else if (type == 'initialize') {
return protocol + Config.baseURL + '/addons/kefu/index/initialize?modulename=index';
} else if (type == 'upload') {
return protocol + Config.baseURL + '/addons/kefu/index/upload?modulename=index';
} else if (type == 'message_prompt') {
if (that.config.__CDN__) {
return that.config.__CDN__ + that.config.ringing;
}
return protocol + Config.baseURL + that.config.ringing;
} else if (type == 'res') {
if (that.config.__CDN__) {
return that.config.__CDN__ + '/assets/addons/kefu' + res_path;
}
return protocol + Config.baseURL + '/assets/addons/kefu' + res_path;
}
},
new_message_prompt: function() {
if (innerAudioContext) {
innerAudioContext.play();
setTimeout(function() {
innerAudioContext.stop();
}, 1000)
} else {
console.error('来信提示音播放失败!');
}
},
scroll_into_footer: function(timeout = 0) {
var that = this
setTimeout(function() {
that.scrollIntoFooter = 'wrapper_footer';
}, timeout)
that.scrollIntoFooter = '';
},
wrapper_scrolltoupper: function() {
if (!this.sessionId || this.chatRecordPage == 'done') {
return;
}
// 加载历史聊天记录
var load_message = {
c: 'Message',
a: 'chatRecord',
data: {
session_id: this.sessionId,
page: this.chatRecordPage
}
};
this.ws_send(load_message);
},
tap_scroll_view: function() {
this.switch_show_tool(false)
},
show_tool: function(name = 'smiley') {
if(this.showTool == name){
this.switch_show_tool(false)
} else {
this.switch_show_tool(name)
}
if (this.showTool) {
this.scroll_into_footer()
}
},
// 输入框聚焦
textarea_focus: function(e) {
var that = this
this.showTool = false;
that.writeBottom = e.detail.height ? e.detail.height:0;
},
// 输入框输入
textarea_input: function(e) {
this.kefuMessage = e.detail.value
this.kefu_message_change()
},
// 显示/隐藏发送按钮-调整消息记录框高度
kefu_message_change: function() {
this.showSendButton = (this.kefuMessage == '') ? false : true;
let write = uni.createSelectorQuery().select('.write');
write.fields({
size: true
}, data => {
if (data.height != this.wrapperHeight) {
this.wrapperHeight = data.height
}
}).exec()
},
// 消息框焦点
kefu_message_blur: function() {
this.kefuMessageFocus = false;
if(!this.showTool){
this.writeBottom = 0;
}
},
select_expression: function(title) {
this.kefuMessage = this.kefuMessage + title;
this.kefu_message_change();
},
format_message: function(message) {
for (let i in message) {
if (message[i].message_type == 4 || message[i].message_type == 5) {
var message_arr = message[i].message.split('#');
var message_obj = {};
for (let y in message_arr) {
let message_temp = message_arr[y].split('=');
if (typeof message_temp[1] != 'undefined') {
message_obj[message_temp[0]] = message_temp[1];
}
}
message[i].message = message_obj;
}
}
return message;
}
}
}
</script>
<style>
* {
padding: 0;
margin: 0;
}
page{
overflow: hidden;
}
.kefu_container {
height: calc(100vh - var(--window-top));
overflow: hidden;
/* background-color: #F8F8F8; */
}
.error_tips {
position: fixed;
top: calc(var(--window-top) + 140rpx);
width: 100%;
height: 80rpx;
font-size: 30rpx;
line-height: 80rpx;
background: rgba(231, 76, 60, 0.7);
z-index: 999;
color: #FFFFFF;
text-align: center;
}
.notice {
position: fixed;
top: var(--window-top);
width: 100%;
font-size: 30rpx;
line-height: 36rpx;
padding: 16rpx;
box-sizing: border-box;
background: rgba(252, 248, 227, 1);
color: #c09853;
z-index: 999;
}
.close_notice {
color: #999;
font-size: 26rpx;
text-decoration: underline;
}
.select_model {
background-color: #f2f2f2;
position: fixed;
bottom: 0rpx;
z-index: 998;
width: 100%;
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
}
.close_select {
box-sizing: border-box;
font-size: 28rpx;
padding: 16rpx 22rpx;
color: #999;
width: 100%;
text-align: right;
}
.project_list {
overflow-y: auto;
overflow-x: hidden;
height: 440rpx;
}
.project_item {
background-color: #FFFFFF;
display: flex;
align-items: center;
padding: 16rpx 8rpx 16rpx 16rpx;
margin-bottom: 16rpx;
}
.project_item:last-child {
margin-bottom: 0;
}
.project_item image {
width: 180rpx;
height: 180rpx;
min-width: 180rpx;
}
.project_item_body {
height: 170rpx;
width: 100%;
margin: 0 16rpx;
}
.project_item_title {
font-size: 30rpx;
display: block;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-height: 34rpx;
height: 68rpx;
color: #181818;
}
.project_item_note {
font-size: 26rpx;
display: block;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-height: 32rpx;
height: 64rpx;
color: #999999;
margin-top: 6rpx;
}
.project_item_price text:last-child {
margin-left: 16rpx;
color: #999999;
font-size: 26rpx;
}
.project_item_price text:first-child {
margin-left: -6rpx;
font-size: 34rpx;
color: rgba(231, 76, 60, 1);
}
.select_model_no_data {
font-size: 30rpx;
height: 100%;
background: #FFFFFF;
color: #999999;
display: flex;
align-items: center;
justify-content: center;
}
.content_wrapper {}
.chat_wrapper {
/* background-color: #F8F8F8; */
width: 100%;
box-sizing: border-box;
color: #181818;
}
.chat_wrapper scroll-view {
height: 100%;
}
.chat_wrapper .status {
position: relative;
float: right;
width: 100%;
margin-bottom: 20rpx;
text-align: center;
height: 60rpx;
line-height: 60rpx;
}
.chat_wrapper .status text {
font-size: 24rpx;
display: inline-block;
background: #ccc;
color: #fff;
border-radius: 10rpx;
padding: 6rpx 20rpx;
line-height: 24rpx;
}
.chat_wrapper .bubble {
max-width: 80%;
font-size: 32rpx;
position: relative;
display: inline-block;
clear: both;
margin-bottom: 16rpx;
padding: 24rpx 24rpx;
vertical-align: top;
border-radius: 10rpx;
}
.chat_wrapper .bubble.me {
float: right;
margin-right: 28rpx;
margin-left: 20rpx;
color: #fff;
background-color: #00b0ff;
word-wrap: break-word;
word-break: break-all;
}
.chat_wrapper .bubble.you {
float: left;
margin-right: 20rpx;
margin-left: 28rpx;
color: #181818;
background-color: #fff;
word-wrap: break-word;
word-break: break-all;
}
.chat_wrapper .bubble:before {
position: absolute;
top: 38rpx;
display: block;
width: 16rpx;
height: 12rpx;
content: '\00a0';
-webkit-transform: rotate(29deg) skew(-35deg);
transform: rotate(29deg) skew(-35deg);
}
.chat_wrapper .bubble.you:before {
left: -6rpx;
background-color: #fff;
}
.chat_wrapper .bubble.me:before {
right: -6rpx;
background-color: #00b0ff;
}
.chat_wrapper .bubble image {
max-height: 400rpx !important;
width: 200rpx;
vertical-align: bottom;
}
.chat_wrapper .bubble .emoji {
display: inline-block;
width: 120rpx;
height: 120rpx;
}
.content_wrapper .write {
box-sizing: border-box;
/* background-color: #f7f7f7; */
/* box-shadow: 0 -2rpx 0 #e5e5e5; */
width: 100%;
padding: 16rpx 12rpx;
display: flex;
align-items: center;
position: fixed;
bottom: 0rpx;
}
#wrapper_footer {
display: block;
clear: both;
}
.write_right {
flex: 2;
display: flex;
align-items: center;
justify-content: center;
}
.widget_textarea {
flex: 8;
/* background-color: #FFFFFF; */
}
.content_wrapper .toolbar_icon {
display: inline-block;
cursor: pointer;
vertical-align: middle;
width: 56rpx;
height: 56rpx;
content: '';
margin-left: 16rpx;
}
.kefu_message {
max-height: 180rpx;
min-height: 40rpx;
width: calc(100% - 36rpx);
outline: none;
border: none;
resize: none;
border-radius: 8rpx;
margin: 10rpx 0;
/* padding: 10rpx; */
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
user-select: text !important;
line-height: 44rpx;
font-size: 32rpx;
/* color: #181818; */
}
.kefu_message::-webkit-scrollbar {
width: 4rpx;
}
.kefu_message::-webkit-scrollbar-track {
/* background-color: #fff; */
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
}
.kefu_message::-webkit-scrollbar-thumb {
/* background-color: #e6e6e6; */
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
}
.footer_div {
position: fixed;
bottom: 0rpx;
/* background-color: #fff; */
box-shadow: 0 8rpx 10rpx rgba(0, 0, 0, .1);
width: 100%;
animation: show_footer_div .1s;
animation-fill-mode: forwards;
padding-top: 12rpx;
box-sizing: border-box;
height: 170px;
overflow-y: auto;
overflow-x: hidden;
}
@keyframes show_footer_div {
from {
height: 0;
}
to {
height: 170px;
}
}
.footer_div image {
height: 51rpx;
width: 51rpx;
padding: 12rpx;
}
.send_btn {
margin-left: 28rpx;
background: #00b0ff;
color: #fff;
border-color: #00b0ff;
outline: none;
padding: 10rpx 20rpx;
font-size: 24rpx;
line-height: 1.5;
border-radius: 6rpx;
}
.send_btn::after {
border: none;
}
.send_btn_hover {
background-color: #19baff;
}
.toolbar {
display: flex;
flex-wrap: wrap;
}
.toolbar_item {
margin-top: 20rpx;
width: 25%;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.toolbar_item image {
width: 60rpx;
height: 60rpx;
}
.toolbar_item view {
display: block;
width: 100%;
font-size: 26rpx;
line-height: 34rpx;
text-align: center;
}
.leave_message {
width: 90%;
display: block;
margin: 40rpx auto;
font-size: 32rpx;
}
.form-control {
margin: 20rpx 0;
width: 90%;
display: block;
padding: 12rpx 24rpx;
font-size: 28rpx;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 8rpx;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
-webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
}
.leave_success {
color: #fff;
background: #5cb85c;
border-color: #4cae4c;
margin: 0 auto;
display: inline-block;
padding: 12rpx 24rpx;
margin-bottom: 0;
font-size: 28rpx;
font-weight: 400;
line-height: 1.42;
text-align: center;
white-space: nowrap;
cursor: pointer;
border: 1px solid transparent;
border-radius: 8rpx;
}
</style>