123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539 |
- <template>
- <view class="container">
- <view id="topnav" class="topnav flex-start" :style="{'height':`${topbarOffsetHeight-statusBarHeight}px`,'padding-top':`${statusBarHeight}px`}">
- <view class="nav-item flex-center" @click="back">
- <image :src="`${assetsUrl}back.png`" mode="widthFix" class="nav-img"></image>
- </view>
- <view class="nav-center flex-center">
- <view class="nav-text font32 fw600">
- {{userInfo.nick}}
- </view>
- <view class="nav-tip font20 fw400" >
- <text></text>{{onlineState}} · {{userInfo.distance}}
- </view>
- </view>
-
- <view class="nav-item"></view>
- </view>
- <view class="message-list" :style="{'height': `${scrollHeight}px`,'margin-top':`${topbarOffsetHeight}px`}">
- <TUI-message-list id="message-list" ref="messageList" :conversation="conversation" :scrollHeight="scrollHeight" :topbarOffsetHeight="topbarOffsetHeight" />
- </view>
- <view class="talk-box" id="talk-box" @touchmove="prevent()">
- <view class="input-box flex-between">
- <input type="text" class="input" confirm-type="send" placeholder="请输入消息…" :adjust-position="false" v-model="inputText" placeholder-style="color:#7D7DA4 ;font-size:24rpx" @confirm="sendTextMessage">
- <view class="input-btn font22 fw400" @click="sendTextMessage">
- 发送
- </view>
- </view>
- <view class="action-box flex-between">
- <image :src="`${assetsUrl}talk-voice.png`" mode="aspectFit" class="act-img" @click="showActionPanel(0)"></image>
- <image :src="`${assetsUrl}talk-pic.png`" mode="aspectFit" class="act-img" @click="showActionPanel(1)"></image>
- <image :src="`${assetsUrl}talk-video.png`" mode="aspectFit" class="act-img" @click="showActionPanel(2)"></image>
- <image :src="`${assetsUrl}talk-emo.png`" mode="aspectFit" class="act-img" @click="showActionPanel(3)"></image>
- </view>
- <view class="action-panel" :style="{'padding-bottom':`${showActionIndex===-1?'68rpx':'20rpx'}`}">
- <view class="voice-panel flex-center" v-if="showActionIndex===0">
- <view class="voice-text font22 fw400">
- {{voiceText}}
- </view>
- <image :src="`${assetsUrl}talk-voice-${isRecording?'delete':'open'}.png`" mode="aspectFill" class="voice-img" @longpress="handleLongPress" @touchmove="handleTouchMove" @touchend="handleTouchEnd"></image>
- </view>
- <view class="emoji-box" v-if="showActionIndex===3">
- <TUI-Emoji @enterEmoji="appendMessage"></TUI-Emoji>
- </view>
- </view>
- </view>
- <view v-if="videoPlay" class="container-box" @tap.stop="stopVideoHander">
- <video
- v-if="videoPlay"
- class="video-message"
- :src="videoMessage.payload.videoUrl||videoMessage.videoUrl"
- :poster="videoMessage.payload.thumbUrl||videoMessage.videoCover"
- object-fit="cover"
- error="videoError"
- autoplay="true"
- direction="0"
- show-fullscreen-btn="false"
- :style="{'width':`${videoMessage.payload.thumbWidth||videoMessage.width}px`,'height':`${videoMessage.payload.thumbHeight||videoMessage.height}px`}"
- />
- </view>
- </view>
- </template>
- <script>
- import TUIEmoji from '../../components/tui-chat/message-elements/emoji/index';
- import TUIMessageList from '../../components/tui-chat/message-list/index';
-
- export default {
- components: {
- TUIMessageList,
- TUIEmoji
- },
- data() {
- return {
- assetsUrl:this.$util.assetsUrl,
- scrollHeight:0,
- topNavHeight:0,
- userInfo:null,
- messageList:[],
- message:null,//消息实例
- onlineState:'',
- scrollRefreshing:false,
- scrollTriggered:true,
- conversation:'',
- conversationID: '',
- inputText:'',
- showSendBtn:false,
- showActionIndex:-1,
- recorderManager:null,
- voiceText:'按住说话,松手发送',
- recordTimeTotal:60,//最大长度,30秒
- isRecording:false,
- canSend:false,
- startPoint:0,
- videoPlay: false,
- videoMessage: {},
-
-
- }
- },
- computed: {
- statusBarHeight() {
- return this.$store.state.statusBarHeight;
- },
- topbarOffsetHeight() {
- return this.$store.state.topbarOffsetHeight;
- }
- },
- created() {
- uni.$on('videoPlayerHandler', value => {
- this.videoPlay = value.isPlay;
- this.videoMessage = value.message;
- });
- uni.authorize({
- scope:'scope.record',
- success:()=>{
- this.startRecordManage();
- },
- fail:err=>{
- uni.showModal({
- content: '检测到您没打开录音功能权限,是否去设置打开?',
- confirmText: "确认",
- cancelText: '取消',
- success:res=>{
- if(res.confirm){
- uni.openSetting({
- success:ress=>{
- if(ress.authSetting){
- this.startRecordManage();
- }
- }
- })
- }
- }
- })
- }
- })
- },
- onLoad(options) {
- this.conversationID=options.conversationid;
- uni.$TUIKit.setMessageRead({
- conversationID:this.conversationID
- })
- uni.$TUIKit.getConversationProfile(this.conversationID).then(res => {
- const { conversation } = res.data;
- this.conversation = conversation;
- console.log(this.conversation)
- });
- let pages=getCurrentPages();
- let prePage=pages[pages.length-2];
- this.userInfo=prePage.$vm.userInfo;
-
- },
- mounted() {
-
- if(this.userInfo.lastActiveTime===0||this.userInfo.online){
- this.onlineState='在线';
- }
- else if(this.userInfo.lastActiveTime<30&&!this.userInfo.online){
- this.onlineState='刚刚';
- }
- else{
- this.onlineState='离线';
- }
- setTimeout(()=>{
- this.computedScollviewHeight();
- },500)
-
-
- },
- methods: {
- back(){
- uni.navigateBack({
- delta:1
- })
- },
- prevent(){
- return false;
- },
-
- stopVideoHander() {
- this.videoPlay = false;
- },
- /**
- * 计算scroll高度
- */
- computedScollviewHeight() {
- let query = uni.createSelectorQuery().in(this);
- let heightLeaf =0;
- query.selectAll('#topnav,#talk-box').boundingClientRect(data => {
- data.forEach(item=>{
- heightLeaf+=item.height;
- })
- }).exec(() => {
- let sysInfo = uni.getSystemInfoSync();
- this.scrollHeight = sysInfo.windowHeight - heightLeaf;
- this.$refs.messageList.scrollToButtom();
- });
-
- },
- /**
- * 推荐下拉刷新、加载更多
- */
- scrollRefresh(){
- if (this.scrollRefreshing)
- {
- return;
- }
- this.scrollRefreshing = true;
- setTimeout(() => {
- this.scrollTriggered = false;
- this.scrollRefreshing = false;
- }, 1000);
- this.isLoadPreData=true;
-
- },
- scrollPulling(e) {},
- scrollRestore() {this.scrollTriggered = true;},
- scrollAbort() {},
- scrollToBottom(){},
- showActionPanel(index){
- if(this.showActionIndex===index){
- this.showActionIndex=-1;
- setTimeout(()=>{
- this.computedScollviewHeight();
- },50)
- return;
- }
-
- switch(index){
- case 0:
- this.showActionIndex=index;
- setTimeout(()=>{
- this.computedScollviewHeight();
- },50)
- break;
- case 1:
- this.showActionIndex=-1;
- uni.chooseImage({
- count: 1,
- sizeType: ['original', 'compressed'],
- sourceType: ['album', 'camera'],
- success:res=>{
- console.log(res)
- const message = uni.$TUIKit.createImageMessage({
- to: String(this.userInfo.id),
- conversationType: this.conversation.type,
- payload: {
- file: res
- },
- onProgress:event=>{}
- });
- this.$sendTIMMessage(message);
- }
- })
- break;
- case 2:
- uni.chooseVideo({
- sourceType: ['album', 'camera'],
- maxDuration: 60,
- camera: 'back',
- success:res=>{
- const message = uni.$TUIKit.createVideoMessage({
- to: String(this.userInfo.id),
- conversationType: this.conversation.type,
- payload: {
- file: res
- },
- onProgress:event=>{}
- });
- this.$sendTIMMessage(message);
- }
- })
- break;
- case 3:
- this.showActionIndex=index;
- setTimeout(()=>{
- this.computedScollviewHeight();
- },50)
- break;
- }
-
- },
- appendMessage(e) {
- this.inputText+=e.detail.message;
- },
- startRecordManage(){
- // 加载声音录制管理器
- this.recorderManager = uni.getRecorderManager();
- console.log(this.recorderManager)
- this.recorderManager.onStop(res => {
- console.log(res)
- clearInterval(this.recordTimer);
- // 兼容 uniapp 打包app,duration 和 fileSize 需要用户自己补充
- // 文件大小 = (音频码率) x 时间长度(单位:秒) / 8
- let msg = {
- duration: res.duration ? res.duration : this.recordTime * 1000,
- tempFilePath: res.tempFilePath,
- fileSize: res.fileSize ? res.fileSize : ((48 * this.recordTime) / 8) * 1024
- };
- uni.hideLoading();
- // 兼容 uniapp 语音消息没有duration
- if (this.canSend) {
- if (msg.duration < 1000) {
- uni.showToast({
- title: '录音时间太短',
- icon: 'none'
- });
- } else {
- // res.tempFilePath 存储录音文件的临时路径
- const message = uni.$TUIKit.createAudioMessage({
- to: String(this.userInfo.id),
- conversationType: this.conversation.type,
- payload: {
- file: msg
- }
- });
- this.$sendTIMMessage(message);
- }
- }
- this.startPoint=0;
- this.isRecording=false;
- this.canSend=true;
- this.voiceText='按住说话,松手发送';
- });
- this.recorderManager.onError(err=>{
- console.log(err)
- })
- },
- handleLongPress(e) {
- uni.vibrateShort();
- this.recorderManager.start({
- duration: 60000,
- // 录音的时长,单位 ms,最大值 600000(10 分钟)
- sampleRate: 44100,
- // 采样率
- numberOfChannels: 1,
- // 录音通道数
- encodeBitRate: 192000,
- // 编码码率
- format: 'aac' // 音频格式,选择此格式创建的音频消息,可以在即时通信 IM 全平台(Android、iOS、微信小程序和Web)互通
- });
- this.startPoint=e.touches[0];
- this.voiceText='正在录音,上划可取消';
- this.isRecording=true;
- this.recordTime=0;
- this.recordTimer = setInterval(() => {
- this.recordTime++;
- if(this.recorTime>=this.recordTimeTotal){
- this.recorderManager.stop();
- clearInterval(this.recordTimer);
- this.recordTimer=null;
- }
- }, 1000);
- },
-
- // 录音时的手势上划移动距离对应文案变化
- handleTouchMove(e) {
- if (this.isRecording) {
- if (this.startPoint.clientY - e.touches[e.touches.length - 1].clientY > 100) {
- this.voiceText='松开手指,取消发送';
- this.canSend=false;
- } else if (this.startPoint.clientY - e.touches[e.touches.length - 1].clientY > 20) {
- this.voiceText='上划可取消';
- this.canSend=true;
- } else {
- this.voiceText='抬起停止';
- this.canSend=true;
- }
- }
- },
-
- // 手指离开页面滑动
- handleTouchEnd() {
- this.isRecording=false;
- this.voiceText='按住说话,松手发送';
- uni.hideLoading();
- this.recorderManager.stop();
- },
- sendTextMessage() {
- const to = String(this.userInfo.id);
- const text =this.inputText;
- const message = uni.$TUIKit.createTextMessage({
- to,
- conversationType: this.conversation.type,
- payload: {
- text
- }
- });
- this.inputText='';
- this.$sendTIMMessage(message);
- },
- $sendTIMMessage(message) {
-
- uni.$TUIKit.sendMessage(message).then((res) => {
- this.$EventBus.$emit('sendMessage', message)
- this.$refs.messageList.scrollToButtom();
- }).catch((error) => {
- uni.showToast({
- title:'发送消息失败',
- icon:"none"
- })
- })
- },
-
- }
- }
- </script>
- <style lang="scss" scoped>
- .container{
- width: 100vw;
- height: 100vh;
- background-color: $bgcolor1;
- position: relative;
- overflow: hidden;
- .topnav {
- padding: 0 10rpx;
- position: fixed;
- top: 0;
- left: 0;
- width: 100vw;
- z-index: 100;
- background-color: $bgcolor1;
- .nav-item{
- width: 40rpx;
- height: 40rpx;
- margin-left: 16rpx;
-
- .nav-img{
- width: 40rpx;
- height: 40rpx;
- }
- }
- .nav-center{
- flex: 1;
- flex-direction: column;
- .nav-text{
-
- color: $fontcolor5;
- height: 40rpx;
- text-align: center;
- }
- .nav-tip{
- color:$fontcolor2;
- }
- }
-
- }
- .talk-box{
- position: fixed;
- bottom: 0;
- left: 0;
- width: 100vw;
- padding-top: 16rpx;
- background-color: $bgcolor1;
- .input-box{
- margin: 0rpx 32rpx;
- height: 80rpx;
- border-radius: 80rpx;
- background-color: $bgcolor4;
- padding: 0 24rpx;
- .input{
- width: 100%;
- height: 100%;
- color: #ffffff;
- font-size: 22rpx;
- }
- .input-btn{
- background-color: $primary;
- color: #ffffff;
- border-radius: 16rpx;
- height: 56rpx;
- line-height: 56rpx;
- width: 120rpx;
- text-align: center;
-
- }
- }
- .action-box{
- padding: 0rpx 0rpx 20rpx 0rpx;
- margin: 0 90rpx;
- margin-top: 36rpx;
- .act-img{
- width: 56rpx;
- height: 56rpx;
- }
- }
- .action-panel{
- width: 100vw;
- .voice-panel{
- height: 400rpx;
- flex-direction: column;
- .voice-text{
- color: #7D7DA4;
- text-align: center;
-
- }
- .voice-img{
- width: 200rpx;
- height: 200rpx;
- margin-top: 40rpx;
- }
- }
- }
- .emoji-box{
- padding: 0 11rpx;
- box-sizing: border-box;
- height: 400rpx;
- flex-wrap: wrap;
- transition: height .3s;
- .emoji-item{
- width: 64rpx;
- height: 64rpx;
- padding: 20rpx;
- .emoji-img{
- width: 64rpx;
- height: 64rpx;
- }
- }
- }
- }
- .container-box {
- position: fixed;
- display: flex;
- justify-content: center;
- align-items: center;
- left: 0;
- right: 0;
- bottom: 0;
- top: 0;
- background-color: rgba(0, 0, 0, 0.5);
- .video-message {
- width: 90vw;
- height: auto;
- }
- }
- }
- </style>
|