xie.bx %!s(int64=3) %!d(string=hai) anos
pai
achega
c7a64075a7

+ 364 - 0
im-ui/src/components/chat/ChatMessageItem.vue

@@ -0,0 +1,364 @@
+<template>
+	<div class="chat-msg-item">
+		<div class="chat-msg-tip" v-show="msgInfo.type==10">{{msgInfo.content}}</div>
+		<div class="chat-msg-normal" v-show="msgInfo.type!=10" :class="{'chat-msg-mine':mine}">
+			<div class="head-image">
+				<head-image :url="headImage" :id="msgInfo.sendId"></head-image>
+			</div>
+			<div class="chat-msg-content">
+				<div class="chat-msg-top">
+					<span>{{showName}}</span>
+					<chat-time :time="msgInfo.sendTime"></chat-time>
+				</div>
+				<div class="chat-msg-bottom" @contextmenu.prevent="showRightMenu($event)">
+					<span class="chat-msg-text" v-if="msgInfo.type==0" v-html="$emo.transform(msgInfo.content)"></span>
+					<div class="chat-msg-image" v-if="msgInfo.type==1">
+						<div class="img-load-box" v-loading="loading" element-loading-text="上传中.." element-loading-background="rgba(0, 0, 0, 0.4)">
+							<img class="send-image" :src="JSON.parse(msgInfo.content).thumbUrl" @click="showFullImageBox()" />
+						</div>
+						<span title="发送失败" v-show="loadFail" @click="handleSendFail" class="send-fail el-icon-warning"></span>
+					</div>
+					<div class="chat-msg-file" v-if="msgInfo.type==2">
+						<div class="chat-file-box" v-loading="loading">
+							<div class="chat-file-info">
+								<el-link class="chat-file-name" :underline="true" target="_blank" type="primary" :href="data.url">{{data.name}}</el-link>
+								<div class="chat-file-size">{{fileSize}}</div>
+							</div>
+							<div class="chat-file-icon">
+								<span type="primary" class="el-icon-document"></span>
+							</div>
+						</div>
+						<span title="发送失败" v-show="loadFail" @click="handleSendFail" class="send-fail el-icon-warning"></span>
+					</div>
+					<div class="chat-msg-voice" v-if="msgInfo.type==3" @click="handlePlayVoice()">
+						<audio controls :src="JSON.parse(msgInfo.content).url"></audio>
+					</div>
+				</div>
+			</div>
+
+		</div>
+		<right-menu v-show="menu && rightMenu.show" :pos="rightMenu.pos" :items="menuItems" @close="rightMenu.show=false"
+		 @select="handleSelectMenu"></right-menu>
+	</div>
+</template>
+
+<script>
+	import ChatTime from "./ChatTime.vue";
+	import HeadImage from "../common/HeadImage.vue";
+	import RightMenu from '../common/RightMenu.vue';
+
+	export default {
+		name: "messageItem",
+		components: {
+			ChatTime,
+			HeadImage,
+			RightMenu
+		},
+		props: {
+			mine: {
+				type: Boolean,
+				required: true
+			},
+			headImage: {
+				type: String,
+				required: true
+			},
+			showName: {
+				type: String,
+				required: true
+			},
+			msgInfo: {
+				type: Object,
+				required: true
+			},
+			menu:{
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				audioPlayState: 'STOP',
+				rightMenu: {
+					show: false,
+					pos: {
+						x: 0,
+						y: 0
+					}
+				}
+			}
+
+		},
+		methods: {
+			handleSendFail() {
+				this.$message.error("该文件已发送失败,目前不支持自动重新发送,建议手动重新发送")
+			},
+			showFullImageBox() {
+				let imageUrl = JSON.parse(this.msgInfo.content).originUrl;
+				if (imageUrl) {
+					this.$store.commit('showFullImageBox', imageUrl);
+				}
+			},
+			handlePlayVoice() {
+				if (!this.audio) {
+					this.audio = new Audio();
+				}
+				this.audio.src = JSON.parse(this.msgInfo.content).url;
+				this.audio.play();
+				this.handlePlayVoice = 'RUNNING';
+			},
+			showRightMenu(e) {
+				this.rightMenu.pos = {
+					x: e.x,
+					y: e.y
+				};
+				this.rightMenu.show = "true";
+			},
+			handleSelectMenu(item) {
+				this.$emit(item.key.toLowerCase(), this.msgInfo);
+			}
+		},
+		computed: {
+			loading() {
+				return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "loading";
+			},
+			loadFail() {
+				return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "fail";
+			},
+			data() {
+				return JSON.parse(this.msgInfo.content)
+			},
+			fileSize() {
+				let size = this.data.size;
+				if (size > 1024 * 1024) {
+					return Math.round(size / 1024 / 1024) + "M";
+				}
+				if (size > 1024) {
+					return Math.round(size / 1024) + "KB";
+				}
+				return size + "B";
+			},
+			menuItems() {
+				let items = [];
+				items.push({
+					key: 'DELETE',
+					name: '删除',
+					icon: 'el-icon-delete'
+				});
+				if (this.msgInfo.selfSend && this.msgInfo.id > 0) {
+					items.push({
+						key: 'RECALL',
+						name: '撤回',
+						icon: 'el-icon-refresh-left'
+					});
+				}
+				return items;
+			}
+		},
+		mounted() {
+			//console.log(this.msgInfo);
+		}
+	}
+</script>
+
+<style lang="scss">
+	.chat-msg-item {
+
+		.chat-msg-tip {
+			line-height: 50px;
+		}
+
+		.chat-msg-normal {
+			position: relative;
+			font-size: 0;
+			margin-bottom: 10px;
+			padding-left: 60px;
+			min-height: 68px;
+
+			.head-image {
+				position: absolute;
+				width: 40px;
+				height: 40px;
+				top: 0;
+				left: 0;
+			}
+
+			.chat-msg-content {
+				display: flex;
+				flex-direction: column;
+
+				.chat-msg-top {
+					display: flex;
+					flex-wrap: nowrap;
+					color: #333;
+					font-size: 14px;
+					line-height: 20px;
+
+					span {
+						margin-right: 12px;
+					}
+				}
+
+				.chat-msg-bottom {
+					text-align: left;
+
+					.chat-msg-text {
+						position: relative;
+						line-height: 22px;
+						margin-top: 10px;
+						padding: 10px;
+						background-color: #eeeeee;
+						border-radius: 3px;
+						color: #333;
+						display: inline-block;
+						font-size: 14px;
+
+						&:after {
+							content: "";
+							position: absolute;
+							left: -10px;
+							top: 13px;
+							width: 0;
+							height: 0;
+							border-style: solid dashed dashed;
+							border-color: #eeeeee transparent transparent;
+							overflow: hidden;
+							border-width: 10px;
+						}
+					}
+
+					.chat-msg-image {
+						display: flex;
+						flex-wrap: nowrap;
+						flex-direction: row;
+						align-items: center;
+
+						.send-image {
+							min-width: 300px;
+							min-height: 200px;
+							max-width: 600px;
+							max-height: 400px;
+							border: #dddddd solid 1px;
+							cursor: pointer;
+						}
+
+						.send-fail {
+							color: #e60c0c;
+							font-size: 30px;
+							cursor: pointer;
+							margin: 0 20px;
+						}
+					}
+
+					.chat-msg-file {
+						display: flex;
+						flex-wrap: nowrap;
+						flex-direction: row;
+						align-items: center;
+						cursor: pointer;
+
+						.chat-file-box {
+							display: flex;
+							flex-wrap: nowrap;
+							align-items: center;
+							width: 20%;
+							min-height: 80px;
+							border: #dddddd solid 1px;
+							border-radius: 3px;
+							background-color: #eeeeee;
+							padding: 10px 15px;
+
+							.chat-file-info {
+								flex: 1;
+								height: 100%;
+								text-align: left;
+								font-size: 14px;
+
+								.chat-file-name {
+									font-size: 16px;
+									font-weight: 600;
+									margin-bottom: 15px;
+								}
+							}
+
+							.chat-file-icon {
+								font-size: 50px;
+								color: #d42e07;
+							}
+						}
+
+						.send-fail {
+							color: #e60c0c;
+							font-size: 30px;
+							cursor: pointer;
+							margin: 0 20px;
+						}
+
+					}
+
+					.chat-msg-voice {
+						font-size: 14px;
+						cursor: pointer;
+
+						audio {
+							height: 45px;
+							padding: 5px 0;
+						}
+					}
+				}
+			}
+
+
+			&.chat-msg-mine {
+				text-align: right;
+				padding-left: 0;
+				padding-right: 60px;
+
+				.head-image {
+					left: auto;
+					right: 0;
+				}
+
+				.chat-msg-content {
+
+					.chat-msg-top {
+						flex-direction: row-reverse;
+
+						span {
+							margin-left: 12px;
+							margin-right: 0;
+						}
+					}
+
+					.chat-msg-bottom {
+						text-align: right;
+
+						.chat-msg-text {
+							margin-left: 10px;
+							background-color: #5fb878;
+							color: #fff;
+							display: inline-block;
+							vertical-align: top;
+							font-size: 14px;
+
+							&:after {
+								left: auto;
+								right: -10px;
+								border-top-color: #5fb878;
+							}
+						}
+
+						.chat-msg-image {
+							flex-direction: row-reverse;
+						}
+
+						.chat-msg-file {
+							flex-direction: row-reverse;
+						}
+					}
+				}
+			}
+
+		}
+	}
+</style>

