This commit is contained in:
cansnow
2025-12-17 08:52:51 +08:00
parent cf1ad1c24b
commit 2e793c3f77
2 changed files with 171 additions and 837 deletions
@@ -2,19 +2,12 @@
<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="" />
<image class="action_btn" v-show="inputType == 'keyboard'" @click.prevent="swtichInputType('record')" mode="heightFix" src="@/static/images/chating_footer_audio.png" alt="" srcset="" />
<image class="action_btn" v-show="inputType == 'record'" @click.prevent="swtichInputType('keyboard')" mode="heightFix" 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 -->
<!-- 使用 SimpleEditor 替代 CustomEditor更简单可靠 -->
<Recoder v-if="inputType == 'record'" @RecodeEvent="onRecodeEvent"></Recoder>
<SimpleEditor
v-if="!isAudio"
v-if="inputType == 'keyboard'"
class="custom_editor"
ref="customEditor"
:value="inputHtml"
@@ -23,16 +16,13 @@
@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(false)" src="@/static/images/chating_footer_add.png" alt="" srcset="" />
<view class="footer_action_area" v-show="inputType == 'keyboard'">
<image class="action_btn" @click.prevent="updateActionBar(true)" src="@/static/images/chating_footer_emoji.png" alt="" srcset="" />
<image v-show="!hasContent" class="action_btn" @click.prevent="updateActionBar(false)" 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"
@onUserEvent="onUserEvent"
v-show="actionBarVisible" />
<chating-action-bar :isEmoji="isEmoji" @onUserEvent="onUserEvent" v-show="actionBarVisible" />
<u-action-sheet :safeAreaInsetBottom="true" round="12" :actions="actionSheetMenu" @select="selectClick"
:closeOnClickOverlay="true" :closeOnClickAction="true" :show="showActionSheet"
@close="showActionSheet = false">
@@ -63,10 +53,9 @@
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";
import SimpleEditor from "./SimpleEditor";
import ChatingActionBar from "./ChatingActionBar";
import Recoder from "./Recoder";
const needClearTypes = [MessageType.TextMessage];
@@ -85,10 +74,9 @@
export default {
components: {
CustomEditor,
SimpleEditor,
ChatingActionBar,
//UParse,
Recoder,
},
props: {
footerOutsideFlag: Number,
@@ -96,12 +84,10 @@
data() {
return {
recording:false,
sendMsgTimmer: null, //发送时间定时器
sendDuring: 0, //发送时间计数器 1分钟以内的信息不显示时间
sendTimeBetween: 60, //发送信息显示的间隔,60秒以内信息不显示发送时间
voiceIconText : "正在录音...",
inputType:"keyboard",
isEmoji:false,
isAudio:false,
inputHtml: "",
inputHtml: '<span>@cansnow</span><span>@baidu</span><span>@jingds</span>',
actionBarVisible: false,
isInputFocus: false,
actionSheetMenu: [],
@@ -218,6 +204,14 @@
this.isEmoji = !!isEmoji;
}
},
swtichInputType(type){
console.log(type);
this.inputType = type;
if(this.inputType == 'record'){
this.actionBarVisible = false;
this.isEmoji = false;
}
},
editorFocus() {
this.isInputFocus = true;
this.$emit("scrollToBottom");
@@ -229,6 +223,34 @@
// SimpleEditor 返回的是纯文本,直接使用
this.inputHtml = e.detail.value || e.detail.text || e.detail.html || "";
},
async sendLocationMessage(res){
console.log(res);
const _this = this;
const message = await IMSDK.asyncApi(
IMMethods.CreateLocationMessage,
IMSDK.uuid(),
{
latitude:res.lat,
longitude:res.lng,
description:res.address
}
);
_this.sendMessage(message,_this.storeCurrentConversation.userID,_this.storeCurrentConversation.groupID);
},
async sendVoiceMessage(audio){
const _this = this;
const message = await IMSDK.asyncApi(
IMMethods.CreateSoundMessageFromFullPath,
IMSDK.uuid(),
{
soundPath:getPurePath(audio.tempFilePath),
duration:audio.contentDuration
}
);
_this.sendMessage(message,_this.storeCurrentConversation.userID,_this.storeCurrentConversation.groupID);
},
// from comp
sendMediaMesage(paths) {
const _this = this;
@@ -296,132 +318,23 @@
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 = '正在录音... '
onRecodeEvent(e){
const _this = this;
switch(e.type){
case "voiceIconTextChange":
_this.voiceIconText = e.text;
break;
case "sendVoiceMessage":
_this.sendVoiceMessage(e.audio);
break;
case "recordingStateChange":
_this.recording = e.state;
break;
default:
break;
}
},
//结束录音
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---------------------------------------------------*/
onUserEvent(e){
const _this = this;
switch(e.type){
@@ -517,16 +430,14 @@
});
break;
case "prepend_location_message":
uni.chooseLocation({
complete(res) {
console.log(res);
},
fail(res) {
console.log(res);
}
//latitude:1,
//longitude:1,
});
uni.navigateTo({
url:"/pages/common/map",
events:{
onConfirm(res) {
_this.sendLocationMessage(res);
}
}
})
break;
default:
console.log(e);
@@ -562,13 +473,13 @@
background: #f6f6f6;
// height: 50px;
max-height: 120px;
padding: 24rpx 20rpx;
padding: 0 20rpx;
gap: 20rpx;
.input_content {
flex: 1;
min-height: 30px;
max-height: 120px;
margin: 0 24rpx;
min-height: 80rpx;
max-height: 240rpx;
border-radius: 8rpx;
position: relative;
@@ -597,28 +508,112 @@
}
}
}
.action_btn{
width: 26px;
height: 26px;
margin: 24rpx auto;
}
.footer_action_area {
display: flex;
align-items: center;
.emoji_action {
margin-right: 24rpx;
}
image {
width: 26px;
height: 26px;
}
gap: 20rpx;
}
.send_btn {
height: 30px;
line-height: 30px;
background-color: $uni-color-success;
padding: 0 8px;
padding: 0 8px;
border-radius: 4px;
color: #fff;
}
}
/* 语音动画 */
.voice_an {
width: 300rpx;
height: 300rpx;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -55%);
background-color: rgba(41, 41, 41, 0.7);
color: white;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
border-radius: 10rpx;
.text {
padding-top: 30rpx;
}
@keyframes runVoice {
0% {
height: 10%;
}
20% {
height: 50%;
}
50% {
height: 100%;
}
80% {
height: 50%;
}
100% {
height: 0%;
}
}
.wave {
width: 6rpx;
height: 100%;
margin-left: 10rpx;
border-radius: 50rpx;
background-color: #999;
vertical-align: middle;
display: inline-block;
}
.voice_an_icon {
width: 200rpx;
height: 100rpx;
line-height: 50rpx;
margin: 50rpx 0;
}
.voice_an_icon #one {
animation: runVoice 0.6s infinite 0.1s;
}
.voice_an_icon #two {
animation: runVoice 0.6s infinite 0.3s;
}
.voice_an_icon #three {
animation: runVoice 0.6s infinite 0.6s;
}
.voice_an_icon #four {
animation: runVoice 0.6s infinite 0.1s;
}
.voice_an_icon #five {
animation: runVoice 0.6s infinite 0.3s;
}
.voice_an_icon #six {
animation: runVoice 0.6s infinite 0.6s;
}
.voice_an_icon #seven {
animation: runVoice 0.6s infinite 0.1s;
}
}
</style>