Files
im/pages/conversation/chating/components/ChatingFooter/index.vue
T
cansnow 29be534f22 9
2025-12-02 03:05:52 +08:00

556 lines
14 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.
<template>
<view>
<view>
<view class="chat_footer">
<!-- 语音信息 -->
<image v-if="1==2" v-show="!isAudio" @click.prevent="isAudio=!isAudio"
src="@/static/images/chating_footer_audio.png" alt="" srcset="" />
<image v-if="1==2" v-show="isAudio" @click.prevent="isAudio=!isAudio"
src="@/static/images/chating_footer_audio_recording.png" alt="" srcset="" />
<view class="input_content">
<!-- #ifdef APP-PLUS -->
<view v-if="isAudio" class="voice_title" @touchstart.stop.prevent="startVoice"
@touchmove.stop.prevent="moveVoice" @touchend.stop="endVoice"
@touchcancel.stop="cancelVoice" :style="{ background: recording ? '#c7c6c6' : '#FFFFFF' }">
<text>{{ voiceTitle }}</text>
</view>
<!-- #endif -->
<CustomEditor v-if="!isAudio" class="custom_editor" ref="customEditor" @ready="editorReady" @focus="editorFocus"
@blur="editorBlur" @input="editorInput" />
</view>
<view class="footer_action_area" v-show="!isAudio">
<image class="emoji_action" @click.prevent="updateActionBar(true)"
src="@/static/images/chating_footer_emoji.png" alt="" srcset="" />
<image v-show="!hasContent" @click.prevent="updateActionBar"
src="@/static/images/chating_footer_add.png" alt="" srcset="" />
<button class="send_btn" type="primary" v-show="hasContent" @touchend.prevent="sendTextMessage">发送</button>
</view>
</view>
<chating-action-bar :isEmoji="isEmoji" @sendMessage="sendMessage" @prepareMediaMessage="prepareMediaMessage"
v-show="actionBarVisible" />
<u-action-sheet :safeAreaInsetBottom="true" round="12" :actions="actionSheetMenu" @select="selectClick"
:closeOnClickOverlay="true" :closeOnClickAction="true" :show="showActionSheet"
@close="showActionSheet = false">
</u-action-sheet>
</view>
<!-- 录音动画 -->
<!-- #ifdef APP-PLUS -->
<view class="voice_an" v-if="recording">
<view class="voice_an_icon">
<view id="one" class="wave"></view>
<view id="two" class="wave"></view>
<view id="three" class="wave"></view>
<view id="four" class="wave"></view>
<view id="five" class="wave"></view>
<view id="six" class="wave"></view>
<view id="seven" class="wave"></view>
</view>
<view class="text">
<text>{{voiceIconText}}</text>
</view>
</view>
<!-- #endif -->
</view>
</template>
<script>
import {mapGetters,mapActions} from "vuex";
import {getPurePath,html2Text} from "@/util/common";
import {offlinePushInfo} from "@/util/imCommon";
import {ChatingFooterActionTypes,UpdateMessageTypes,} from "@/constant";
import IMSDK, {IMMethods,MessageStatus,MessageType,} from "openim-uniapp-polyfill";
import UParse from "@/components/gaoyia-parse/parse.vue";
import CustomEditor from "./CustomEditor.vue";
import ChatingActionBar from "./ChatingActionBar.vue";
const needClearTypes = [MessageType.TextMessage];
const rtcChoose = [
{
name: "视频通话",
type: ChatingFooterActionTypes.Video,
idx: 0,
},
{
name: "语言通话",
type: ChatingFooterActionTypes.Voice,
idx: 1,
},
];
export default {
components: {
CustomEditor,
ChatingActionBar,
UParse,
},
props: {
footerOutsideFlag: Number,
},
data() {
return {
recording:false,
sendMsgTimmer: null, //发送时间定时器
sendDuring: 0, //发送时间计数器 1分钟以内的信息不显示时间
sendTimeBetween: 60, //发送信息显示的间隔,60秒以内信息不显示发送时间
isEmoji:false,
isAudio:false,
customEditorCtx: null,
inputHtml: "",
actionBarVisible: false,
isInputFocus: false,
actionSheetMenu: [],
showActionSheet: false,
};
},
computed: {
...mapGetters([
"storeCurrentConversation",
"storeCurrentGroup",
"storeBlackList",
]),
hasContent() {
return html2Text(this.inputHtml) !== "";
},
},
watch: {
footerOutsideFlag(newVal) {
this.onClickActionBarOutside();
},
},
mounted() {
this.setKeyboardListener();
},
beforeDestroy() {
this.disposeKeyboardListener();
},
methods: {
...mapActions("message", ["pushNewMessage", "updateOneMessage"]),
async createTextMessage() {
let message = "";
const text = html2Text(this.inputHtml);
message = await IMSDK.asyncApi(
IMMethods.CreateTextMessage,
IMSDK.uuid(),
text
);
console.log(message);
return message;
},
async sendTextMessage() {
if (!this.hasContent) return;
const message = await this.createTextMessage();
this.sendMessage(message);
},
sendMessage(message) {
this.pushNewMessage(message);
if (needClearTypes.includes(message.contentType)) {
this.customEditorCtx.clear();
}
this.$emit("scrollToBottom");
IMSDK.asyncApi(IMMethods.SendMessage, IMSDK.uuid(), {
recvID: this.storeCurrentConversation.userID,
groupID: this.storeCurrentConversation.groupID,
message,
offlinePushInfo,
})
.then(({data}) => {
this.updateOneMessage({
message: data,
isSuccess: true,
});
})
.catch(({data,errCode,errMsg}) => {
uni.$u.toast(errMsg);
this.updateOneMessage({
message: data,
type: UpdateMessageTypes.KeyWords,
keyWords: [
{
key: "status",
value: MessageStatus.Failed,
},
{
key: "errCode",
value: errCode,
},
],
});
});
},
recordAudioMsg(){
if (uni.getSystemInfoSync().platform == "android") {
permission.requestAndroid("android.permission.RECORD_AUDIO"); //Android请求录音权限
} else {
permission.requestIOS("record"); //ios请求录音权限
}
},
// action
onClickActionBarOutside() {
if (this.actionBarVisible) {
this.actionBarVisible = false;
}
},
updateActionBar(isEmoji) {
this.actionBarVisible = !this.actionBarVisible;
console.log(this.isEmoji);
this.isEmoji = !!isEmoji;
console.log(this.isEmoji);
},
editorReady(e) {
this.customEditorCtx = e.context;
this.customEditorCtx.clear();
},
editorFocus() {
this.isInputFocus = true;
this.$emit("scrollToBottom");
},
editorBlur() {
this.isInputFocus = false;
},
editorInput(e) {
this.inputHtml = e.detail.html;
},
prepareMediaMessage(type,extra) {
console.log(type)
if (type === ChatingFooterActionTypes.Video) {
this.actionSheetMenu = [...rtcChoose];
this.showActionSheet = true;
}
if (type === ChatingFooterActionTypes.Album) {
this.chooseOrShotImage(["album"]).then((paths) =>
this.batchCreateImageMesage(paths)
);
}
if (type === ChatingFooterActionTypes.Camera) {
this.chooseOrShotImage(["camera"]).then((paths) =>
this.batchCreateImageMesage(paths)
);
}
if (type === ChatingFooterActionTypes.Location) {
uni.chooseLocation({
complete(res) {
console.log(res);
},
fail(res) {
console.log(res);
}
//latitude:1,
//longitude:1,
})
}
if (type === "emoji") {
//TODO 在光标处插入文字extra
this.customEditorCtx.insertText({
text: extra,
success: () => {
console.log("插入文字成功");
},
fail: (err) => {
console.log("插入文字失败", err);
}
});
}
},
// from comp
batchCreateImageMesage(paths) {
/*
createAdvancedTextMessage
createTextAtMessage
createLocationMessage
createTextMessage
createCustomMessage
createQuoteMessage
createAdvancedQuoteMessage
createCardMessage
createImageMessage
createImageMessage
createImageMessageByURL
createSoundMessage
createSoundMessageFromFullPath
createSoundMessageByURL
createVideoMessage
createVideoMessageFromFullPath
createVideoMessageByURL
createFileMessage
createFileMessageFromFullPath
createFileMessageByURL
createMergerMessage
createFaceMessage
createForwardMessage
*/
paths.forEach(async (path) => {
const message = await IMSDK.asyncApi(
IMMethods.CreateImageMessageFromFullPath,
IMSDK.uuid(),
getPurePath(path)
);
this.sendMessage(message);
});
},
selectClick({idx}) {
if (idx === 0) {
uni.$u.toast('根据相关政策,暂时禁用视频通话');
//发送视频通话
// this.chooseOrShotImage(["album"]).then((paths) =>
// this.batchCreateImageMesage(paths)
// );
} else {
uni.$u.toast('根据相关政策,暂时禁用音频通话');
//发送音频通话
// this.chooseOrShotImage(["camera"]).then((paths) =>
// this.batchCreateImageMesage(paths)
// );
}
},
chooseOrShotImage(sourceType) {
return new Promise((resolve, reject) => {
uni.chooseImage({
count: 9,
sizeType: ["compressed"],
sourceType,
success: function({tempFilePaths}) {
resolve(tempFilePaths);
},
fail: function(err) {
console.log(err);
reject(err);
},
});
});
},
// keyboard
keyboardChangeHander({height}) {
if (height > 0) {
if (this.actionBarVisible) {
this.actionBarVisible = false;
}
}
},
setKeyboardListener() {
uni.onKeyboardHeightChange(this.keyboardChangeHander);
},
disposeKeyboardListener() {
uni.offKeyboardHeightChange(this.keyboardChangeHander);
},
/*----------------------------------------------------H5不支持)录音相关 start-------------------------------------- */
//准备开始录音
startVoice(e) {
if (!this.Audio.paused) {
//如果音频正在播放 先暂停。
this.stopAudio(this.AudioExam)
}
this.recording = true;
this.isStopVoice = false;
this.voiceCanSend = true;
this.voiceIconText = "正在录音..."
this.PointY = e.touches[0].clientY;
this.Recorder.start({
format: 'mp3'
});
},
//录音已经开始
beginVoice() {
let that = this;
if (that.isStopVoice) {
that.Recorder.stop();
return;
}
that.voiceTitle = '松开 结束'
that.voiceInterval = setInterval(() => {
console.log("that.voiceTime", that.voiceTime);
if (that.voiceTime > 49) {
that.voiceIconText = "录音结束倒计时[" + (60 - that.voiceTime) + "]s";
};
if (that.voiceTime == 60) {
clearInterval(that.voiceInterval);
that.endVoice();
}
that.voiceTime++;
}, 1000)
},
//move 正在录音中
moveVoice(e) {
const PointY = e.touches[0].clientY;
const slideY = this.PointY - PointY;
if (slideY > uni.upx2px(120)) {
this.voiceCanSend = false;
this.voiceIconText = '松开手指 取消发送 '
} else if (slideY > uni.upx2px(60)) {
this.voiceCanSend = true;
this.voiceIconText = '手指上滑 取消发送 '
} else {
this.voiceIconText = '正在录音... '
}
},
//结束录音
endVoice() {
this.isStopVoice = true; //加锁 确保已经结束录音并不会录制
this.Recorder.stop();
this.voiceTitle = '按住 说话'
},
//录音被打断
cancelVoice(e) {
console.log("路由被打断", e);
this.voiceTime = 0;
this.voiceTitle = '按住 说话';
this.voiceCanSend = false;
this.Recorder.stop();
},
//处理录音文件
handleRecorder({tempFilePath,duration }) {
if (this.voiceTime < 1) {
this.voiceIconText = "说话时间过短";
setTimeout(() => {
this.recording = false;
}, 500)
return;
}
let contentDuration = this.voiceTime;
this.voiceTime = 0;
this.recording = false;
clearInterval(this.voiceInterval);
console.log("录音文件", tempFilePath);
console.log("是否发送语音信息", this.voiceCanSend);
let voiceFile = {
tempFilePath: tempFilePath,
contentDuration: Math.ceil(contentDuration),
anmitionPlay: false,
};
if (this.voiceCanSend) {
console.log("=====上传语音文件,并发送语音信息====");
let audioType = this.messageApi.CONTENT_TYPE.AUDIO_CONTENT_TYPE;
this.uploadFile(voiceFile, audioType);
return;
} else {
console.log("=====已经取消发送语音信息====")
return;
}
},
//控制播放还是暂停音频文件
handleAudio(item) {
this.AudioExam = item;
this.Audio.paused ? this.playAudio(item) : this.stopAudio(item);
},
//播放音频
playAudio(item) {
let target = item.content.fileSaveTarget;
let src = item.content.fullPath;
if (target == "local") {
src = this.$u.api.multipartAddress.getFileByPath + src;
}
this.Audio.src = src;
this.Audio.hasBeenSentId = item.id;
this.Audio.play();
let currentAudioMsg = this.messageList.find(it => it.id == item.id);
currentAudioMsg.content.anmitionPlay = true;
},
//停止音频
stopAudio(item) {
let currentAudioMsg = this.messageList.find(it => it.id == item.id);
currentAudioMsg.content.anmitionPlay = false;
this.Audio.src = '';
this.Audio.stop();
},
//关闭动画
closeAnmition() {
const hasBeenSentId = this.Audio.hasBeenSentId;
let item = this.messageList.find(it => it.id == hasBeenSentId);
item.content.anmitionPlay = false;
},
/*-------------------------------------录音相关方法块 end---------------------------------------------------*/
},
};
</script>
<style lang="scss" scoped>
.custom_editor {
img {
vertical-align: sub;
}
}
.forbidden_footer {
width: 100%;
height: 112rpx;
color: #8e9ab0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background: #f0f2f6;
}
.chat_footer {
display: flex;
align-items: flex-end;
// background-color: #e9f4ff;
background: #f6f6f6;
// height: 50px;
max-height: 120px;
padding: 24rpx 20rpx;
.input_content {
flex: 1;
min-height: 30px;
max-height: 120px;
margin: 0 24rpx;
border-radius: 8rpx;
position: relative;
.record_btn {
// background-color: #3c9cff;
background: #fff;
color: black;
height: 30px;
font-size: 24rpx;
}
}
.quote_message {
@include vCenterBox();
justify-content: space-between;
margin-top: 12rpx;
padding: 8rpx;
// padding-top: 20rpx;
border-radius: 6rpx;
background-color: #fff;
color: #666;
.content {
::v-deep uni-view {
@include ellipsisWithLine(2);
}
}
}
.footer_action_area {
display: flex;
align-items: center;
.emoji_action {
margin-right: 24rpx;
}
image {
width: 26px;
height: 26px;
}
}
.send_btn {
height: 30px;
line-height: 30px;
background-color: $uni-color-success;
padding: 0 8px;
border-radius: 4px;
color: #fff;
}
}
</style>