+ 131 - 0
im-ui/src/components/chat/ChatVideoAcceptor.vue

@@ -0,0 +1,131 @@
+<template>
+	<div class="chat-video-acceptor">
+		<div>
+			<head-image :size="120" :url="this.friend.headImage" :id="this.friend.id"></head-image>
+		</div>
+		<div>
+			{{friend.nickName}} 请求和您进行视频通话...
+		</div>
+		<div class="acceptor-btn-group">
+		  <div  class="icon iconfont icon-phone-accept accept" @click="accpet()"></div>
+		  <div  class="icon iconfont icon-phone-reject reject" @click="reject()"></div>
+		</div>
+	</div>
+</template>
+
+<script>
+	import HeadImage from '../common/HeadImage.vue';
+	
+	export default {
+		name: "videoAcceptor",
+		components:{HeadImage},
+		props: {
+			friend:{
+				type: Object
+			}
+		},
+		data(){
+			return {
+				offer:{},
+				audio: new Audio()
+			}
+		},
+		methods:{
+			accpet(){
+				let info ={
+					friend: this.friend,
+					master: false,
+					offer: this.offer
+				}
+				this.$store.commit("showChatPrivateVideoBox",info);
+				this.close();
+			},
+			reject(){
+				this.$http({
+					url: `/webrtc/private/reject?uid=${this.friend.id}`,
+					method: 'post'
+				})
+				this.close();
+			},
+			failed(reason){
+				this.$http({
+					url: `/webrtc/private/failed?uid=${this.friend.id}&reason=${reason}`,
+					method: 'post'
+				})
+				this.close();
+			},
+			onCall(msgInfo){
+				console.log("onCall")
+				this.offer = JSON.parse(msgInfo.content);
+				if(this.$store.state.userStore.state == this.$enums.USER_STATE.BUSY){
+					this.failed("对方正忙,暂时无法接听");
+					return;
+				}
+				// 超时未接听
+				this.timer && clearTimeout(this.timer);
+				this.timer = setTimeout(()=>{
+					this.failed("对方未接听");
+				},30000)
+				this.audio.play();
+			},
+			onCancel(){
+				this.$message.success("对方取消了呼叫");
+				this.close();
+			},
+			handleMessage(msgInfo){
+				if(msgInfo.type == this.$enums.MESSAGE_TYPE.RTC_CALL){
+					this.onCall(msgInfo);
+				}else if(msgInfo.type == this.$enums.MESSAGE_TYPE.RTC_CANCEL){
+					this.onCancel();
+				}
+			},
+			close(){
+				this.timer && clearTimeout(this.timer);
+				this.audio.pause();
+				this.$emit("close");
+			},
+			initAudio(){
+				let url = require(`@/assets/audio/call.wav`);
+				this.audio.src = url;
+				this.audio.loop = true;
+			}
+		},
+		mounted() {
+			// 初始化语音
+			this.initAudio();
+			
+		}
+	}
+	
+</script>
+
+<style scoped lang="scss">
+	.chat-video-acceptor {
+		position: absolute;
+		right: 1px;
+		bottom: 1px;
+		width: 250px;
+		height: 250px;
+		padding: 20px;
+		text-align: center;
+		background-color: #eeeeee;
+		border: #dddddd solid 1px;
+		
+		.acceptor-btn-group {
+			display: flex;
+			justify-content: space-around;
+			margin-top: 20px;
+			
+			.icon {
+				font-size: 50px;
+				cursor: pointer;
+				&.accept {
+					color: green;
+				}
+				&.reject {
+					color: red;
+				}
+			}	
+		}
+	}
+</style>