微信小程式聊天功能 WebSocket 實現傳送文字,圖片,語音以及WebSocket 常見問題解決方案
阿新 • • 發佈:2019-02-12
如果對你有幫助,來個關注加好評,謝謝。
小程式 WebSocket 常見問題:(本文已解決的)
1.自動斷開連結,重連但是隻能存在兩個 WebSocket 的問題。
---1相容情況:1.1 正常聊天過一段時間 WebSocket 自動斷開後重新連結,並且儲存之前的聊天記錄
---1相容情況:1.2 在使用者黑屏但是沒退出小程式過一段時間時 WebSocket 自動斷開連結後重新連結,並且儲存之前的聊天記錄
---1相容情況:1.3 在聊天室的頁面,點選右上角返回按鈕,頁面會自動執行解除安裝,這個時候 WebSocket 是沒有銷燬的,再次進入時會同時存在兩個WebSocket,第三次進入就會報錯了(只能同時存在兩個 WebSocket )。
解決方案:因為需要相容的情況比較多,解決方案可詳見程式碼。具體思路就是,新增一個自動重連的開關。(必須WebSocket 銷燬以後才能新建WebSocket )根據情況判斷是否重連 WebSocket 。
2. 錄音成功,但是傳送給後端接收不到語音檔案。
解決辦法:在已錄製完指定幀大小的事件回撥函式中, 使用 wx.arrayBufferToBase64(res.frameBuffer),把得到的arrayBuffer 轉為 Base64 再傳給後端,同時設定 signType: 'BASE64'。
3.錄音傳輸給後端時,後端BASE64 解碼失敗的問題。
解決方案:與後端確認 錄音檔案的取樣率、編碼位元速率、音訊格式、幀大小是否一致。
推薦設定:
var recorder = wx.getRecorderManager(); const options = { duration: 10000, //指定錄音的時長,單位 ms sampleRate: 16000, //取樣率 numberOfChannels: 1, //錄音通道數 encodeBitRate: 24000, //編碼位元速率 format: 'mp3', //音訊格式,有效值 aac/mp3 frameSize: 12, //指定幀大小,單位 KB } recorder.start(options) //開始錄音
4.如何自動把頁面聚焦在最新的聊天資訊
效果圖:
解決方案:在聊天資訊的list 賦值成功後執行一次公共頁面聚焦的方法:
// 公共聚焦方法,方法比較笨,但是過度效果平滑流暢
bottom: function() {
var that = this;
this.setData({
scrollTop: 100000
})
},
//呼叫示例:
this.setData({
allContentList: that.data.allContentList,
})
this.bottom();
5.使用者輸入空格併發送,這個時候聊天冒泡框樣式肯定會變形,因為沒有空格行高。
解決方案: css 設定冒泡框最低高度 ---- min-height: 80rpx;(數值根據需求自定義)
6.聊天冒泡框的小三角形怎麼實現?三角形還想要給它邊框,又如何實現?
效果圖:
實現步驟:設定文字冒泡框的樣式-在文字冒泡框內新建一個盒子設定相對定位,裡面放一個em和一個span標籤,設定絕對定位,利用邊框設定透明色,示例程式碼:
<view class='new_txt_ai'>
<view class='arrow'>
<em></em>
<span></span>
</view>
<view class='ai_content'>
121(一百二十一)是 120與122之間的一個自然數。它也是奇數、合數、平方數
</view>
</view>
.new_txt_ai {
width: 460rpx;
border-radius: 7rpx;
left: 20rpx;
background-color: #fff;
position: relative;
border: 1px solid #d0d0d0;
float: left;
}
.new_txt_ai .arrow {
position: relative;
width: 40rpx;
left: -30rpx;
}
.new_txt_ai .arrow em {
position: absolute;
border-style: solid;
border-width: 15rpx;
top: 20rpx;
border-color: transparent #d0d0d0 transparent transparent;
}
.new_txt_ai .arrow span {
position: absolute;
top: 20rpx;
border-style: solid;
border-width: 15rpx;
border-color: transparent #fff transparent transparent;
left: 2rpx;
}
.ai_content {
word-break: break-all;
padding: 17rpx 30rpx 17rpx 30rpx;
}
還有很多常見問題就不多囉嗦了,直接上程式碼吧。因業務原因,傳送圖片和錄音功能的程式碼暫時註釋,註釋開啟可用。
聊天室實現效果圖:
全部相關程式碼,程式碼邏輯比較多但是思路清晰,可塑性較強。提供借鑑參考。
js原始碼
// pages/index/to_news/to_news.js
var app = getApp();
var util = require("../../utils/util.js");
var socketOpen = false;
var uuid = '',
time_ = "1";
var recorder = wx.getRecorderManager();
const innerAudioContext = wx.createInnerAudioContext() //獲取播放物件
var frameBuffer_Data, session, SocketTask, string_base64, open_num = 0, submitTo_string,
onUnload_num = 0,
autoRestart, onHide_s = false;
Page({
data: {
listCustmerServiceBanner: [],
indicatorDots: false,
autoplay: false,
interval: 5000,
duration: 1000,
user_input_text: '', //使用者輸入文字
inputValue: '',
time: '',
returnValue: '',
if_send: false,
add: true,
cross: false,
// is_my: true, text: '12432'
allContentList: [{}, {
is_ai: []
}],
num: 0
},
// 頁面載入
onLoad: function(e) {
autoRestart = true; //是否重啟
console.log('onLoad')
if (e && e.ofOperatorType) {
this.setData({
ofOperatorType: e.ofOperatorType
})
} else {
this.setData({
ofOperatorType: 2
})
}
// if (onUnload_num < 1) {
this.webSocket_open()
// }
},
onShow: function(e) {
onHide_s = false
},
onHide: function() {
autoRestart = false;
onHide_s = true
console.log('onHide')
},
onUnload: function() {
onUnload_num++;
autoRestart = false;
console.log('onUnload')
this.close();
},
// 頁面載入完成
onReady: function() {
var that = this;
this.on_recorder();
this.bottom()
},
// 建立websocket
webSocket_open: function () {
var that = this;
console.log('開始建立')
// 建立Socket
SocketTask = wx.connectSocket({
url: app.webS_url,
header: {
'content-type': 'application/json'
},
method: 'post',
success: function(res) {
console.log('WebSocket連線建立', res)
},
fail: function(err) {
wx.showToast({
title: '網路異常!',
})
console.log(err)
},
})
that.initSocket();
},
// 提交文字
submitTo: function(e) {
submitTo_string =false
console.log('提交文字')
console.log("SocketTask", SocketTask)
let that = this;
if (that.data.inputValue == "") {
return;
}
var data = {
cmd: 1,
type: 1,
signType: 'BASE64',
session: session,
body: that.data.inputValue,
}
console.log('提交文字data:', socketOpen, data)
if (socketOpen) {
// 如果打開了socket就傳送資料給伺服器
sendSocketMessage(data)
if (session != undefined && session != null) {
this.data.allContentList.push({
is_my: true,
text: this.data.inputValue
});
this.setData({
allContentList: this.data.allContentList,
if_send: false,
inputValue: ''
})
}
that.bottom()
} else {
submitTo_string=true;
this.webSocket_open()
}
},
// socket監聽事件
initSocket: function () {
var that = this;
console.log("aaa", SocketTask)
SocketTask.onOpen(res => {
socketOpen = true;
open_num++
if (session == undefined || session == null) {
// repositoryType = 1 聯通 2 移動 3 電信
// ofType int 進入客服小程式型別 1, 小程式跳轉 2,搜尋
// ofOperatorType int 否 運營商型別1,移動 2,聯通 3,電信
// wy_appid String 否 小程式appid
if (app.appid) {
var data = {
cmd: 2,
ofType: 1,
wy_appid: app.appid
}
} else {
var data = {
cmd: 2,
ofType: 2,
ofOperatorType: that.data.ofOperatorType
// ofOperatorType: 1
}
}
sendSocketMessage(data)
}
console.log('監聽 WebSocket 連線開啟事件。', res)
})
SocketTask.onClose(onClose => {
console.log('監聽 WebSocket 連線關閉事件。', onClose)
session = null;
SocketTask = false;
socketOpen = false;
// if (!autoRestart && onHide_s) {
// this.webSocket_open()
// }
// if (autoRestart) {
// this.webSocket_open()
// }
})
SocketTask.onError(onError => {
console.log('監聽 WebSocket 錯誤。錯誤資訊', onError)
session = null;
})
SocketTask.onMessage(onMessage => {
var onMessage_data = JSON.parse(onMessage.data);
console.log("onMessage:", onMessage_data)
// if (onMessage_data == 'session為空') {
// if (submitTo_string) {
// console.log('submitTo_string2222222222')
// that.submitTo()
// }
// return;
// }
if (onMessage_data.minipTitle) {
wx.setTopBarText({
text: onMessage_data.minipTitle,
})
}
let is_ai_arr = onMessage_data.body;
// 登入。預設傳送一條訊息給使用者展示,不展示已解決未解決
if (onMessage_data.cmd == 3) {
that.session_pro = new Promise(function (resolve) {
session = onMessage_data.session;
if (submitTo_string) {
console.log('submitTo_string11111111')
that.submitTo()
}
resolve(session)
})
var messageTime = util.formatTime(onMessage_data.messageTime);
// if (open_num < 2){
if (is_ai_arr.length == 1) {
that.data.allContentList.push({
is_ai: is_ai_arr,
solve_show: false,
show_answer: true,
messageTime: messageTime
});
} else {
console.log('is_ai_arr:', is_ai_arr)
that.data.allContentList.push({
is_ai: is_ai_arr,
show_answer: false,
solve_show: false,
messageTime: messageTime
});
}
// }
this.setData({
listCustmerServiceBanner: onMessage_data.listCustmerServiceBanner,
staffServicePhone: onMessage_data.staffServicePhone,
allContentList: that.data.allContentList
})
} else {
// 正常接收訊息
uuid = onMessage_data.messageRecordUuid;
var messageTime;
time_ = onMessage_data.messageTime;
if (time_ + 1000 * 60 * 10 > onMessage_data.messageTime) {
messageTime = 0;
} else {
messageTime = util.formatTime(onMessage_data.messageTime);
}
let arr_list = that.data.allContentList
if (is_ai_arr.length == 1) {
arr_list.push({
show_answer: true,
is_ai: is_ai_arr,
messageTime: messageTime,
solve_show: true,
no_problem: false,
yse_problem: false
});
} else {
arr_list.push({
show_answer: false,
is_ai: is_ai_arr,
messageTime: messageTime,
solve_show: true,
no_problem: false,
yse_problem: false
});
}
that.setData({
allContentList: arr_list
})
}
that.bottom();
})
},
// 點選輪播圖
swiper_item_click: function (e) {
var id = e.target.id
console.log(id);
var item_banners = this.data.listCustmerServiceBanner[id];
var page = item_banners.page;
// 型別1、自己小程式、2、其它小程式 3、H5
switch (item_banners.type) {
case 1:
wx.navigateTo({
url: page,
})
break;
case 2:
wx.navigateToMiniProgram({
appId: item_banners.appid,
path: page,
extraData: {},
envVersion: 'release',
success(res) {
// 開啟成功
}
})
break;
case 3:
wx.navigateTo({
url: web + '?url=' + page,
})
break;
}
},
// 關閉
close: function (e) {
if (SocketTask) {
SocketTask.close(function (close) {
console.log('關閉 WebSocket 連線。', close)
})
}
},
// 解決問題
is_problem: function(e) {
console.log('e.target.id', e.currentTarget.dataset.id)
console.log('item', e.currentTarget.dataset.item)
var id = e.currentTarget.dataset.id;
var item = e.currentTarget.dataset.item;
// id=1 已解決 0 未解決
var yse_problem = this.data.allContentList[item].yse_problem;
var no_problem = this.data.allContentList[item].no_problem;
if (yse_problem || no_problem) {
console.log(12)
return
} else {
if (id == 1) {
this.setData({
['allContentList[' + item + '].yse_problem']: true
})
} else if (id == 0) {
this.setData({
['allContentList[' + item + '].no_problem']: true
})
}
console.log(this.data.allContentList[item].yse_problem, this.data.allContentList[item].no_problem)
this.bottom();
}
var url = app.httpUrl + '/v1/userFeedbackResult.do'
var data = {
'session': app.http_session,
'type': id,
'uuid': uuid
}
console.log('userFeedbackResult提交的資料:', data)
util.request(url, 'POST', data, '', function(res) {
console.log('userFeedbackResult返回的資料:', res.data)
}, function(err) {
console.log(err)
})
},
// 跳轉小程
minip: function(e) {
console.log(e)
wx.navigateToMiniProgram({
appId: e.target.dataset.appid,
path: e.target.dataset.path,
extraData: {},
envVersion: 'develop',
success(res) {
// 開啟成功
}
})
},
// 跳轉WEB
link: function(e) {
console.log(e.target.id)
wx.navigateTo({
url: '../web/web?link=' + e.target.id,
})
},
// 點選加號
add_icon_click: function(e) {
console.log(e.target.id)
// e.target.id == 1 點選加號 ==2 點選 X
if (e.target.id == 2) {
this.setData({
add: true,
cross: false,
input_bottom: 0
})
} else if (e.target.id == 1) {
this.setData({
add: false,
cross: true,
input_bottom: 240
})
}
},
// 自動新增問題答案
add_question: function(e) {
var that = this;
let answer = e.currentTarget.dataset.answer;
let messageTime = e.currentTarget.dataset.messagetime;
let question = e.currentTarget.dataset.question;
console.log('question:', question, 'answer:', answer, 'messageTime', messageTime);
this.data.allContentList.push({
is_my: true,
text: question
});
this.setData({
allContentList: this.data.allContentList,
if_send: false,
inputValue: ''
})
that.bottom();
setTimeout(function() {
that.data.allContentList.push({
is_ai: [{
answer: answer,
type: 1
}],
solve_show: true,
show_answer: true,
messageTime: false,
text: question
});
that.setData({
allContentList: that.data.allContentList,
})
that.bottom();
}, 1000)
},
// 撥打電話
phone_click: function() {
var that = this;
wx.showModal({
title: '',
content: '是否撥打' + that.data.staffServicePhone + '人工客服電話',
success: function(res) {
if (res.confirm) {
wx.makePhoneCall({
phoneNumber: that.data.staffServicePhone //僅為示例,並非真實的電話號碼
})
} else if (res.cancel) {
console.log('使用者點選取消')
}
}
})
},
// 輸入框
bindKeyInput: function(e) {
console.log(e.detail.value)
if (e.detail.value == "") {
this.setData({
if_send: false,
inputValue: e.detail.value
})
} else {
this.setData({
if_send: true,
inputValue: e.detail.value
})
}
},
// 獲取到焦點
focus: function(e) {
var that = this;
console.log(e.detail.height)
this.setData({
focus: true,
add: true,
cross: false,
input_bottom: e.detail.height
})
},
// 失去焦點
no_focus: function(e) {
if (this.data.cross) {
this.setData({
focus: false,
input_bottom: 240,
})
} else {
this.setData({
focus: false,
input_bottom: 0
})
}
},
// 獲取hei的id節點然後螢幕焦點調轉到這個節點
bottom: function() {
var that = this;
this.setData({
scrollTop: 100000
})
},
hide_bg: function() {
this.setData({
block: false
})
},
// 點選錄音事件
my_audio_click: function(e) {
console.log('my_audio_click執行了', e)
var index = e.currentTarget.dataset.id;
console.log('url地址', this.data.allContentList[index].audio);
innerAudioContext.src = this.data.allContentList[index].audio
innerAudioContext.seek(0);
innerAudioContext.play();
},
// 手指點選錄音
voice_ing_start: function() {
var that = this;
this.setData({
voice_ing_start_date: new Date().getTime(), //記錄開始點選的時間
})
const options = {
duration: 10000, //指定錄音的時長,單位 ms
sampleRate: 16000, //取樣率
numberOfChannels: 1, //錄音通道數
encodeBitRate: 24000, //編碼位元速率
format: 'mp3', //音訊格式,有效值 aac/mp3
frameSize: 12, //指定幀大小,單位 KB
}
recorder.start(options) //開始錄音
this.animation = wx.createAnimation({
duration: 1200,
}) //播放按鈕動畫
that.animation.scale(0.8, 0.8); //還原
that.setData({
spreakingAnimation: that.animation.export()
})
},
// 錄音監聽事件
on_recorder: function() {
var that = this;
recorder.onStart((res) => {
console.log('開始錄音');
})
recorder.onStop((res) => {
console.log('停止錄音,臨時路徑', res.tempFilePath);
// _tempFilePath = res.tempFilePath;
var x = new Date().getTime() - this.data.voice_ing_start_date
if (x > 1000) {
that.data.allContentList.push({
is_my: true,
audio: res.tempFilePath,
length: x / 1000 * 30
});
that.setData({
allContentList: that.data.allContentList
})
}
})
recorder.onFrameRecorded((res) => {
var x = new Date().getTime() - this.data.voice_ing_start_date
if (x > 1000) {
console.log('onFrameRecorded res.frameBuffer', res.frameBuffer);
string_base64 = wx.arrayBufferToBase64(res.frameBuffer)
// console.log('string_base64--', wx.arrayBufferToBase64(string_base64))
if (res.isLastFrame) {
that.session_pro.then(function(session) {
var data = {
audioType: 3,
cmd: 1,
type: 2,
signType: 'BASE64',
session: session,
body: string_base64,
}
console.log('that.data.allContentList', that.data.allContentList)
sendSocketMessage(data)
})
// 進行下一步操作
} else {
that.session_pro.then(function(session) {
var data = {
cmd: 1,
audioType: 2,
type: 2,
signType: 'BASE64',
session: session,
body: string_base64
}
console.log('錄音上傳的data:', data)
sendSocketMessage(data)
})
}
}
})
},
// 手指鬆開錄音
voice_ing_end: function() {
var that = this;
that.setData({
voice_icon_click: false,
animationData: {}
})
this.animation = "";
var x = new Date().getTime() - this.data.voice_ing_start_date
if (x < 1000) {
console.log('錄音停止,說話小於1秒!')
wx.showModal({
title: '提示',
content: '說話要大於1秒!',
})
recorder.stop();
} else {
// 錄音停止,開始上傳
recorder.stop();
}
},
// 點選語音圖片
voice_icon_click: function() {
this.setData({
voice_icon_click: !this.data.voice_icon_click
})
},
})
//通過 WebSocket 連線傳送資料,需要先 wx.connectSocket,並在 wx.onSocketOpen 回撥之後才能傳送。
function sendSocketMessage(msg) {
var that = this;
if (app.http_session != "") {
msg.http_session = app.http_session
console.log('通過 WebSocket 連線傳送資料', JSON.stringify(msg))
SocketTask.send({
data: JSON.stringify(msg)
}, function(res) {
console.log('已傳送', res)
})
} else {
app.promise.then(function(http_session) {
msg.http_session = http_session;
console.log('通過 WebSocket 連線傳送資料', JSON.stringify(msg));
SocketTask.send({
data: JSON.stringify(msg)
}, function(res) {
console.log('已傳送', res);
})
})
}
}
wxml原始碼
<!-- <button bindtap='close'>關閉</button>
<button bindtap='open'>開啟</button> -->
<!-- <swiper indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}">
<block wx:for="{{listCustmerServiceBanner}}" wx:key=''>
<swiper-item>
<image src="{{item.picUrl}}" bindtap='swiper_item_click' id='{{index}}' class="slide-image" />
</swiper-item>
</block>
</swiper> -->
<view class='page_bg' wx:if='{{block}}' bindtap='hide_bg' />
<view class='btn_bg' wx:if='{{block}}'>
<view wx:for="{{link_list}}" wx:key='index'>
<button class="sp_tit" id='{{index}}' bindtap='list_item'>檢視詳情 {{item}} </button>
</view>
</view>
<scroll-view class="history" scroll-y="true" scroll-with-animation scroll-top="{{scrollTop}}">
<block wx:key="{{index}}" wx:for="{{allContentList}}">
<block wx:if="{{item.is_my}}">
<view class='my_right new_txt'>
<view class='time' wx:if='{{item.messageTime&&item.messageTime!=0}}'>
{{item.messageTime}}
</view>
<view class='p_r page_r' style='margin-right: 25rpx;' wx:if='{{item.text}}'>
<view class='new_txt'>
<view class='new_txt_my'>
<view class='arrow'>
<em></em>
<span></span>
</view>
<text decode="true">{{item.text}}</text>
</view>
</view>
<open-data class='new_img' type="userAvatarUrl"></open-data>
</view>
<view class='p_r page_r' style='margin-right: 25rpx;' wx:if='{{item.audio}}' bindtap='my_audio_click' data-id='{{index}}'>
<view class='new_txt_my_2' style=' width:{{item.length}}px'>
<image class='my_audio' src='/images/yuyin_icon.png'></image>
</view>
<span class='_span'></span>
<open-data class='new_img' type="userAvatarUrl"></open-data>
</view>
</view>
</block>
<!-- <view class='you_left' id='id_{{allContentList.length}}'> -->
<block wx:if="{{item.is_ai&&item.is_ai!=''}}">
<view class='you_left' style='width:100%;' id='id_{{allContentList.length}}' wx:key="{{index}}">
<view class='time' wx:if='{{item.messageTime}}'>
{{item.messageTime}}
</view>
<view class='p_r' style='margin-left: 20rpx;'>
<image class='new_img' src='/images/top_img.png'></image>
<view class='new_txt'>
<view class='new_txt_ai'>
<view class='arrow'>
<em></em>
<span></span>
</view>
<!-- {{item.text}} -->
<view class='ai_content'>
<block wx:for='{{item.is_ai}}' wx:for-item='itt' wx:for-index='indexi' wx:key=''>
<text wx:if='{{itt.type=="1"&&item.show_answer}}' decode="true" >{{itt.answer}}</text>
<block wx:if='{{itt.type=="1"&&!item.show_answer}}'>
<text decode="true" wx:if='{{indexi==0}}'>{{itt.answer}}</text>
<view decode="true" style='color:#0000EE' bindtap='add_question' data-messagetime='{{itt.messageTime}}' data-question='{{itt.question}}' data-answer='{{itt.answer}}'>· {{itt.question}}?</view>
</block>
<text wx:if='{{item.type=="2"}}' decode="true" style='color:#0000EE' bindtap='link' id='{{item.link}}'>{{item.text}}</text>
<image wx:if='{{item.type=="3"}}' style='width:{{item.w}}rpx;height:{{item.h}}rpx;' src='{{item.src}}'></image>
<text wx:if='{{item.type=="10"}}' decode="true" data-path='{{item.path}}' data-appid='{{item.appId}}' bindtap='minip'>{{item.text}}</text>
</block>
</view>
<!-- <view class='is_ai_btn' wx:if='{{item.solve_show&&item.is_ai[0].answer!="我不明白"}}'>
<view bindtap='is_problem' data-id='1' data-item='{{index}}' style=' {{item.yse_problem?"color: red;":""}}'>
<image src='{{item.yse_problem?"/images/in_zan.png":"/images/zan.png"}}' /> 已解決
</view>
<view bindtap='is_problem' data-id='0' data-item='{{index}}' class='two' style=' {{item.no_problem?"color: #00B1FF;":""}}'>
<image src='{{item.no_problem?"/images/in_zan_no.png":"/images/zan_no.png"}}' /> 未解決
</view>
</view> -->
<view class='yes_problem_log' wx:if="{{item.yse_problem&&item.solve_show}}" style=''>感謝您的反饋,我們會再接再厲!</view>
<view class='yes_problem_log' style='color:#32CF3C' wx:if="{{item.no_problem&&item.solve_show}}" bindtap='phone_click'>撥打人工客服</view>
</view>
</view>
</view>
</view>
</block>
</block>
</scroll-view>
<!-- 遮罩 -->
<view class='zezhao' wx:if='{{cross}}' bindtap='add_icon_click' id='2'></view>
<!-- 輸入框 -->
<view class='{{cross?"in_voice_icon":""}}'>
<view class="sendmessage" wx:if='{{!cross}}' style='bottom:{{input_bottom}}px'>
<input type="text" style='{{focus?"border-bottom: 1px solid #88DD4B;":""}}' adjust-position='{{false}}' cursor-spacing='5' bindinput="bindKeyInput" value='{{inputValue}}' focus='{{focus}}' bindblur='no_focus' bindfocus="focus" confirm-type="done" placeholder="請輸入您要諮詢的問題"/>
<button wx:if='{{if_send&&inputValue!=""}}' bindtap="submitTo" class='user_input_text'>傳送</button>
<image class='add_icon' bindtap='add_icon_click' id='1' wx:if='{{add&&!if_send&&inputValue==""}}' src='/images/jia_img.png'></image>
<image class='add_icon' bindtap='add_icon_click' id='2' wx:if='{{cross}}' src='/images/audio/cross37.png'></image>
</view>
<view wx:if='{{cross}}' class='item' bindtap='phone_click'>
<image class='img' src='/images/yuyin_icon.png'></image>
<view class='text'>人工客服</view>
</view>
</view>
<!-- <view class='zezhao' wx:if='{{add_icon_click}}' bindtap='add_icon_click'></view> -->
<!-- <view class='in_voice_icon'>
<view class="sendmessage_2">
<input type="text" bindinput="bindKeyInput" adjust-position='{{false}}' value='{{inputValue}}' focus='{{focus}}' bindfocus="focus" confirm-type="done" placeholder="" />
<image class='add_icon' bindtap='add_icon_click' src='/images/audio/cross37.png'></image>
</view>
<view class='item' bindtap='phone_click'>
<image class='img' src='/images/yuyin_icon.png'></image>
<view class='text'>人工客服</view>
</view>
</view> -->
wxss原始碼
page {
background-color: #f2f2f2;
height: 100%;
padding: 0 auto;
margin: 0 auto;
}
swiper {
height: 180rpx;
}
swiper swiper-item .slide-image {
width: 100%;
height: 180rpx;
}
.jia_img {
height: 80rpx;
width: 90rpx;
}
.time {
text-align: center;
padding: 5rpx 20rpx 5rpx 20rpx;
border-radius: 10rpx;
display: block;
height: 38rpx;
line-height: 38rpx;
position: relative;
margin: 0 auto;
margin-bottom: 20rpx;
width: 90rpx;
color: white;
font-size: 26rpx;
background-color: #dedede;
}
.tab {
bottom: 120rpx;
}
.tab_1 {
position: fixed;
bottom: 50rpx;
width: 200rpx;
font-size: 26rpx;
left: 50%;
margin-left: -45rpx;
height: 100rpx;
}
.tab_2 {
right: 30rpx;
position: fixed;
}
/* 聊天 */
.my_right {
float: right;
margin-top: 30rpx;
position: relative;
}
.my_audio {
height: 60rpx;
width: 60rpx;
z-index: 2;
position: relative;
top: 10rpx;
left: 20rpx;
}
.you_left {
margin-top: 30rpx;
float: left;
position: relative;
padding-left: 5rpx;
}
.new_img {
width: 85rpx;
height: 85rpx;
overflow: hidden;
}
.page_r {
float: right;
}
.new_txt {
min-width: 380rpx;
width: 460rpx;
word-break: break-all;
}
.new_txt_my {
border-radius: 7rpx;
background: #9fe75a;
position: relative;
right: 30rpx;
min-height: 50rpx;
padding: 17rpx 30rpx 17rpx 30rpx;
float: right;
border: 1px solid #d0d0d0;
}
.new_txt_my .arrow {
position: absolute;
z-index: 2;
width: 40rpx;
right: -38rpx;
}
.new_txt_my .arrow em {
position: absolute;
border-style: solid;
border-width: 15rpx;
border-color: transparent transparent transparent #d0d0d0;
top: 1rpx;
}
.new_txt_my .arrow span {
position: absolute;
top: 5rpx;
border-style: solid;
border-width: 15rpx;
border-color: transparent transparent transparent #9fe75a;
}
.new_txt_my_2 {
word-break: break-all;
border-radius: 7rpx;
background: #9fe75a;
min-width: 330rpx;
max-width: 530rpx;
padding: 17rpx 30rpx 17rpx 30rpx;
float: right;
}
.new_txt_ai {
width: 460rpx;
border-radius: 7rpx;
left: 20rpx;
background-color: #fff;
position: relative;
border: 1px solid #d0d0d0;
float: left;
}
.new_txt_ai .arrow {
position: relative;
width: 40rpx;
left: -30rpx;
}
.new_txt_ai .arrow em {
position: absolute;
border-style: solid;
border-width: 15rpx;
top: 20rpx;
border-color: transparent #d0d0d0 transparent transparent;
}
.new_txt_ai .arrow span {
position: absolute;
top: 20rpx;
border-style: solid;
border-width: 15rpx;
border-color: transparent #fff transparent transparent;
left: 2rpx;
}
.ai_content {
word-break: break-all;
padding: 17rpx 30rpx 17rpx 30rpx;
}
.sanjiao {
top: 25rpx;
position: relative;
width: 0px;
height: 0px;
border-width: 15rpx;
border-style: solid;
}
.my {
border-color: transparent transparent transparent #9fe75a;
}
.you {
border-color: transparent #fff transparent transparent;
}
._span {
border-color: #fff transparent transparent;
top: -17px;
}
.is_ai_btn {
border-radius: 0 0 7px 7px;
border-top: 1px solid #d0d0d0;
background: white;
position: relative;
bottom: 0;
left: 0;
width: 100%;
height: 80rpx;
line-height: 80rpx;
display: flex;
flex-direction: row;
text-align: center;
}
.is_ai_btn view {
width: 50%;
}
.is_ai_btn image {
width: 32rpx;
position: relative;
top: 4rpx;
height: 32rpx;
}
.is_ai_btn .two {
border-left: 1px solid #d0d0d0;
}
.yes_problem_log {
border-top: 1px solid #d0d0d0;
height: 80rpx;
text-align: center;
line-height: 80rpx;
}
.voice_icon {
width: 60rpx;
height: 60rpx;
margin: 0 auto;
padding: 10rpx 10rpx 10rpx 10rpx;
}
.add_icon {
width: 70rpx;
height: 70rpx;
margin: 0 auto;
padding: 20rpx 10rpx 10rpx 15rpx;
}
.voice_ing {
width: 90%;
height: 75rpx;
line-height: 85rpx;
text-align: center;
border-radius: 15rpx;
border: 1px solid #d0d0d0;
}
.zezhao {
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 100%;
background: rgba(0, 0, 0, 0.5);
}
.in_voice_icon {
z-index: 3;
left: 0;
bottom: 0;
width: 100%;
position: absolute;
height: 500rpx;
background: #f8f8f8;
}
.in_voice_icon .item {
position: relative;
left: 50%;
margin-left: -60rpx;
margin-top: 180rpx;
text-align: center;
width: 120rpx;
}
.in_voice_icon .img {
width: 120rpx;
height: 120rpx;
border-radius: 15rpx;
}
.in_voice_icon .text {
font-size: 32rpx;
margin-top: 20rpx;
background: white;
width: 200rpx;
margin-left: -40rpx;
border-radius: 15rpx;
height: 80rpx;
line-height: 80rpx;
}
.sendmessage {
width: 100%;
z-index: 2;
display: flex;
position: fixed;
bottom: 0px;
background-color: #f8f8f8;
flex-direction: row;
height: 100rpx;
}
.sendmessage input {
width: 78%;
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
margin-top: 10rpx;
margin-left: 20rpx;
border-bottom: 1px solid #d0d0d0;
padding-left: 20rpx;
}
.sendmessage button {
border: 1px solid white;
width: 18%;
height: 80rpx;
background: #0c0;
color: white;
line-height: 80rpx;
margin-top: 10rpx;
font-size: 28rpx;
}
.hei {
height: 20rpx;
}
.history {
/* height: 73%; */
height: 88%;
display: flex;
font-size: 14px;
line-height: 50rpx;
position: relative;
top: 20rpx;
}
.icno_kf {
position: fixed;
bottom: 160rpx;
margin: 0 auto;
text-align: center;
left: 50%;
margin-left: -40rpx;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
引用的util檔案原始碼:
// 手機號碼驗證
function isUnicoms(mobileNo) {
//移動:134(0 - 8) 、135、136、137、138、139、147、150、151、152、157、158、159、178、182、183、184、187、188、198
//聯通:130、131、132、145、155、156、175、176、185、186、166
//電信:133、153、173、177、180、181、189、199
// 1,移動 2,聯通 3,電信
var move = /^((134)|(135)|(136)|(137)|(138)|(139)|(147)|(150)|(151)|(152)|(157)|(158)|(159)|(178)|(182)|(183)|(184)|(187)|(188)|(198))\d{8}$/g;
var link = /^((130)|(131)|(132)|(155)|(156)|(145)|(185)|(186)|(176)|(175)|(170)|(171)|(166))\d{8}$/g;
var telecom = /^((133)|(153)|(173)|(177)|(180)|(181)|(189)|(199))\d{8}$/g;
if (move.test(mobileNo)) {
return '1';
} else if (link.test(mobileNo)) {
return '2';
} else if (telecom.test(mobileNo)) {
return '3';
} else {
return '非三網號段';
}
}
// 網路請求
function request(url, method, data, message, _success, _fail) {
wx.showNavigationBarLoading()
if (message != "") {
wx.showLoading({
title: message
})
}
wx.request({
url: url,
data: data,
header: {
'content-type': 'application/x-www-form-urlencoded'
},
method: method,
success: function (res) {
_success(res)
wx.hideNavigationBarLoading()
if (message != "") {
wx.hideLoading()
}
},
fail: function (err) {
if (err) {
_fail(err)
}
wx.hideNavigationBarLoading()
if (message != "") {
wx.hideLoading()
}
},
})
}
//上傳語音
function up_audio(url, audioSrc,name, data, _succ, _fail) {
const uploadTask = wx.uploadFile({
url: url, //僅為示例,非真實的介面地址
filePath: audioSrc,
name: name,
formData: data,
header: {
"content-type": "multipart/form-data"
},
success: function (res) {
_succ(res)
},fail:function(err){
_fail(err)
}
})
uploadTask.onProgressUpdate((res) => {
console.log('audio上傳進度', res.progress)
console.log('audio已經上傳的資料長度', res.totalBytesSent)
console.log('audio預期需要上傳的資料總長度', res.totalBytesExpectedToSend)
})
}
function formatTime(unixtime) {
var dateTime = new Date(parseInt(unixtime))
var year = dateTime.getFullYear();
var month = dateTime.getMonth() + 1;
var day = dateTime.getDate();
var hour = dateTime.getHours();
var minute = dateTime.getMinutes();
var second = dateTime.getSeconds();
var now = new Date();
var now_new = Date.parse(now.toDateString()); //typescript轉換寫法
var milliseconds = now_new - dateTime;
var timeSpanStr = hour + ':' + minute;
// var timeSpanStr = year + '-' + month + '-' + day + ' ' + hour + ':' + minute;
return timeSpanStr;
}
module.exports = {
request: request,
isUnicoms: isUnicoms,
formatTime:formatTime,
up_audio: up_audio
}
引用的app.js
//app.js
var util = require('utils/util.js');
App({
onLaunch: function () {
var that = this;
that.http_session = '';
return that.promise = new Promise(function (resolve) {
// that.webS_url = 'ws://192.168.199.147:7041';//填你請求的地址
// that.httpUrl = 'http://192.168.199.147:7051'//填你請求的測試
wx.login({
success: function (res) {
var data = {
code: res.code
}
if (res.code) {
//發起網路請求
var url = that.httpUrl + '/v1/user/login.do';
util.request(url, 'POST', data, '', function (res) {
console.log(res);
that.http_session = res.data.body;
resolve(that.http_session);
}, function (err) {
console.log(err);
})
} else {
console.log('登入失敗!' + res.errMsg)
}
}
});
})
},
// 提交formid
form_id_bg: function (formId) {
console.log('form_id_bg執行了')
let url = this.httpUrl + '/v1/formid/saveFormid.do';
this.promise.then(function (http_session) {
let data = {
session: http_session,
// minipid: '10000',
formId: formId
}
util.request(url, 'post', data, '', function (res) {
})
})
},
onShow: function (even) {
var e;
// if (even.referrerInfo.extraData && even.referrerInfo.extraData.foo) {
// e = even.referrerInfo.extraData.foo
// }
if (e && e.appid) {
this.appid = e.appid;
}
}
})