Browse Source

修复ios h5录音失败的bug

master
xsx 1 year ago
parent
commit
ba439f28b3
  1. 6
      im-uniapp/App.vue
  2. 8
      im-uniapp/common/recorder-app.js
  3. 52
      im-uniapp/common/recorder-h5.js
  4. 23
      im-uniapp/components/chat-message-item/chat-message-item.vue
  5. 23
      im-uniapp/components/chat-record/chat-record.vue
  6. 203
      im-uniapp/pages/chat/chat-box.vue

6
im-uniapp/App.vue

@ -378,6 +378,12 @@ export default {
//
let loginInfo = uni.getStorageSync("loginInfo")
this.refreshToken(loginInfo).then(() => {
// #ifdef H5
//
uni.switchTab({
url: "/pages/chat/chat"
})
// #endif
//
this.init();
this.closeSplashscreen(0);

8
im-uniapp/common/recorder-app.js

@ -3,6 +3,9 @@ import UNI_APP from '@/.env.js';
const rc = uni.getRecorderManager();
// 录音开始时间
let startTime = null;
let checkIsEnable = ()=>{
return true;
}
let start = () => {
return new Promise((resolve, reject) => {
@ -20,10 +23,6 @@ let start = () => {
})
}
let pause = () => {
rc.stop();
}
let close = () => {
rc.stop();
}
@ -61,6 +60,7 @@ let upload = () => {
}
export {
checkIsEnable,
start,
close,
upload

52
im-uniapp/common/recorder-h5.js

@ -4,26 +4,39 @@ let rc = null;
let duration = 0;
let chunks = [];
let stream = null;
let startTime = null;
let checkIsEnable = () => {
if (origin.indexOf('https') === -1 && origin.indexOf('localhost') === -1 &&
origin.indexOf('127.0.0.1') === -1) {
uni.showToast({
title: '请在https环境中使用录音功能',
icon: 'error'
})
return false;
}
if (!navigator.mediaDevices || !window.MediaRecorder) {
uni.showToast({
title: '当前浏览器不支持录音',
icon: 'error'
})
return false;
}
return true;
}
let start = () => {
return navigator.mediaDevices.getUserMedia({ audio: true }).then(audioStream => {
const startTime = new Date().getTime();
console.log("start record")
startTime = new Date().getTime();
chunks = [];
stream = audioStream;
rc = new MediaRecorder(stream)
rc.ondataavailable = (e) => {
console.log("ondataavailable")
chunks.push(e.data)
}
rc.onstop = () => {
duration = (new Date().getTime() - startTime) / 1000;
console.log("时长:", duration)
}
rc.start()
})
}
let close = () => {
console.log("stream:", stream)
stream.getTracks().forEach((track) => {
track.stop()
})
@ -33,11 +46,23 @@ let close = () => {
let upload = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
rc.ondataavailable = (e) => {
console.log("ondataavailable:",e.data)
console.log("size:",e.data.size)
console.log("type:",e.data.type)
chunks.push(e.data)
}
rc.onstop = () => {
if(!chunks[0].size){
chunks = [];
return;
}
duration = (new Date().getTime() - startTime) / 1000;
console.log("时长:", duration)
console.log("上传,chunks:", chunks.length)
const newbolb = new Blob(chunks, { 'type': 'audio/mpeg' });
const name = new Date().getDate() + '.mp3';
const file = new File([newbolb], name)
console.log("upload")
uni.uploadFile({
url: UNI_APP.BASE_URL + '/file/upload',
header: {
@ -62,11 +87,12 @@ let upload = () => {
reject(e);
}
})
}, 100)
}
})
}
export {
checkIsEnable,
start,
close,
upload

23
im-uniapp/components/chat-message-item/chat-message-item.vue

@ -65,12 +65,12 @@
</view>
</long-press-menu>
<view class="chat-msg-status" v-if="!isAction">
<text class="chat-readed" v-show="msgInfo.selfSend && !msgInfo.groupId
<text class="chat-readed" v-if="msgInfo.selfSend && !msgInfo.groupId
&& msgInfo.status == $enums.MESSAGE_STATUS.READED">已读</text>
<text class="chat-unread" v-show="msgInfo.selfSend && !msgInfo.groupId
<text class="chat-unread" v-if="msgInfo.selfSend && !msgInfo.groupId
&& msgInfo.status != $enums.MESSAGE_STATUS.READED">未读</text>
</view>
<view class="chat-receipt" v-show="msgInfo.receipt" @click="onShowReadedBox">
<view class="chat-receipt" v-if="msgInfo.receipt" @click="onShowReadedBox">
<text v-if="msgInfo.receiptOk" class="tool-icon iconfont icon-ok"></text>
<text v-else>{{ msgInfo.readedCount }}人已读</text>
</view>
@ -125,14 +125,16 @@ export default {
this.innerAudioContext = uni.createInnerAudioContext();
let url = JSON.parse(this.msgInfo.content).url;
this.innerAudioContext.src = url;
console.log(url);
this.innerAudioContext.onEnded((e) => {
console.log('停止')
this.audioPlayState = "STOP"
this.emit();
})
this.innerAudioContext.onError((e) => {
this.audioPlayState = "STOP"
console.log("播放音频出错");
console.log(e)
this.emit();
});
}
if (this.audioPlayState == 'STOP') {
@ -145,6 +147,7 @@ export default {
this.innerAudioContext.play();
this.audioPlayState = "PLAYING"
}
this.emit();
},
onSelectMenu(item) {
this.$emit(item.key.toLowerCase(), this.msgInfo);
@ -158,6 +161,16 @@ export default {
},
onShowReadedBox() {
this.$refs.chatGroupReaded.open();
},
emit(){
this.$emit("audioStateChange",this.audioPlayState,this.msgInfo);
},
stopPlayAudio(){
if (this.innerAudioContext) {
this.innerAudioContext.stop();
this.innerAudioContext = null;
this.audioPlayState = "STOP"
}
}
},
computed: {
@ -262,6 +275,7 @@ export default {
.chat-msg-bottom {
display: inline-block;
padding-right: 80rpx;
margin-top: 5rpx;
.chat-msg-text {
position: relative;
@ -289,7 +303,6 @@ export default {
border-color: $im-bg transparent transparent;
overflow: hidden;
border-width: 18rpx;
//box-shadow: $im-box-shadow-dark;
}
}

23
im-uniapp/components/chat-record/chat-record.vue

@ -50,12 +50,14 @@ export default {
/* 使@touchend
一直处于录音状态这里允许用户再次点击发送语音并结束录音 */
if (this.recording) {
this.onEndRecord();
return;
}
console.log("开始录音")
this.moveToCancel = false;
this.initRecordBar();
if(!this.$rc.checkIsEnable()){
return;
}
this.$rc.start().then(() => {
this.recording = true;
console.log("开始录音成功")
@ -70,9 +72,12 @@ export default {
});
},
onEndRecord() {
if(!this.recording){
return;
}
this.recording = false;
//
this.StopTimer();
this.stopTimer();
//
this.$rc.close();
//
@ -80,8 +85,8 @@ export default {
console.log("录音取消")
return;
}
// 1
if (this.druation == 0) {
// 1
if (this.druation <= 1) {
uni.showToast({
title: "说话时间太短",
icon: 'none'
@ -95,13 +100,11 @@ export default {
title: e,
icon: 'none'
})
}).finally(() => {
this.$rc.close();
})
},
startTimer() {
this.druation = 0;
this.StopTimer();
this.stopTimer();
this.rcTimer = setInterval(() => {
this.druation++;
// 60s,
@ -110,7 +113,7 @@ export default {
}
}, 1000)
},
StopTimer() {
stopTimer() {
this.rcTimer && clearInterval(this.rcTimer);
this.rcTimer = null;
},
@ -134,6 +137,10 @@ export default {
}
return `录音时长:${this.druation}s`;
}
},
unmounted() {
this.stopTimer();
this.recording = false;
}
}

203
im-uniapp/pages/chat/chat-box.vue

@ -6,10 +6,11 @@
<scroll-view class="scroll-box" scroll-y="true" upper-threshold="200" @scrolltoupper="onScrollToTop"
:scroll-into-view="'chat-item-' + scrollMsgIdx">
<view v-if="chat" v-for="(msgInfo, idx) in chat.messages" :key="idx">
<chat-message-item v-if="idx >= showMinIdx" :headImage="headImage(msgInfo)"
@call="onRtCall(msgInfo)" :showName="showName(msgInfo)" @recall="onRecallMessage"
@copy="onCopyMessage" @delete="onDeleteMessage" @longPressHead="onLongPressHead(msgInfo)"
@download="onDownloadFile" :id="'chat-item-' + idx" :msgInfo="msgInfo"
<chat-message-item :ref="'message'+msgInfo.id" v-if="idx >= showMinIdx"
:headImage="headImage(msgInfo)" @call="onRtCall(msgInfo)" :showName="showName(msgInfo)"
@recall="onRecallMessage" @delete="onDeleteMessage" @copy="onCopyMessage"
@longPressHead="onLongPressHead(msgInfo)" @download="onDownloadFile"
@audioStateChange="onAudioStateChange" :id="'chat-item-' + idx" :msgInfo="msgInfo"
:groupMembers="groupMembers">
</chat-message-item>
</view>
@ -19,7 +20,7 @@
<view class="iconfont icon-at">:&nbsp;</view>
<scroll-view v-if="atUserIds.length > 0" class="chat-at-scroll-box" scroll-x="true" scroll-left="120">
<view class="chat-at-items">
<view v-for="m in atUserItems" class="chat-at-item">
<view v-for="m in atUserItems" class="chat-at-item" :key="m.userId">
<head-image :name="m.showNickName" :url="m.headImage" size="minier"></head-image>
</view>
</view>
@ -31,24 +32,26 @@
<chat-record v-if="showRecord" class="chat-record" @send="onSendRecord"></chat-record>
<view v-else class="send-text">
<editor id="editor" class="send-text-area" :placeholder="isReceipt ? '[回执消息]' : ''"
:read-only="isReadOnly" @focus="onEditorFocus" @blur="onEditorBlur" @ready="onEditorReady"
@input="onTextInput">
:read-only="isReadOnly" @focus="onEditorFocus" @blur="onEditorBlur" @ready="onEditorReady" @input="onTextInput">
</editor>
<!-- <textarea class="send-text-area" v-model="sendText" auto-height :show-confirm-bar="false"
:placeholder="isReceipt ? '[回执消息]' : ''" :adjust-position="false" @confirm="sendTextMessage()"
@keyboardheightchange="onKeyboardheightchange" @input="onTextInput" confirm-type="send"
confirm-hold :hold-keyboard="true"></textarea> -->
</view>
<view v-if="chat && chat.type == 'GROUP'" class="iconfont icon-at" @click="openAtBox()"></view>
<view class="iconfont icon-icon_emoji" @click="onShowEmoChatTab()"></view>
<view v-if="isEmpty" class="iconfont icon-add" @click="onShowToolsChatTab()">
</view>
<button v-if="!isEmpty || atUserIds.length > 0" class="btn-send" type="primary"
<button v-if="!isEmpty || atUserIds.length" class="btn-send" type="primary"
@touchend.prevent="sendTextMessage()" size="mini">发送</button>
</view>
</view>
<view class="chat-tab-bar">
<view v-if="chatTabBox == 'tools'" class="chat-tools" :style="{height: keyboardHeight+'px'}">
<view class="chat-tools-item">
<file-upload ref="fileUpload" :onBefore="onUploadFileBefore" :onSuccess="onUploadFileSuccess"
:onError="onUploadFileFail">
<view class="tool-icon iconfont icon-folder"></view>
</file-upload>
<view class="tool-name">文件</view>
</view>
<view class="chat-tools-item">
<image-upload :maxCount="9" sourceType="album" :onBefore="onUploadImageBefore"
:onSuccess="onUploadImageSuccess" :onError="onUploadImageFail">
@ -63,15 +66,6 @@
</image-upload>
<view class="tool-name">拍摄</view>
</view>
<view class="chat-tools-item">
<file-upload ref="fileUpload" :onBefore="onUploadFileBefore" :onSuccess="onUploadFileSuccess"
:onError="onUploadFileFail">
<view class="tool-icon iconfont icon-folder"></view>
</file-upload>
<view class="tool-name">文件</view>
</view>
<view class="chat-tools-item" @click="onRecorderInput()">
<view class="tool-icon iconfont icon-microphone"></view>
<view class="tool-name">语音消息</view>
@ -131,8 +125,10 @@ export default {
scrollMsgIdx: 0, //
chatTabBox: 'none',
showRecord: false,
keyboardHeight: 300,
chatMainHeight: 0, //
keyboardHeight: 290, //
windowHeight: 1000, //
initHeight: 1000, // h5
atUserIds: [],
needScrollToBottom: false, //
showMinIdx: 0, // showMinIdx
@ -142,7 +138,8 @@ export default {
editorCtx: null, //
isEmpty: true, //
isFocus: false, //
isReadOnly: false //
isReadOnly: false, //
playingAudio: null //
}
},
methods: {
@ -265,7 +262,6 @@ export default {
sendTextMessage() {
this.editorCtx.getContents({
success: (e) => {
//
this.editorCtx.clear();
this.atUserIds = [];
@ -275,7 +271,6 @@ export default {
this.showBannedTip();
return;
}
let sendText = this.isReceipt ? "【回执消息】" : "";
e.delta.ops.forEach((op) => {
if (op.insert.image) {
@ -302,7 +297,6 @@ export default {
}
// id
this.fillTargetId(msgInfo, this.chat.targetId);
this.sendMessageRequest(msgInfo).then((m) => {
m.selfSend = true;
this.chatStore.insertMessage(m, this.chat);
@ -314,6 +308,7 @@ export default {
});
}
})
},
createAtText() {
let atText = "";
@ -356,7 +351,6 @@ export default {
this.$nextTick(() => {
this.scrollMsgIdx = idx;
});
},
onShowEmoChatTab() {
this.showRecord = false;
@ -538,7 +532,7 @@ export default {
uni.setClipboardData({
data: msgInfo.content,
success: () => {
uni.showToast({ title: '已复制', icon: 'none' });
uni.showToast({ title: '复制成功' });
},
fail: () => {
uni.showToast({ title: '复制失败', icon: 'none' });
@ -575,7 +569,7 @@ export default {
//
this.scrollToMsgIdx(this.showMinIdx);
// #endif
// 0
// 20
this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0;
},
onShowMore() {
@ -607,6 +601,15 @@ export default {
onEditorBlur(e) {
this.isFocus = false;
},
onAudioStateChange(state, msgInfo) {
const playingAudio = this.$refs['message' + msgInfo.id][0]
if (state == 'PLAYING' && playingAudio != this.playingAudio) {
//
this.playingAudio && this.playingAudio.stopPlayAudio();
//
this.playingAudio = playingAudio;
}
},
loadReaded(fid) {
this.$http({
url: `/message/private/maxReadedId?friendId=${fid}`,
@ -665,7 +668,7 @@ export default {
})
},
rpxTopx(rpx) {
// pxrpx
// rpxpx
let info = uni.getSystemInfoSync()
let px = info.windowWidth * rpx / 750;
return Math.floor(rpx);
@ -696,29 +699,103 @@ export default {
})
}
},
reCalChatMainHeight() {
setTimeout(() => {
let h = this.windowHeight;
//
h -= 50;
//
if (this.isShowKeyBoard || this.chatTabBox != 'none') {
console.log("减去键盘高度:", this.keyboardHeight)
h -= this.keyboardHeight;
this.scrollToBottom();
}
// #ifndef H5
// h5
h -= uni.getSystemInfoSync().statusBarHeight;
// #endif
this.chatMainHeight = h;
console.log("窗口高度:", this.chatMainHeight)
if (this.isShowKeyBoard || this.chatTabBox != 'none') {
this.scrollToBottom();
}
// ios
// #ifdef H5
if (uni.getSystemInfoSync().platform == 'ios') {
//
const delays = [50, 100, 500];
delays.forEach((delay) => {
setTimeout(() => {
uni.pageScrollTo({
scrollTop: 0,
duration: 10
});
}, delay);
})
}
// #endif
}, 30)
},
listenKeyBoard() {
// #ifdef H5
// H5TextArea@keyboardheightchange
//
const userAgent = navigator.userAgent;
const regex = /(macintosh|windows)/i;
if (regex.test(userAgent)) {
//
console.log("userAgent:", userAgent)
return;
}
if (uni.getSystemInfoSync().platform == 'ios') {
// ios h5
window.addEventListener('focusin', this.focusInListener);
window.addEventListener('focusout', this.focusOutListener);
} else {
// h5
let initHeight = window.innerHeight;
window.addEventListener('resize', () => {
let keyboardHeight = initHeight - window.innerHeight;
this.isShowKeyBoard = keyboardHeight > 0;
if (this.isShowKeyBoard) {
this.keyboardHeight = keyboardHeight;
window.addEventListener('resize', this.resizeListener);
}
this.reCalChatMainHeight();
});
// #endif
// #ifndef H5
uni.onKeyboardHeightChange((res) => {
// app
uni.onKeyboardHeightChange(this.keyBoardListener);
// #endif
},
unListenKeyboard() {
// #ifdef H5
// h5
window.removeEventListener('resize', this.resizeListener);
window.removeEventListener('focusin', this.focusInListener);
window.removeEventListener('focusout', this.focusOutListener);
// #endif
// #ifndef H5
uni.offKeyboardHeightChange(this.keyBoardListener);
// #endif
},
keyBoardListener(res) {
this.isShowKeyBoard = res.height > 0;
if (this.isShowKeyBoard) {
this.keyboardHeight = res.height; //
}
this.reCalChatMainHeight();
});
// #endif
},
resizeListener() {
console.log("resize")
let keyboardHeight = this.initHeight - window.innerHeight;
this.isShowKeyBoard = keyboardHeight > 150;
if (this.isShowKeyBoard) {
this.keyboardHeight = keyboardHeight;
}
this.reCalChatMainHeight();
},
focusInListener() {
console.log("focusInListener")
this.isShowKeyBoard = true;
this.reCalChatMainHeight();
},
focusOutListener() {
console.log("focusOutListener")
this.isShowKeyBoard = false;
this.reCalChatMainHeight();
},
showBannedTip() {
let msgInfo = {
@ -736,27 +813,6 @@ export default {
}
this.chatStore.insertMessage(msgInfo, this.chat);
},
reCalChatMainHeight() {
const sysInfo = uni.getSystemInfoSync();
let h = sysInfo.windowHeight;
//
h -= 50;
// #ifdef H5
// h5sysInfo.windowHeight
if (this.chatTabBox != 'none') {
h -= this.keyboardHeight;
}
// #endif
// #ifndef H5
//
h -= sysInfo.statusBarHeight;
if (this.isShowKeyBoard || this.chatTabBox != 'none') {
h -= this.keyboardHeight;
}
// #endif
console.log("h:", h)
this.chatMainHeight = h;
},
generateId() {
// id
return String(new Date().getTime()) + String(Math.floor(Math.random() * 1000));
@ -858,7 +914,20 @@ export default {
//
this.listenKeyBoard();
//
this.$nextTick(() => this.reCalChatMainHeight())
this.$nextTick(() => {
this.windowHeight = uni.getSystemInfoSync().windowHeight;
this.reCalChatMainHeight()
// ios h5:
// #ifdef H5
this.initHeight = window.innerHeight;
document.body.addEventListener('touchmove', function(e) {
e.preventDefault();
}, { passive: false });
// #endif
});
},
onUnload() {
this.unListenKeyboard();
},
onShow() {
if (this.needScrollToBottom) {
@ -870,14 +939,12 @@ export default {
}
</script>
<style lang="scss" scoped>
<style lang="scss">
.chat-box {
$icon-color: rgba(0, 0, 0, 0.88);
position: relative;
background-color: #fafafa;
.header {
display: flex;
justify-content: center;
@ -912,7 +979,7 @@ export default {
width: 100%;
display: flex;
flex-direction: column;
z-index: 9;
z-index: 2;
.chat-msg {
flex: 1;

Loading…
Cancel
Save