<template> <view class="content"> <view class="content-box" @touchstart="touchstart" id="content-box" :class="{'content-showfn':showFunBtn}"> <!-- 背景图- 定位方式 --> <!-- <image class="content-box-bg" :src="_user_info.chatBgImg" :style="{ height: imgHeight }"></image> --> <view class="content-box-loading" v-if="loading"> <view class="cu-load load-cuIcon loading"></view> </view> <view class="message" v-for="(item, index) in messageList" style="padding:20px 10px" :key="index" :id="`msg-${item.sendMsgTime}`"> <view class="message-item " :class="item.isItMe ? 'right' : 'left'"> <image class="img" :src="item.isItMe ?'https://thirdwx.qlogo.cn/mmopen/vi_32/VqRuEfXRcc088VjU82ohiaDEId1y7trwaf2LiaTkicv37PXjT4IbuEn3axDCg2sdf1dooQfHpsz9ktrCuxkbm5djg/132':'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3095823371,2737858048&fm=26&gp=0.jpg'" mode="" @tap="linkToBusinessCard(item.fromUserId)"></image> <view style="margin-top: 2px;"> <view v-if="!item.isItMe" style="margin-top: -6rpx;margin-left: 8px;font-size:13px">{{item.userName}}</view> <!-- contentType = 1 文本 --> <view class="content" v-if="item.msgType == 0"> {{ item.message }} </view> </view> <!-- contentType = 2 语音 --> <view class="content contentType2" :class="[{ 'content-type-right': item.isItMe }]" v-if="item.contentType == 2" @tap="handleAudio(item)" hover-class="contentType2-hover-class" :style="{width:`${130+(item.contentDuration*2)}rpx`}" > <view class="voice_icon" :class="[ { voice_icon_right: item.isItMe }, { voice_icon_left: !item.isItMe }, { voice_icon_right_an: item.anmitionPlay && item.isItMe }, { voice_icon_left_an: item.anmitionPlay && !item.isItMe } ]" ></view> <view class="">{{ item.contentDuration }}''</view> </view> <!-- contentType = 3 图片 --> <view class="content contentType3" v-if="item.msgType == 1" @tap="viewImg([item.message])" > <image :src="'http://139.9.163.126/gastric/' + item.message" class="img" mode="widthFix"></image> </view> </view> </view> </view> <!-- 底部聊天输入框 --> <view class="input-box" :class="{ 'input-box-mpInputMargin': mpInputMargin }"> <view class="input-box-flex"> <!-- #ifndef H5 --> <!-- <image v-if="chatType === 'voice'" class="icon_img" :src="require('@/static/voice.png')" @click="switchChatType('keyboard')"></image> --> <!-- <image v-if="chatType === 'keyboard'" class="icon_img" :src="require('@/static/keyboard.png')" @click="switchChatType('voice')"></image> --> <!-- #endif --> <view class="input-box-flex-grow"> <input v-if="chatType === 'voice'" type="text" class="content" id="input" v-model="formData.content" :hold-keyboard="true" :confirm-type="'send'" :confirm-hold="true" placeholder-style="color:#DDDDDD;" :cursor-spacing="10" @confirm="sendMsg(null)" /> <!-- <view class="voice_title" v-if="chatType === 'keyboard'" :style="{ background: recording ? '#c7c6c6' : '#FFFFFF' }" @touchstart.stop.prevent="startVoice" @touchmove.stop.prevent="moveVoice" @touchend.stop="endVoice" @touchcancel.stop="cancelVoice" > {{ voiceTitle }} </view> --> </view> <!-- 功能性按钮 --> <image class="icon_btn_add" :src="require('@/static/add.png')" @tap="switchFun"></image> <!-- #ifdef H5 --> <button class="btn" type="primary" size="mini" @touchend.prevent="sendMsg(null)">发送</button> <!-- #endif --> </view> <view class="fun-box" :class="{'show-fun-box':showFunBtn}"> <view class="cu-list grid col-5 no-border" style="background-color: #eaeaea;" > <view class="cu-item quick-item" v-for="(item, index) in funList" :index="index" :key="index" @click="clickGrid(index)" style="border-radius: 20px;background-color: #fff;margin: 0 15rpx" > <text class="lg text-gray" :class="'cuIcon-' + item.icon"></text> <view class="grid-text">{{ item.title }}</view> </view> </view> </view> </view> <!-- //语音动画 --> <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">{{voiceIconText}}</view> </view> </view> </template> <script> import { dateFormat } from '@/utils/index'; var socket = null; var previewImageFlag; export default { data() { return { essageList: [], timeId: null, msgGroupID: '', fromUserInfo: {}, formData: { content: '', limit: 15, index: 1 }, messageList: [], loading: false, //标识是否正在获取数据 imgHeight: '1000px', mpInputMargin: false, //适配微信小程序 底部输入框高度被顶起的问题 chatType:"voice", // 图标类型 'voice'语音 'keyboard'键盘 voiceTitle: '按住 说话', Recorder: uni.getRecorderManager(), Audio: uni.createInnerAudioContext(), recording: false, //标识是否正在录音 isStopVoice: false, //加锁 防止点击过快引起的当录音正在准备(还没有开始录音)的时候,却调用了stop方法但并不能阻止录音的问题 voiceInterval:null, voiceTime:0, //总共录音时长 canSend:true, //是否可以发送 PointY:0, //坐标位置 pageNum:1, voiceIconText:"正在录音...", showFunBtn:false, //是否展示功能型按钮 AudioExam:null, //正在播放音频的实例 funList: [ { icon:"album",title:"照片",uploadType:["album"] }, { icon:"camerafill",title:"拍摄",uploadType:["camera"] }, ], }; }, // 关闭websocket【必须在实例销毁之前关闭,否则会是underfined错误】 onShow() { if (previewImageFlag) { previewImageFlag = false; return; } let id = this.fromUserInfo.userID socket = uni.connectSocket({ url: `wss://inno.sh-sict.com/gastric-socket/websocket/${id}`, complete: (res) => { console.log(res); }, success: (res) => { console.log("连接成功"); }, }); socket.onOpen(() => { console.log('连接已打开') }); socket.onMessage(res => { console.log('收到服务器内容:'); this.messageList.push(JSON.parse(res.data)) this.$nextTick(() => { this.bindScroll(); }); // this.bindScroll(); }); socket.onClose((res) => { console.log(res); console.log('close:' + res.code + "原因:" + res.reason + "断开:" + res.wasClean) // openWs() }); // socket.onError((err)=>{console.log('发生错误:' +err)}) //socket创建和监听 function openWs() { socket = uni.connectSocket({ url: `wss://inno.sh-sict.com/gastric-socket/websocket/${id}`, complete: () => { console.log('重新连接'); } }); socket.onOpen(() => { console.log('连接已打开') }); //监听等事件... } //监测连接,如果断开就重连 function checkWs() { //心跳重连 if ([2, 3].includes(socket.readyState)) { //closing 或 closed socket.onClose(); socket = null; openWs(); } } //可以每隔一定时间就监测一次 this.timeId = setInterval(() => { // checkWs(); let data11 = { type: "维持连接消息", sendUserId: this.fromUserInfo.userID, msgGroupID: this.msgGroupID, // msgType:0, "userName": "史锦峰", message: this.formData.content, } let that = this socket.send({ data: JSON.stringify(data11), success: function(res) { console.log(res); console.log("重连成功"); }, fail: function(res) { console.log(res); console.log("失败"); } }) }, 35000); this.setGroup() }, // timeId destroyed() { clearInterval(this.timeId) this.closeSocket() }, methods: { // 关闭websocket【离开这个页面的时候执行关闭】 closeSocket() { console.log(this.timeId) let that = this socket.close({ success(res) { console.log("关闭成功", res) }, fail(err) { console.log("关闭失败", err) } }) }, //处理滚动 bindScroll( duration = 0) { const query = uni.createSelectorQuery().in(this); query.select('content-box').boundingClientRect(data => { uni.pageScrollTo({ scrollTop: 99999999999, duration }); }) .exec(); }, setGroup() { let data = this.fromUserInfo this.$http.post(`/gastric-cancer-im/im/user/createChatGroupByPatientID`, data).then(res => { if (res.data.code == 1) { this.msgGroupID = res.data.object.msgGroupID this.getMessageData(); uni.onKeyboardHeightChange(res => { if (res.height == 0) { // #ifdef MP-WEIXIN this.mpInputMargin = false; // #endif } else { this.showFunBtn = false; } }); } }).catch(err => { console.log(err) }) }, //获取消息 getMessageData() { this.loading = true let arr = []; let data = { pageNum:1, pageSize:15000 } this.$http.get(`/gastric-cancer-im/im/group/getPageGroupMsgByGroupId/${this.msgGroupID}?pageNum=${data.pageNum}&pageSize=${data.pageSize}`).then(res => { if (res.data.code == 1) { let arr = res.data.object.groupMessageList if(arr.length>0){ function compareUp(data, propertyName) { // 升序排序 if ((typeof data[0][propertyName]) != "number") { // 属性值为非数字 return function(object1, object2) { var value1 = object1[propertyName]; var value2 = object2[propertyName]; return value1.localeCompare(value2); } } else { return function(object1, object2) { // 属性值为数字 var value1 = object1[propertyName]; var value2 = object2[propertyName]; return value1 - value2; } } } arr = arr.sort(compareUp(arr, "sendMsgTime")) let result = [] let id = this.fromUserInfo.userID if (arr.length > 0) { arr.forEach((item) => { item.isItMe = item.sendUserId == id result.push(item) }); if(result.length > 0){ this.messageList = result } if(this.pageNum == 1){ this.$nextTick(() => { this.bindScroll(); }); }else{ const query = uni.createSelectorQuery().in(this); query.select('content-box').boundingClientRect(data => { uni.pageScrollTo({ scrollTop: 100, duration:0 }); }) .exec(); } } } this.loading = false; } }).catch(err => { console.log(err) }) }, //切换语音或者键盘方式 switchChatType(type) { // this.chatType = type; this.showFunBtn =false; }, //切换功能性按钮 switchFun(){ // this.chatType = 'keyboard' this.showFunBtn = !this.showFunBtn; uni.hideKeyboard() }, //发送消息 sendMsg(data) { let msgData = {} if(data && data.msgType == 1){ // 上传图片 msgData = { type: "group", sendUserId: this.fromUserInfo.userID, msgGroupID: this.msgGroupID, msgType:1, isItMe:true, userName: this.fromUserInfo.userName, sendMsgTime:dateFormat(), message: data.message } }else{ if(this.formData.content.length == 0){ return } msgData = { type: "group", sendUserId: this.fromUserInfo.userID, msgGroupID: this.msgGroupID, msgType:0, isItMe:true, userName: this.fromUserInfo.userName, sendMsgTime:dateFormat(), message: this.formData.content, } } let that = this console.log(msgData) socket.send({ data: JSON.stringify(msgData), success: function(res) { that.formData.content = '' uni.hideKeyboard() that.messageList.push(msgData); if(msgData.msgType == 0){ that.$nextTick(() => { let info = uni.createSelectorQuery().select('.content-box'); info.boundingClientRect(function(data) { console.log(data) uni.pageScrollTo({ scrollTop: 999999999, duration:0 }); }).exec() }); }else{ that.$nextTick(() => { setTimeout(()=>{ uni.pageScrollTo({ scrollTop: 99999, duration: 0, //小程序如果有滚动效果 input的焦点也会随着页面滚动... }); },550) }); } }, fail: function(res) { console.log(res); console.log("失败"); } }) }, //用户触摸屏幕的时候隐藏键盘 touchstart() { uni.hideKeyboard(); }, // userid 用户id linkToBusinessCard(userId) { this.$u.route({ url: 'pages/businessCard/businessCard', params: { userId } }); }, //点击宫格时触发 clickGrid(index){ if(index == 0){ this.chooseImage(['album']) }else if(index == 1){ this.chooseImage(['camera']) } }, //发送图片 chooseImage(sourceType){ uni.chooseImage({ count: 1, sourceType, sizeType:['compressed'], success:res=>{ this.showFunBtn = false //因为有一张图片, 输出下标[0], 直接输出地址 var imgFiles = res.tempFilePaths[0] // const formData = new FormData(); // formData.append("file", imgFiles); // // 上传图片 // this.$http.post(`/gastric-cancer-data/file/upload?bucketName=im.consult.image`,formData).then(res => { // console.log(res) // }) // 做成一个上传对象 let that = this let baseUrl = "" // #ifdef H5 baseUrl = "/gastric-api" // #endif // #ifdef MP-WEIXIN baseUrl = "https://inno.sh-sict.com/gastric-api" // #endif var uper = uni.uploadFile({ // 需要上传的地址 url:baseUrl+'/zuul/gastric-cancer-data/file/upload', // filePath 需要上传的文件 filePath: imgFiles, formData: { 'bucketName': 'im.consult.image', file:imgFiles }, name: 'file', complete(res1) { // 显示上传信息 const params = { msgType: 1, message: 'gastric-resources/im.consult.image/' + JSON.parse(res1.data).object.fileName, }; that.sendMsg(params) }, }); } }) }, //查看大图 viewImg(imgList){ if(imgList.length>0){ let arr = [] imgList.forEach((item) => { arr.push('http://139.9.163.126/gastric/' + item) }); previewImageFlag = true console.log(arr) uni.previewImage({ urls: arr, // #ifndef MP-WEIXIN indicator: 'number' // #endif }); } }, }, onPageScroll(e) { if(!this.loading){ if (e.scrollTop < 5) { this.pageNum = this.pageNum + 1 this.getMessageData(); } } }, onNavigationBarButtonTap({ index }) { if (index == 0) { //用户详情 设置 } else if (index == 1) { //返回按钮 this.$u.route({ type: 'switchTab', url: 'pages/home/home' }); } }, //返回按钮事件 onBackPress(e) { //以下内容对h5不生效 //--所以如果用浏览器自带的返回按钮进行返回的时候页面不会重定向 正在寻找合适的解决方案 this.$u.route({ type: 'switchTab', url: 'pages/home/home' }); return true; }, onLoad(info) { if(info.obj){ const obj = JSON.parse(decodeURIComponent(info.obj)); if(obj.hospitalId == this.$store.getters.userInfo.hospitalId){ obj.hospitalName = this.$store.getters.userInfo.hospitalName } console.log(obj) this.fromUserInfo = { hospitalId:obj.hospitalId, userID:obj.id, userName:obj.name, hospitalName:obj.hospitalName }; }else{ this.fromUserInfo = { userID: this.$store.getters.userInfo.userId, hospitalId: this.$store.getters.userInfo.hospitalId, userName:this.$store.getters.userInfo.name, hospitalName: this.$store.getters.userInfo.hospitalName }; } uni.setNavigationBarTitle({ title: this.fromUserInfo.hospitalName }); //录音开始事件 this.Recorder.onStart(e => { this.beginVoice(); }); //录音结束事件 this.Recorder.onStop(res => { clearInterval(this.voiceInterval); this.handleRecorder(res); }); //音频停止事件 this.Audio.onStop(e => { this.closeAnmition(); }); //音频播放结束事件 this.Audio.onEnded(e => { this.closeAnmition(); }); }, onReady() { uni.getSystemInfo({ success: res => { this.imgHeight = res.windowHeight + 'px'; } }); this.loading = false uni.onKeyboardHeightChange(res => { if (res.height == 0) { this.mpInputMargin = false; }else{ this.showFunBtn = false; } }); } }; </script> <style lang="scss" scoped> @import './index.scss' </style>