Selaa lähdekoodia

加入环信IMsdk

xiaobin.zhang 2 vuotta sitten
vanhempi
commit
8fd86337f6
100 muutettua tiedostoa jossa 6587 lisäystä ja 36 poistoa
  1. 506 36
      App.vue
  2. 4 0
      chat/chatroom/chatroom.css
  3. 63 0
      chat/chatroom/chatroom.vue
  4. 4 0
      chat/components/chat/chat.css
  5. 256 0
      chat/components/chat/chat.vue
  6. 394 0
      chat/components/chat/emediaInvite/emediaInvite.css
  7. 266 0
      chat/components/chat/emediaInvite/emediaInvite.vue
  8. 65 0
      chat/components/chat/inputbar/inputbar.css
  9. 228 0
      chat/components/chat/inputbar/inputbar.vue
  10. 114 0
      chat/components/chat/inputbar/suit/attach/index.vue
  11. 73 0
      chat/components/chat/inputbar/suit/audio/audio.css
  12. 409 0
      chat/components/chat/inputbar/suit/audio/audio.vue
  13. 14 0
      chat/components/chat/inputbar/suit/audio/record_status.js
  14. 39 0
      chat/components/chat/inputbar/suit/emoji/emoji.css
  15. 85 0
      chat/components/chat/inputbar/suit/emoji/emoji.vue
  16. 169 0
      chat/components/chat/inputbar/suit/image/image.vue
  17. 95 0
      chat/components/chat/inputbar/suit/location/location.vue
  18. 65 0
      chat/components/chat/inputbar/suit/main/main.css
  19. 193 0
      chat/components/chat/inputbar/suit/main/main.vue
  20. 66 0
      chat/components/chat/inputbar/suit/ptopcall/ptopcall.vue
  21. 110 0
      chat/components/chat/inputbar/suit/videoComp/videoComp.vue
  22. 110 0
      chat/components/chat/longPressModal/index.vue
  23. 154 0
      chat/components/chat/msglist/msglist.css
  24. 449 0
      chat/components/chat/msglist/msglist.vue
  25. 25 0
      chat/components/chat/msglist/type/audio/audio.css
  26. 192 0
      chat/components/chat/msglist/type/audio/audio.vue
  27. 62 0
      chat/components/chat/msglist/type/audio/audioCtxFactory.js
  28. 5 0
      chat/components/chat/msglist/type/audio/playStatus.js
  29. 10 0
      chat/components/chat/msglist/type/file/index.css
  30. 139 0
      chat/components/chat/msglist/type/file/index.vue
  31. 64 0
      chat/components/chat/msgpackager.js
  32. 201 0
      chat/components/chat/msgstorage.js
  33. 14 0
      chat/components/chat/msgtype.js
  34. 0 0
      chat/components/chat/multiemedia/index.css
  35. 508 0
      chat/components/chat/multiemedia/index.nvue
  36. 45 0
      chat/components/chat/pushStorage.js
  37. 29 0
      chat/components/chat/swipedelete/swipedelete.css
  38. 74 0
      chat/components/chat/swipedelete/swipedelete.vue
  39. 234 0
      chat/conversation/chat.css
  40. 666 0
      chat/conversation/conversation.vue
  41. BIN
      chat/static/Emoji.png
  42. BIN
      chat/static/ad.png
  43. BIN
      chat/static/camora.png
  44. BIN
      chat/static/ctbg.png
  45. BIN
      chat/static/faces/btn_del.png
  46. BIN
      chat/static/faces/del.png
  47. BIN
      chat/static/faces/ee_1.png
  48. BIN
      chat/static/faces/ee_10.png
  49. BIN
      chat/static/faces/ee_11.png
  50. BIN
      chat/static/faces/ee_12.png
  51. BIN
      chat/static/faces/ee_13.png
  52. BIN
      chat/static/faces/ee_14.png
  53. BIN
      chat/static/faces/ee_15.png
  54. BIN
      chat/static/faces/ee_16.png
  55. BIN
      chat/static/faces/ee_17.png
  56. BIN
      chat/static/faces/ee_18.png
  57. BIN
      chat/static/faces/ee_19.png
  58. BIN
      chat/static/faces/ee_2.png
  59. BIN
      chat/static/faces/ee_20.png
  60. BIN
      chat/static/faces/ee_21.png
  61. BIN
      chat/static/faces/ee_22.png
  62. BIN
      chat/static/faces/ee_23.png
  63. BIN
      chat/static/faces/ee_24.png
  64. BIN
      chat/static/faces/ee_25.png
  65. BIN
      chat/static/faces/ee_26.png
  66. BIN
      chat/static/faces/ee_27.png
  67. BIN
      chat/static/faces/ee_28.png
  68. BIN
      chat/static/faces/ee_29.png
  69. BIN
      chat/static/faces/ee_3.png
  70. BIN
      chat/static/faces/ee_30.png
  71. BIN
      chat/static/faces/ee_31.png
  72. BIN
      chat/static/faces/ee_32.png
  73. BIN
      chat/static/faces/ee_33.png
  74. BIN
      chat/static/faces/ee_34.png
  75. BIN
      chat/static/faces/ee_35.png
  76. BIN
      chat/static/faces/ee_4.png
  77. BIN
      chat/static/faces/ee_5.png
  78. BIN
      chat/static/faces/ee_6.png
  79. BIN
      chat/static/faces/ee_7.png
  80. BIN
      chat/static/faces/ee_8.png
  81. BIN
      chat/static/faces/ee_9.png
  82. BIN
      chat/static/file.png
  83. BIN
      chat/static/groupTheme.png
  84. BIN
      chat/static/inform.png
  85. BIN
      chat/static/msgFile.png
  86. BIN
      chat/static/msgerr.png
  87. BIN
      chat/static/pic.png
  88. BIN
      chat/static/popleftarrow2x.png
  89. BIN
      chat/static/poprightarrow2x.png
  90. BIN
      chat/static/send.png
  91. BIN
      chat/static/theme2x.png
  92. BIN
      chat/static/voice.png
  93. BIN
      chat/static/voicemsg.png
  94. BIN
      chat/static/voicemsgmy.png
  95. 0 0
      hxChatSDK/Easemob-chat-4.1.7.js
  96. 37 0
      hxChatSDK/utils/Dispatcher.js
  97. 58 0
      hxChatSDK/utils/Observe.js
  98. 194 0
      hxChatSDK/utils/WebIM.js
  99. 96 0
      hxChatSDK/utils/WebIMConfig.js
  100. 3 0
      hxChatSDK/utils/broadcast.js

+ 506 - 36
App.vue

@@ -4,42 +4,373 @@
 	// // #ifndef H5
 	// let livePlayer = requirePlugin('live-player-plugin')
 	// // #endif
+	import _chunkArr from './hxChatSDK/utils/chunkArr';
+	let WebIM = (wx.WebIM = require("./hxChatSDK/utils/WebIM")["default"]);
+	let msgStorage = require("./chat/components/chat/msgstorage");
+	let msgType = require("./chat/components/chat/msgtype");
+	let disp = require("./hxChatSDK/utils/broadcast");
+	let logout = false;
+
+	import {
+		onGetSilentConfig
+	} from './chat/components/chat/pushStorage'
+
+
+
 	export default {
 		onLaunch: function(options) {
 			// this.globalData.appInitData();
 			this.globalData.InitUpdateManager();
 			this.globalData.getChatList();
 			// 
-			uni.hideTabBar()
+			uni.hideTabBar();
+
+			var me = this;
+			var logs = uni.getStorageSync("logs") || [];
+			logs.unshift(Date.now());
+			uni.setStorageSync("logs", logs);
+
+			disp.on("em.main.ready", function() {
+				calcUnReadSpot();
+			});
+			disp.on("em.chatroom.leave", function() {
+				calcUnReadSpot();
+			});
+			disp.on("em.chat.session.remove", function() {
+				calcUnReadSpot();
+			});
+			disp.on("em.chat.audio.fileLoaded", function() {
+				calcUnReadSpot();
+			});
+			disp.on("em.main.deleteFriend", function() {
+				calcUnReadSpot();
+			});
+			disp.on("em.chat.audio.fileLoaded", function() {
+				calcUnReadSpot();
+			}); //
+			disp.on("em.mian.profile.update", function() {
+				me.fetchUserInfoWithLoginId()
+			});
+			disp.on("em.mian.friendProfile.update", function() {
+				me.fetchFriendInfoFromServer()
+			});
+			WebIM.conn.listen({
+				onOpened(message) {
+					if (
+						getCurrentRoute() == "pages/login/login" ||
+						getCurrentRoute() == "pages/login_token/login_token"
+					) {
+						me.globalData.onLoginSuccess(
+							uni.getStorageSync("myUsername").toLowerCase()
+						);
+					}
+				},
+
+				onReconnect() {
+					uni.showToast({
+						title: "重连中...",
+						duration: 2000,
+					});
+				},
+
+				onSocketConnected() {
+					uni.showToast({
+						title: "socket连接成功",
+						duration: 2000,
+					});
+				},
+
+				onClosed() {
+					uni.showToast({
+						title: "退出登录",
+						icon: "none",
+						duration: 2000,
+					});
+					uni.redirectTo({
+						url: "../login/login",
+					});
+					me.globalData.conn.closed = true;
+					WebIM.conn.close();
+				},
+
+				onInviteMessage(message) {
+					me.globalData.saveGroupInvitedList.push(message);
+					disp.fire("em.invite.joingroup", message);
+				},
+
+				onReadMessage(message) {
+					//console.log('已读', message)
+				},
+
+				//onPresence为旧版 ,建议参考最新增删好友api文档 :http://docs-im.easemob.com/im/web/basics/buddy
+				onPresence(message) {
+					switch (message.type) {
+						case "unsubscribe":
+							break;
+							// 好友邀请列表
+						case "subscribe":
+							for (let i = 0; i < me.globalData.saveFriendList.length; i++) {
+								if (me.globalData.saveFriendList[i].from === message.from) {
+									me.globalData.saveFriendList[i] = message;
+									disp.fire("em.subscribe");
+									return;
+								}
+							}
+							msgStorage.saveReceiveMsg(message, "INFORM"); //存添加好友消息,方便展示通知
+							me.globalData.saveFriendList.push(message);
+							disp.fire("em.subscribe");
+							break;
+						case "subscribed":
+							uni.showToast({
+								title: "添加成功",
+								duration: 1000,
+							});
+							disp.fire("em.subscribed");
+							break;
+						case "unsubscribed":
+							disp.fire("em.unsubscribed", message);
+							break;
+						case "direct_joined":
+							saveGroups();
+							uni.showToast({
+								title: "已进群",
+								duration: 1000,
+							});
+							break;
+						case "memberJoinPublicGroupSuccess":
+							saveGroups();
+							uni.showToast({
+								title: "已进群",
+								duration: 1000,
+							});
+							break;
+						case "invite":
+							// 防止重复添加
+							for (
+								let i = 0; i < me.globalData.saveGroupInvitedList.length; i++
+							) {
+								if (me.globalData.saveGroupInvitedList[i].from === message.from) {
+									me.globalData.saveGroupInvitedList[i] = message;
+									disp.fire("em.invite.joingroup");
+									return;
+								}
+							}
+							me.globalData.saveGroupInvitedList.push(message);
+							msgStorage.saveReceiveMsg(message, "INFORM"); //存添加好友消息,方便展示通知
+							disp.fire("em.invite.joingroup");
+							break;
+						case "unavailable":
+							disp.fire("em.contacts.remove");
+							disp.fire("em.group.leaveGroup", message);
+							break;
+						case "deleteGroupChat":
+							disp.fire("em.invite.deleteGroup", message);
+							break;
+						case "leaveGroup":
+							disp.fire("em.group.leaveGroup", message);
+							break;
+						case "removedFromGroup":
+							disp.fire("em.group.leaveGroup", message);
+							break;
+						default:
+							break;
+					}
+				},
+
+				onRoster(message) {},
+
+				onVideoMessage(message) {
+					console.log("onVideoMessage: ", message);
+					if (message) {
+						msgStorage.saveReceiveMsg(message, msgType.VIDEO);
+					}
+					calcUnReadSpot(message);
+					ack(message);
+					onGetSilentConfig(message);
+				},
+
+				onAudioMessage(message) {
+					console.log("onAudioMessage", message);
+					if (message) {
+						if (onMessageError(message)) {
+							msgStorage.saveReceiveMsg(message, msgType.AUDIO);
+						}
+						calcUnReadSpot(message);
+						ack(message);
+						onGetSilentConfig(message);
+					}
+				},
+
+				onCmdMessage(message) {
+					console.log("onCmdMessage", message);
+					if (message) {
+						if (onMessageError(message)) {
+							msgStorage.saveReceiveMsg(message, msgType.CMD);
+						}
+						calcUnReadSpot(message);
+						ack(message);
+						onGetSilentConfig(message);
+					}
+				},
+
+				onTextMessage(message) {
+					console.log("onTextMessage", message);
+
+					if (message) {
+						if (onMessageError(message)) {
+							msgStorage.saveReceiveMsg(message, msgType.TEXT);
+						}
+
+						calcUnReadSpot(message);
+						ack(message);
+						onGetSilentConfig(message);
+					}
+				},
+
+				onEmojiMessage(message) {
+					console.log("onEmojiMessage", message);
+					if (message) {
+						if (onMessageError(message)) {
+							msgStorage.saveReceiveMsg(message, msgType.EMOJI);
+						}
+						calcUnReadSpot(message);
+						ack(message);
+						onGetSilentConfig(message);
+					}
+				},
+
+				onPictureMessage(message) {
+					console.log("onPictureMessage", message);
+					if (message) {
+						if (onMessageError(message)) {
+							msgStorage.saveReceiveMsg(message, msgType.IMAGE);
+						}
+						calcUnReadSpot(message);
+						ack(message);
+						onGetSilentConfig(message);
+					}
+				},
+
+				onFileMessage(message) {
+					console.log("onFileMessage", message);
+					if (message) {
+						if (onMessageError(message)) {
+							msgStorage.saveReceiveMsg(message, msgType.FILE);
+						}
+						calcUnReadSpot(message);
+						ack(message);
+						onGetSilentConfig(message);
+					}
+				},
+
+				// 各种异常
+				onError(error) {
+					console.log(error); // 16: server-side close the websocket connection
+					if (error.type == WebIM.statusCode.WEBIM_CONNCTION_OPEN_ERROR) {
+						uni.hideLoading();
+						disp.fire("em.error.passwordErr");
+					}
+					if (error.type == WebIM.statusCode.WEBIM_CONNCTION_AUTH_ERROR) {
+						uni.hideLoading();
+						disp.fire("em.error.tokenErr");
+					}
+
+					if (error.type == "socket_error") {
+						///sendMsgError
+						console.log("socket_errorsocket_error", error);
+						uni.showToast({
+							title: "网络已断开",
+							icon: "none",
+							duration: 2000,
+						});
+						disp.fire("em.error.sendMsgErr", error);
+					}
+				},
+			});
+			this.globalData.checkIsIPhoneX();
+
 		},
 		onShow(options) {
 			let scene = options.scene;
-			req.setStorage('scene',scene)
+			req.setStorage('scene', scene)
 			this.globalData.getVideoScene();
-			// // #ifndef H5
-			// // 分享卡片/订阅消息/扫码二维码/广告/朋友圈等场景才能调用getShareParams接口获取以下参数
-			// const sceneList = [1007, 1008, 1014, 1044, 1045, 1046, 1047, 1048, 1049, 1073, 1154, 1155]
-			// if (sceneList.includes(options.scene)) {
-			// 	livePlayer.getShareParams()
-			// 		.then(res => {
-			// 			// 房间号
-			// 			console.log('get room id', res.room_id)
-			// 			// 用户openid
-			// 			console.log('get openid', res.openid)
-			// 			// 分享者openid,分享卡片进入场景才有
-			// 			console.log('get share openid', res.share_openid)
-			// 			// 开发者在跳转进入直播间页面时,页面路径上携带的自定义参数,这里传回给开发者
-			// 			console.log('get custom params', res.custom_params)
-			// 			if(res.custom_params){
-			// 				req.setStorage('pidCode',res.custom_params.userId)
-			// 			}
-			// 		}).catch(err => {
-			// 			console.log('get share params', err)
-			// 		})
-			// }
-			// // #endif
 		},
+
 		globalData: {
+			phoneNumber: '',
+			unReadMessageNum: 0,
+			userInfo: null,
+			userInfoFromServer: null, //用户属性从环信服务器获取
+			friendUserInfoMap: new Map(), //好友属性
+			saveFriendList: [],
+			saveGroupInvitedList: [],
+			isIPX: false, //是否为iphone X
+			conn: {
+				closed: false,
+				curOpenOpt: {},
+				open(opt) {
+					uni.showLoading({
+						title: "正在初始化客户端..",
+						mask: true,
+					});
+					this.curOpenOpt = opt;
+					WebIM.conn.open(opt).then(() => {
+						//token获取成功,即可开始请求用户属性。
+						disp.fire("em.mian.profile.update");
+						disp.fire("em.mian.friendProfile.update");
+					}).catch((err) => {
+						console.log('>>>>>token获取失败', err)
+					});
+					this.closed = false;
+				},
+
+				reopen() {
+					if (this.closed) {
+						//this.open(this.curOpenOpt);
+						WebIM.conn.open(this.curOpenOpt);
+						this.closed = false;
+					}
+				},
+			},
+			onLoginSuccess: function(myName) {
+				uni.hideLoading();
+				uni.redirectTo({
+					url: "../conversation/conversation?myName=" + myName,
+				});
+			},
+
+			getUserInfo(cb) {
+				var me = this;
+
+				if (this.userInfo) {
+					typeof cb == "function" && cb(this.userInfo);
+				} else {
+					// 调用登录接口
+					uni.login({
+						success() {
+							uni.getUserInfo({
+								success(res) {
+									me.userInfo = res.userInfo;
+									typeof cb == "function" && cb(me.userInfo);
+								},
+							});
+						},
+					});
+				}
+			},
+			checkIsIPhoneX: function() {
+				const me = this;
+				uni.getSystemInfo({
+					success: function(res) {
+						// 根据 model 进行判断
+						if (res.model && res.model.search("iPhone X") != -1) {
+							me.isIPX = true;
+						}
+					},
+				});
+			},
+
+
 			//获取客服配置
 			getChatList() {
 				req.getRequest('/api/customer/list', {}, res => {
@@ -50,7 +381,7 @@
 				var getSysInfo = uni.getSystemInfoSync();
 				this.isIPhone = getSysInfo.model.indexOf("iPhone") != -1;
 				this.isIPhoneX = getSysInfo.model.indexOf("iPhone X") != -1 || getSysInfo.model.indexOf("iPhone 11") != -
-				1; // 是否为全面屏
+					1; // 是否为全面屏
 
 				this.isFullScreen = getSysInfo.screenHeight / getSysInfo.screenWidth >= 2.1; // iPhoneX底部空白高度为68,全面屏为20
 
@@ -58,16 +389,16 @@
 				this.height = getSysInfo.statusBarHeight;
 				this.screenHeight = getSysInfo.screenHeight;
 			},
-			getVideoScene(){
+			getVideoScene() {
 				let scene = req.getStorage('scene');
-				let arr = [1195,1193,1191,1184,1177,1176,1175,1201,1216,10001]
-				req.setStorage('isVideoScene',false)
-				arr.some(it=>{
-					if(it === scene){
-						req.setStorage('isVideoScene',true)
+				let arr = [1195, 1193, 1191, 1184, 1177, 1176, 1175, 1201, 1216, 10001]
+				req.setStorage('isVideoScene', false)
+				arr.some(it => {
+					if (it === scene) {
+						req.setStorage('isVideoScene', true)
 					}
 				})
-				console.log('isVideoScene==',req.getStorage('isVideoScene'))
+				console.log('isVideoScene==', req.getStorage('isVideoScene'))
 			},
 			InitUpdateManager() {
 				const updateManager = uni.getUpdateManager();
@@ -132,8 +463,8 @@
 
 			userInfo: null,
 			height: 0,
-			isredenvelopes:true,
-			isLayerAd:true,
+			isredenvelopes: true,
+			isLayerAd: true,
 
 			/**
 			 * 打开新页面
@@ -195,9 +526,148 @@
 			}
 
 		},
-		methods: {}
+		methods: {
+			async fetchUserInfoWithLoginId() {
+				const userId = await uni.WebIM.conn.user;
+				if (userId) {
+					try {
+						const {
+							data
+						} = await uni.WebIM.conn.fetchUserInfoById(userId)
+						this.globalData.userInfoFromServer = Object.assign({}, data[userId]);
+					} catch (error) {
+						console.log(error)
+						uni.showToast({
+							title: "用户属性获取失败",
+							icon: "none",
+							duration: 2000,
+						})
+					}
+
+				}
+			},
+			async fetchFriendInfoFromServer() {
+				let friendList = []
+				try {
+					const res = await uni.WebIM.conn.getContacts()
+					friendList = Object.assign([], res?.data)
+					if (friendList.length && friendList.length < 99) {
+						const {
+							data
+						} = await uni.WebIM.conn.fetchUserInfoById(friendList)
+						this.setFriendUserInfotoMap(data)
+					} else {
+						let newArr = _chunkArr(friendList, 99)
+						for (let i = 0; i < newArr.length; i++) {
+							const {
+								data
+							} = await uni.WebIM.conn.fetchUserInfoById(newArr[i])
+							this.setFriendUserInfotoMap(data)
+						}
+					}
+				} catch (error) {
+					console.log(error)
+					uni.showToast({
+						title: "用户属性获取失败",
+						icon: "none"
+					})
+				}
+
+			},
+			setFriendUserInfotoMap(data) {
+				if (Object.keys(data).length) {
+					for (const key in data) {
+						if (Object.hasOwnProperty.call(data, key)) {
+							const values = data[key];
+							Object.values(values).length && this.globalData.friendUserInfoMap.set(key, values);
+						}
+					}
+				}
+			}
+		}
 	};
+
+	/*--------环信SDK消息-----------------------*/
+
+	function ack(receiveMsg) {
+		// 处理未读消息回执
+		var bodyId = receiveMsg.id; // 需要发送已读回执的消息id
+
+		var ackMsg = new WebIM.message("read", WebIM.conn.getUniqueId());
+		ackMsg.set({
+			id: bodyId,
+			to: receiveMsg.from,
+		});
+		WebIM.conn.send(ackMsg.body);
+	}
+
+	function onMessageError(err) {
+		if (err.type === "error") {
+			uni.showToast({
+				title: err.errorText,
+			});
+			return false;
+		}
+
+		return true;
+	}
+
+	function getCurrentRoute() {
+		let pages = getCurrentPages();
+		if (pages.length > 0) {
+			let currentPage = pages[pages.length - 1];
+			return currentPage.route;
+		}
+		return "/";
+	}
+
+	// 包含陌生人版本
+	//该方法用以计算本地存储消息的未读总数。
+	function calcUnReadSpot(message) {
+		let myName = uni.getStorageSync("myUsername");
+		let pushObj = uni.getStorageSync("pushStorageData")
+		let pushAry = pushObj[myName] || []
+		uni.getStorageInfo({
+			success: function(res) {
+				let storageKeys = res.keys;
+				let newChatMsgKeys = [];
+				let historyChatMsgKeys = [];
+				storageKeys.forEach((item) => {
+					if (item.indexOf(myName) > -1 && item.indexOf("rendered_") == -1) {
+						newChatMsgKeys.push(item);
+					}
+				});
+				let count = newChatMsgKeys.reduce(function(result, curMember, idx) {
+					let newName = curMember.split(myName)[0]
+					let chatMsgs;
+					chatMsgs = uni.getStorageSync(curMember) || [];
+					//过滤消息来源与当前登录ID一致的消息,不计入总数中。
+					chatMsgs = chatMsgs.filter((msg) => msg.yourname !== myName);
+					if (pushAry.includes(newName)) return result
+					return result + chatMsgs.length;
+				}, 0);
+				getApp().globalData.unReadMessageNum = count;
+				disp.fire("em.unreadspot", message);
+			},
+		});
+	}
+
+	function saveGroups() {
+		var me = this;
+		return WebIM.conn.getGroup({
+			limit: 50,
+			success: function(res) {
+				uni.setStorage({
+					key: "listGroup",
+					data: res.data,
+				});
+			},
+			error: function(err) {
+				console.log(err);
+			},
+		});
+	}
 </script>
 <style>
 	@import "./app.css";
-</style>
+</style>

+ 4 - 0
chat/chatroom/chatroom.css

@@ -0,0 +1,4 @@
+page{
+	background-color: #FAFAFA;
+	height: 100%;
+}

+ 63 - 0
chat/chatroom/chatroom.vue

@@ -0,0 +1,63 @@
+<template>
+<chat id="chat" :username="username" ref="chat" chatType="singleChat" @onClickInviteMsg="onClickMsg"></chat>
+</template>
+
+<script>
+let disp = require("../../hxChatSDK/utils/broadcast");
+import chat from "../../chat/components/chat/chat.vue";
+
+export default {
+  data() {
+    return {
+      username: {
+        your: ""
+      }
+    };
+  },
+
+  components: {
+    chat
+  },
+  props: {},
+  // options = 系统传入的 url 参数
+  onLoad(options) {
+    let username = JSON.parse(options.username);
+    this.setData({
+      username: username
+    });
+	// 生成的支付宝小程序在onLoad里获取不到,这里放到全局变量下
+	uni.username = username;
+    uni.setNavigationBarTitle({
+      title: username?.yourNickName || username?.your
+    });
+  },
+
+  onUnload(options) {
+    disp.fire("em.chatroom.leave");
+  },
+
+  onPullDownRefresh: function () {
+    uni.showNavigationBarLoading();
+    this.$refs.chat.getMore();
+    // 停止下拉动作
+    uni.hideNavigationBarLoading();
+    uni.stopPullDownRefresh();
+  },
+  methods: {
+	  onClickMsg(msg){
+		msg.action = 'join'
+		uni.navigateTo({
+			url: "../emedia/index?srcData="+JSON.stringify(msg)
+		});
+	  },
+    onNavigationBarButtonTap(e) {
+      uni.navigateTo({
+          url: `/pages/moreMenu/moreMenu?username=${this.username.your}&type=singleChat`
+      })
+  },
+  }
+};
+</script>
+<style>
+@import "./chatroom.css";
+</style>

+ 4 - 0
chat/components/chat/chat.css

@@ -0,0 +1,4 @@
+.main {
+	width: 100%;
+	height: 100%;
+}

+ 256 - 0
chat/components/chat/chat.vue

@@ -0,0 +1,256 @@
+<template>
+	<view class="main">
+		<view class="main">
+			<chatSuitAudio ref="chatSuitAudio" :username="username" :chatType="chatType" @newAudioMsg="saveSendMsg">
+			</chatSuitAudio>
+
+			<chatMsglist ref="chatMsglist" :username="username" @msglistTap="normalScroll" @clickMsg="clickMsg"
+				id="chat-msglist"></chatMsglist>
+		</view>
+		<chatInputbar ref="chatInputbar" :username="username" :chatType="chatType" @newTextMsg="saveSendMsg"
+			@newImageMsg="saveSendMsg" @newLocationMsg="saveSendMsg" @newVideoMsg="saveSendMsg"
+			@tapSendAudio="toggleRecordModal" @inputFocused="shortScroll" @inputBlured="normalScroll"
+			@makeVideoCall="onMakeVideoCall" @makeAudioCall="onMakeAudioCall" v-show="!showEmediaInvite"></chatInputbar>
+
+		<chatEmediaInvite :username="username" :action="action" @onStartConfr="onStartConfr" @goBack="onGoBack"
+			v-if="showEmediaInvite" />
+	</view>
+
+</template>
+
+<script>
+	let msgStorage = require("./msgstorage");
+	let msgType = require("./msgtype");
+	import chatMsglist from "./msglist/msglist";
+	import chatInputbar from "./inputbar/inputbar";
+	import chatSuitAudio from "./inputbar/suit/audio/audio";
+	import chatEmediaInvite from "./emediaInvite/emediaInvite.vue"
+	import chatMultiEmedia from "./multiemedia/index.nvue"
+	export default {
+		data() {
+			return {
+				__comps__: {
+					msglist: null,
+					inputbar: null,
+					audio: null,
+				},
+				inputbarVisible: 'block',
+				action: null,
+				pubUrl: '',
+				subUrl: '',
+				showEmedia: false,
+				showmultiEmedia: false,
+				showEmediaInvite: false,
+				emediaAction: null,
+				multiEmediaVisible: 'block',
+				confrId: '',
+				groupId: '',
+				confrMember: []
+			};
+		},
+
+		components: {
+			chatMsglist,
+			chatInputbar,
+			chatSuitAudio,
+			chatEmediaInvite,
+			chatMultiEmedia
+		},
+		props: {
+			username: {
+				type: Object,
+				default: () => ({}),
+			},
+			chatType: {
+				type: String,
+				default: msgType.chatType.SINGLE_CHAT,
+			},
+		},
+		computed: {
+			computedUserName() {
+				return this.username
+			}
+		},
+
+		mounted() {
+			this.username = uni.username;
+			uni.$on('createConfrSuccess', this.onCreateConfrSuccess)
+		},
+
+		moved() {},
+
+		destroyed() {},
+
+		methods: {
+			toggleRecordModal() {
+				this.$refs.chatSuitAudio.toggleRecordModal();
+			},
+
+			normalScroll() {
+				this.$refs.chatMsglist.normalScroll();
+				this.$refs.chatInputbar.cancelEmoji();
+				this.$refs.chatInputbar.closeFunModal();
+			},
+			clickMsg(msg) {
+				this.$emit('onClickInviteMsg', msg)
+				console.log('点击消息上一级', msg)
+
+			},
+			shortScroll() {
+				this.$refs.chatMsglist.shortScroll();
+			},
+
+			saveSendMsg(evt) {
+				msgStorage.saveMsg(evt.msg, evt.type);
+				this.$refs.chatInputbar.cancelEmoji();
+				this.$refs.chatInputbar.closeFunModal()
+			},
+
+			getMore() {
+				this.$refs.chatMsglist.getHistoryMsg();
+			},
+			onMakeVideoCall() {
+				this.setData({
+					showEmediaInvite: true,
+					inputbarVisible: 'none',
+					action: 'create'
+					//showEmedia: true
+				})
+				console.log(this.showEmediaInvite)
+			},
+			onStartConfr(data) {
+				console.log('发起邀请的回调', data)
+				this.setData({
+					showEmediaInvite: false,
+					showmultiEmedia: true,
+					multiEmediaVisible: 'block',
+					inputbarVisible: 'none',
+					confrMember: data.confrMember,
+					emediaAction: {
+						action: 'create'
+					}
+				})
+
+				this.$emit('onMakeVideoCall', {
+					confrMember: data.confrMember,
+					groupId: this.username.groupId
+				})
+			},
+			onCreateConfrSuccess(data) {
+				console.log('创建会成功议回调', data)
+				this.setData({
+					confrId: data.confrId
+				})
+				getApp().globalData.confrId = data.confrId
+				this.sendInviteMsg(this.confrMember, data.confrId, data)
+			},
+			onGoBack() {
+				this.setData({
+					showEmediaInvite: false,
+					showmultiEmedia: true,
+					multiEmediaVisible: 'block',
+					inputbarVisible: 'none',
+					confrMember: []
+				})
+			},
+
+			onInviteMember(e) {
+				let username = this.username;
+				if (!this.username.groupId) {
+					username.groupId = e.detail
+				}
+
+				this.setData({
+					action: 'invite',
+					showEmediaInvite: true,
+					inputbarVisible: 'none',
+					//showmultiEmedia: false
+					multiEmediaVisible: 'none',
+					// username
+				})
+			},
+			onMakeAudioCall() {
+				this.setData({
+					showEmediaInvite: true,
+					showmultiEmedia: false,
+					inputbarVisible: 'none'
+				})
+			},
+			onClickInviteMsg(data) {
+				console.log('收到邀请消息')
+				console.log(data)
+				let confrId = data.conferenceId
+				let msg_extension = typeof(data.msg_extension) == 'string' ? JSON.parse(data.msg_extension) : data
+					.msg_extension
+				let password = data.password || ''
+				this.setData({
+					emediaAction: {
+						action: 'join',
+						confrId: confrId,
+						password: password,
+						roomName: data.roomName || ''
+					},
+					showEmediaInvite: false,
+					showmultiEmedia: true,
+					inputbarVisible: 'none',
+					groupId: msg_extension.group_id
+				})
+			},
+			onHangup() {
+				this.setData({
+					showEmediaInvite: false,
+					showmultiEmedia: false,
+					inputbarVisible: 'block'
+				})
+				getApp().globalData.confrId = ''
+			},
+			sendInviteMsg(members, confrId, data) {
+				console.log("%c members", "background: green")
+				console.log(members)
+				console.log('进入发邀请的函数', members)
+				console.log('this.username.groupId----', this.username.groupId)
+
+				console.log('confrId', confrId)
+				console.log('data', data)
+				members && members.forEach((value) => {
+					let id = uni.WebIM.conn.getUniqueId();
+					let msg = new uni.WebIM.message('txt', id);
+
+					msg.set({
+						msg: wx.WebIM.conn.context.userId + ' invite you to video call',
+						from: wx.WebIM.conn.context.userId,
+						to: value,
+						roomType: false,
+						chatType: 'singleChat',
+						ext: {
+							msg_extension: JSON.stringify({
+								inviter: wx.WebIM.conn.context.userId,
+								group_id: this.username.groupId
+							}),
+							// roomName: data&&data.roomName || '',
+							password: '123456',
+							conferenceId: getApp().globalData.confrId
+						},
+						success(id, serverMsgId) {
+							console.log('发送邀请消息成功 to: ' + value)
+							//disp.fire('em.chat.sendSuccess', id, me.data.userMessage);
+						},
+						fail(id, serverMsgId) {
+							console.log('发送邀请消息失败了')
+						}
+					});
+
+					// if(this.chatType == msgType.chatType.CHAT_ROOM){
+					// 	msg.setGroup("groupchat");
+					// }
+					console.log('发送邀请', msg.body)
+					uni.WebIM.conn.send(msg.body);
+
+				})
+			},
+		},
+	};
+</script>
+<style>
+	@import "./chat.css";
+</style>

+ 394 - 0
chat/components/chat/emediaInvite/emediaInvite.css

@@ -0,0 +1,394 @@
+page{
+	height: 100%;
+	width: 100%;
+}
+.wraper{ 
+	height: 100%;
+	position: fixed;
+	z-index: 999;
+	width: 100%;
+	top: 0;
+	left: 0;
+	background: #fff;
+}
+.main_title{
+	background-color: #fff;
+	width: 100%;
+	height: 128rpx;
+	position: fixed;
+	top: 0;
+	z-index: 9;
+}
+.main_title text{
+	line-height: 96rpx;
+	font-size: 64rpx;
+	font-weight: 400;
+	margin-left: 32rpx;
+}
+.main_title_hide{
+	top: -128rpx;
+	transition: top 0.5s;
+}
+.main_title_show{
+	top: 0;
+	transition: top 0.5s;
+}
+.search,.search_input {
+	width: 100%;
+	height: 88rpx;
+	background-color: #fff;
+	display: flex;
+	align-items: center;
+	top: 0;
+	left: 0;
+}
+.search {
+	justify-content: space-around;
+}
+.search_input {
+	justify-content: space-around;
+}
+.search view, .search_input view {
+	height: 64rpx;
+	line-height: 64rpx;
+	background-color: #F5F5F5;
+	border-radius: 16rpx;
+	text-align: center;
+	display: flex;
+	align-items: center;
+	margin: 0 32rpx;
+}
+.search view {
+	width: 100%;
+	justify-content: center;
+	align-items: center;
+}
+.search image {
+	display: block;
+	width: 6%;
+	height: 53%;
+	margin-right: 10rpx;
+}
+.search_input view {
+	padding-left: 24rpx;
+	text-align: left;
+	flex: 1;
+	margin: 0 28rpx 0 32rpx;
+}
+.search icon,.search_input icon {
+	display: inline-block;
+	margin: 4rpx 12rpx 0;
+	font-size: 24rpx;
+}
+.search text {
+	font-size: 30rpx;
+	color: #9B9B9B;
+}
+
+.search_input text {
+	display: inline-block;
+	height: 60rpx;
+	line-height: 60rpx;
+	font-size: 30rpx;
+	color: #0873DE;
+	margin-right: 32rpx;
+}
+.search_input input {
+	font-size: 28rpx;
+	width: 90%;
+}
+
+.scrollWraper{
+	flex: 1;
+	position: relative;
+}
+
+/*.contain {
+	margin-top: 40px;
+}*/
+.chat,.chat_groups,.chat_lists{
+	position: relative;
+	width: 100%;
+	height: 90rpx;
+	display: flex;
+	align-items: center;
+}
+.chat,.chat_groups {
+	border-bottom: 0.5px solid #E5E5E5;
+}
+.chat image {
+	width: 6%;
+	height: 50%;
+	margin:0 26rpx 0 20rpx;
+}
+.chat .em-msgNum {
+	position: absolute;
+	right: 20rpx;
+	height: 34rpx;
+	border-radius: 20rpx;
+	min-width: 30rpx;
+	background: #f04134;
+	color: #fff;
+	line-height: 40rpx;
+	text-align: center;
+	padding: 0px 4rpx 4rpx 4rpx;
+}
+.chat_groups image {
+	width: 8%;
+	height: 54%;
+	margin:0 20rpx 0 16rpx;
+}
+.chat_lists image {
+	width: 12%;
+	height: 100%;
+	margin-right: 4rpx;
+}
+.chat text,.chat_groups text,.chat_lists text {
+	font-size: 28rpx;
+}
+.numbers {
+	width: 100%;
+	height: 100rpx;
+}
+.nbr_header {
+	height: 2rpx;
+	background-color: #D4D4D4;
+	line-height: 40rpx;
+	padding-left: 32rpx;
+}
+.nbr_body {
+	width: 70%;
+	height: 100rpx;
+	display: inline-block;
+	font-size: 32rpx;
+	line-height: 100rpx;
+	margin-right: 0;
+	float: left;
+}
+.mask {
+	background-color: black;
+	opacity: 0.4;
+	position: absolute;
+	top: 40rpx;
+	left: 0;
+	right: 0;
+	bottom: 0;
+}
+.delete {
+	width: 60rpx;
+	height: 70rpx;
+	float: right;
+	margin-top: 12rpx;
+}
+.delete image {
+	width: 32rpx;
+	height: 4rpx;
+}
+.info {
+	margin-top: 10rpx;
+	width: 12%;
+	height: 80rpx;
+	display: inline-block;
+	float: left;
+}
+.info image {
+	width: 68%;
+	height: 70%;
+	display: inline-block;
+	margin: 12rpx auto auto 12rpx;
+}
+
+.otherItem{
+	height: 108rpx;
+	display: flex;
+  	align-items: center;
+  	border-bottom: 0.5px solid #E5E5E5;
+  	margin:0 32rpx;
+  	position: relative;
+}
+.otherItem image{
+	width: 72rpx;
+	height: 72rpx;
+	margin: 0 22rpx 0 0;
+}
+.otherItem text{
+	font-size: 34rpx;
+	color: #000;
+}
+.otherItem .line{
+	position: absolute;
+	bottom: 0;
+	width: 100%;
+	height:.5px;
+}
+
+.content{
+	box-sizing: border-box; 
+} 
+
+.location{
+	width: 100%;
+} 
+.location_top{
+	height: 76rpx;
+	line-height: 76rpx;
+	background: #f4f4f4;
+	color: #606660;
+	font-size: 28rpx;
+	padding: 0 20rpx;
+} 
+.location_bottom{
+	height: 140rpx;
+	line-height: 140rpx;
+	color: #d91f16;
+	font-size: 28rpx;
+	border-top: 0.5px #E5E5E5 solid;
+	border-bottom: 0.5px #E5E5E5 solid;
+	padding: 0 20rpx;
+	align-items: center;
+	display: -webkit-flex;
+} 
+.address_top{
+	height: 80rpx;
+    line-height: 70rpx;
+    background: #FAFAFA;
+    color: #9B9B9B;
+    font-size: 17px;
+    padding: 10rpx 32rpx;
+} 
+.address_bottom{
+	height: 108rpx;
+	line-height: 108rpx;
+	background: #fff;
+	color: #000000;
+	font-size: 34rpx;
+	border-bottom: 0.5px #E5E5E5 solid;
+	margin: 0 32rpx;
+	display: flex;
+	align-items: center;
+} 
+.address_bottom image{
+	width: 72rpx;
+	height: 72rpx;
+	margin-right: 24rpx;
+}
+.address_bottom text{
+	overflow: hidden;
+	text-overflow: ellipsis;
+	flex: 1;
+}
+.location_img{
+	width: 48rpx;
+	height: 48rpx;
+	position: absolute;
+	right: 20rpx;
+	top: 125rpx;
+} 
+.add_city{
+	width: 228rpx;
+	height: 60rpx;
+	line-height: 60rpx;
+	text-align: center;
+	border: 0.5px solid #E5E5E5;
+	color: #000000;
+	margin-right: 20rpx;
+} 
+.add_citying{
+	width: 228rpx;
+	height: 60rpx;
+	line-height: 60rpx;
+	text-align: center;
+	border: 2rpx solid #09bb07;
+	color: #09bb07;
+	margin-right: 20rpx;
+} 
+.orientation{
+	white-space:normal;
+	display: inline-block;
+ 	width: 45rpx;
+ 	height:50rpx;
+	font-size: 28rpx;
+	font-weight: bold;
+ 	color: rgb(88, 87, 87);
+ 	text-align: center;
+} 
+.orientation_region{
+	padding: 10rpx 0px; 
+	width: 28rpx;
+	font-size: 20rpx;
+	position: fixed;
+	top: 50%;
+	right: 0;
+	transform:translate(0,-50%);
+	border-radius: 10px
+} 
+.orientation_city{
+	height: 24rpx;
+	width: 24rpx;
+	line-height: 24rpx;
+	font-size: 18rpx;
+	color: #000;
+	text-align: center;
+} 
+.active{
+	color: #fff;
+	background-color: #0873DE;
+	border-radius: 50%;
+}
+.list-fixed{
+	position: fixed;
+	width: 100%;
+	z-index: 999; 
+	height: 56rpx;
+	line-height: 56rpx; 
+	background: #EBEBEB;
+	color: #999999;
+	font-size: 28rpx;
+	padding: 0 20rpx;
+	top: 128rpx;
+}
+.fixed-title{
+	color:#2cc1d1;
+}
+
+.goTop{
+	top: 0;
+	transition: top 0.5s;
+	padding-bottom: 104rpx!important;
+}
+.goTopX{
+	top: 0rpx;
+	transition: top 0.5s;
+	padding-bottom: 140rpx!important;
+}
+.goback{
+	top: 128rpx;
+	transition: top 0.5s;
+}
+.fixedTitleTop{
+	top: 0;
+}
+.tap_mask{
+	width: 100%;
+	overflow: hidden;
+}
+.btnWraper{
+	align-items: center;
+	justify-content: center;
+	display: flex;
+	flex-direction: column;
+}
+.button{
+	width: 80%;
+	height: 80rpx;
+	background: green;
+	border-radius: 40rpx;
+	right: 0;
+	left: 0;
+	color: #fff;
+	font-size: 16px;
+	margin: 10rpx;
+}
+.red{
+	background: red;
+}

+ 266 - 0
chat/components/chat/emediaInvite/emediaInvite.vue

@@ -0,0 +1,266 @@
+<template>
+	<view class="wraper">
+		<view class="search" v-if="search_btn">
+			<view bindtap="openSearch">
+				<icon type="search" size="13"></icon>
+				<text>搜索</text>
+			</view>
+		</view>
+	
+		<view class="search_input" v-if="search_friend">
+			<view>
+				<icon type="search" size="13"></icon>
+				<input placeholder="搜索" 
+					placeholder-style="color:#CFCFCF;line-height:20px;font-size:12px;" 
+					auto-focus
+					confirm-type="search"
+					type='text'
+					bindconfirm="onSearch"
+					bindinput="onInput"
+					v-model="input_code"
+					>
+					<icon type="clear" size="13" catchtap='clearInput' v-if="show_clear"></icon>
+			</view>
+			<text bindtap="cancel">取消</text>
+		</view>
+		
+		<scroll-view
+			class="content" 
+			enable-back-to-top
+			scroll-y="true"
+			scroll-with-animation="true" 
+			:style="{height: second_height + 'px'}"
+		> 
+	
+		<checkbox-group v-for="item in renderList" :data-item="item" :key="item.member||item.owner"  :id="item.id" :data-id='item.id' @change="checkboxChange"> 
+	
+			<view class="tap_mask" @tap="into_room" :data-username="item.member||item.owner">
+	          	<view class="address_bottom" :data-username="item.member||item.owner" @tap="into_room">
+					<image src="../../../static/theme2x.png" @tap="into_room" :data-username="item.member||item.owner"></image>
+		          	<text @tap="into_room" :data-username="item.member||item.owner">{{item.member||item.owner}}</text>
+		          	<checkbox :value="item.member||item.owner" :checked="item.checked" :disabled="item.disabled"/>
+	      		</view>
+	  		</view>
+	
+		</checkbox-group>
+		</scroll-view>
+	
+		<view class="btnWraper">
+			<button class="button" @tap="startConfr">{{buttonText}}</button>
+			<button class="button red" @tap="goBack">返回</button>
+		</view>
+	
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				search_btn: true,
+				search_friend: false,
+				groupMember: [],
+				serchList: [],
+		
+				checkedValue: [],
+				renderList: [],
+				buttonText: '发起会议',
+				second_height: 450,
+				show_clear: false,
+			}
+		},
+		props: {
+		  username: {
+		    type: Object,
+		    default: () => ({})
+		  },
+		  action: {
+		    type: String,
+		    default: ''
+		  }
+		},
+		mounted() {
+			console.log('邀请页面的参数', this)
+			console.log(this.properties)
+			var that = this
+			// 获取系统信息
+			wx.getSystemInfo({
+				success: function (res) {
+					console.log('height=' + res.windowHeight);
+					console.log('width=' + res.windowWidth);
+					// 计算主体部分高度,单位为px
+					that.setData({
+					  // second部分高度 = 利用窗口可使用高度 - first部分高度(这里的高度单位为px,所有利用比例将300rpx转换为px)
+					  second_height: res.windowHeight - res.windowWidth / 750 * 300
+					})
+				}
+			})
+
+			if(this.action == 'invite'){
+				this.setData({
+					buttonText: '邀请'
+				})
+			}
+
+			var roomId = this.username&&this.username.groupId
+			console.log('roomId', this.username)
+			roomId&&this.getGroupMember(roomId)
+		},
+		methods: {
+			getGroupMember: function(roomId){
+				var me = this;
+				// 获取群成员
+				var pageNum = 1,
+					pageSize = 1000;
+				var options = {
+					pageNum: pageNum,
+					pageSize: pageSize,
+					groupId: roomId,
+					success: function(resp){
+						console.log('获取群成员', resp)
+						if(resp && resp.data){
+							me.setData({
+								groupMember: resp.data
+							});
+							me.getRenderList(resp.data)
+						}
+					},
+					error: function(err){
+	
+					}
+				};
+				uni.WebIM.conn.listGroupMember(options);
+			},
+	
+			getRenderList(list){
+				console.log('this.checkedValue', this.checkedValue)
+				let serchList = list.map((item) => {
+					for (var i = 0; i < this.checkedValue.length; i++) {
+						if((item.member&&item.member.indexOf(this.checkedValue[i]) != -1) || (item.owner&&item.owner.indexOf(this.checkedValue[i]) != -1)){
+							item.checked = true
+							return item
+							break;
+						}else{
+							item.checked = false
+						}
+					}
+					return item
+				})
+	
+				serchList.forEach((item) => {
+					if(item.member == wx.WebIM.conn.context.userId || item.owner == wx.WebIM.conn.context.userId){
+						item.disabled = true
+					}
+					
+				})
+				this.setData({
+					renderList: serchList
+				})
+				console.log('serchList >>>>', serchList)
+	
+			},
+	
+	
+			checkboxChange: function (e) {
+				console.log('checkbox发生change事件,携带value值为:', e)
+				if(this.checkedValue.indexOf(e.detail.value) == -1 && e.detail.value[0]){
+					this.checkedValue.push(e.detail.value[0])
+					console.log(this.checkedValue)
+				}else{
+					let value = e.target.dataset.item.name
+					this.checkedValue.splice(this.checkedValue.indexOf(value), 1)
+					console.log(this.checkedValue)
+				}
+			},
+	
+			openSearch: function(){
+				this.setData({
+					search_btn: false,
+					search_friend: true,
+					show_mask: true,
+					gotop: true
+				});
+			},
+	
+			cancel: function(){
+				this.setData({
+					search_btn: true,
+					search_friend: false,
+					gotop: false
+					//show_mask: false
+				});
+				//this.getBrands(this.member)
+			},
+	
+			
+	
+			onInput(e){
+	
+				let inputValue = e.detail.value
+				if (inputValue) {
+					this.setData({
+						show_clear: true
+					})
+				} else {
+					this.setData({
+						show_clear: false
+					})
+				}
+			},
+	
+			clearInput: function(){
+				this.setData({
+					input_code: '',
+					show_clear: false
+				})
+			},
+	
+			cancel: function(){
+				this.setData({
+					search_btn: true,
+					search_friend: false,
+				});
+				let original = this.groupMember||[]
+				this.getRenderList(original)
+			},
+	
+			onSearch: function(val){
+				let searchValue = val.detail.value
+				let member = this.groupMember;
+				let serchList = [];
+				member.forEach((item, index)=>{
+					if(String(item.member).indexOf(searchValue) != -1 || String(item.owner).indexOf(searchValue) != -1){
+						serchList.push(item)
+					}
+				})
+				// this.setData({
+				// 	groupMember: serchList
+				// })
+	
+				this.getRenderList(serchList)
+			},
+	
+			startConfr(){
+				this.$emit('onStartConfr', {confrMember: this.checkedValue, action: this.action, groupId: this.username.groupId})
+				this.setData({
+					checkedValue: []
+				})
+			},
+	
+			goBack(){
+				this.setData({
+					checkedValue: []
+				})
+				this.$emit('goBack')
+			},
+			
+			into_room(){
+				
+			}
+		}
+	}
+</script>
+<style>
+@import "./emediaInvite.css";
+</style>
+

+ 65 - 0
chat/components/chat/inputbar/inputbar.css

@@ -0,0 +1,65 @@
+.room_bar {
+	width: 100%;
+	height: auto;
+	border-top: 1px solid rgba(0 0 0 0.5);
+	position: fixed;
+	bottom: 0;
+	right: 0;
+	z-index: 1;
+	background-color: #FFFFFF;
+	/* transform: translateZ(1000px); */
+}
+
+.other_func {
+	width: 100%;
+	/* height: 60rpx; */
+	display: flex;
+}
+.other_func_X{
+	/* height: 128rpx; */
+}
+
+.other_func image {
+	width: 54px;
+	height: 54px;
+	display: block;
+	margin-bottom: 5px;
+}
+
+.open_emoji,
+.menu_wrap,
+.open_camera,
+.v-record {
+	margin-left: 48rpx;
+	text-align: center;
+	color: #b7b7b7;
+    font-size: 13px;
+}
+
+
+.v-record .icon-record {
+	width: 18rpx;
+	height: 40rpx;
+}
+.v-record{
+	margin-left: 48rpx;
+}
+
+.fun_list {
+	margin-top: 30px;
+	width: 100%;
+	height: 145px;
+	background-color: #dddddd;
+	padding-top: 10px;
+	padding-left: 3%;
+	display: none;
+}
+.showFunModal {
+	width: 100%;
+	height: 145px;
+	padding-top: 10px;
+	display: block;
+	background-color: #f2f2f2;
+}
+
+

+ 228 - 0
chat/components/chat/inputbar/inputbar.vue

@@ -0,0 +1,228 @@
+<template>
+  <view class="room_bar">
+    <chatSuitEmoji
+      ref="chatSuitEmoji"
+      @newEmojiStr="emojiAction"
+    ></chatSuitEmoji>
+    <chatSuitMain
+      ref="chatSuitMain"
+      :username="username"
+      :chatType="chatType"
+      @inputFocused="closeAllModal"
+      @openEmoji="openEmoji"
+      @openRecordModal="toggleRecordModal"
+      @openFunModal="openFunModal"
+    ></chatSuitMain>
+    <chatSuitImage
+      ref="chatSuitImage"
+      :username="username"
+      :chatType="chatType"
+    ></chatSuitImage>
+    <!-- <chat-suit-location id="chat-suit-location" username="{{ username }}"></chat-suit-location> -->
+    <!-- <chat-suit-video ref="chatSuitVideo" :username="username"></chat-suit-video> -->
+    <chatSuitPtopcall
+      ref="chatSuitPtopcall"
+      :chatType="chatType"
+      @makeVideoCall="onMakeVideoCall"
+    >
+    </chatSuitPtopcall>
+
+    <swiper
+      :class="showFunModal"
+      :indicator-dots="true"
+      :autoplay="false"
+      :interval="5000"
+      :duration="1000"
+    >
+      <swiper-item>
+        <view :class="'other_func ' + (isIPX ? 'other_func_X' : '')">
+          <view class="open_camera" @tap="openCamera">
+            <image src="../../../static/camora.png"></image>
+            相机
+          </view>
+          <view class="menu_wrap" @tap="sendImage">
+            <image src="../../../static/pic.png"></image>
+            相册
+          </view>
+          <!-- #ifdef APP-PLUS -->
+          <view class="menu_wrap">
+            <chatSuitAttach :username="username" :chatType="chatType">
+              <image
+                style="background-color: #fff"
+                src="../../../static/file.png"
+              ></image>
+              附件
+            </chatSuitAttach>
+          </view>
+          <!-- #endif -->
+          <view
+            class="menu_wrap"
+            @tap="edit_group"
+            v-show="chatType === 'chatRoom'"
+          >
+            <image src="../../../static/pic.png"></image>
+            群信息
+          </view>
+
+        </view>
+      </swiper-item>
+    </swiper>
+  </view>
+</template>
+
+<script>
+let RecordStatus = require("./suit/audio/record_status").RecordStatus;
+let msgType = require("../msgtype");
+import chatSuitEmoji from "./suit/emoji/emoji";
+import chatSuitImage from "./suit/image/image";
+import chatSuitLocation from "./suit/location/location";
+import chatSuitMain from "./suit/main/main";
+import chatSuitPtopcall from "./suit/ptopcall/ptopcall.vue";
+import chatSuitAttach from "./suit/attach";
+
+// import chatSuitVideo from "./suit/videoComp/videoComp"
+
+let FUNMODAL_STATUS = {
+  OPENED: "showFunModal",
+  CLOSED: "fun_list"
+};
+
+export default {
+  data() {
+    return {
+      recordStatus: RecordStatus.HIDE,
+      RecordStatus,
+      __comps__: {
+        main: null,
+        emoji: null,
+        image: null,
+        location: null,
+        video: null
+      },
+      isIPX: "",
+      showFunModal: FUNMODAL_STATUS.CLOSED
+    };
+  },
+
+  components: {
+    chatSuitEmoji,
+    chatSuitImage,
+    chatSuitLocation,
+    chatSuitMain,
+    chatSuitPtopcall,
+    chatSuitAttach
+    // chatSuitVideo
+  },
+  props: {
+    username: {
+      type: Object,
+      default: () => ({})
+    },
+    chatType: {
+      type: String,
+      default: msgType.chatType.SINGLE_CHAT
+    }
+  },
+
+  // lifetimes
+  created() {},
+
+  beforeMount() {},
+
+  moved() {},
+
+  destroyed() {},
+
+  onLoad() {
+    this.setData({
+      isIPX: false //getApp().globalData.isIPX
+    });
+    // let comps = this.$data.__comps__;
+    // comps.main = this.selectComponent("#chatSuitMain");
+    // comps.emoji = this.selectComponent("#chatSuitEmoji");
+    // comps.image = this.selectComponent("#chatSuitImage");
+  },
+
+  methods: {
+    // 事件有长度限制:仅限 26 字符
+    toggleRecordModal() {
+      this.$emit("tapSendAudio", null, {
+        bubbles: true,
+        composed: true
+      });
+    },
+
+    // sendVideo(){
+    // 	this.$refs.chatSuitVideo.sendVideo();
+    // },
+    openCamera() {
+      this.$refs.chatSuitImage.openCamera();
+    },
+
+    openEmoji() {
+      setTimeout(() => {
+        this.setData({
+          showFunModal: FUNMODAL_STATUS.CLOSED
+        });
+      }, 100);
+      this.$refs.chatSuitEmoji.openEmoji();
+    },
+
+    cancelEmoji() {
+      this.$refs.chatSuitEmoji.cancelEmoji();
+    },
+
+    sendImage() {
+      this.$refs.chatSuitImage.sendImage();
+    },
+
+    sendLocation() {
+      // this.data.__comps__.location.sendLocation();
+    },
+
+    emojiAction(evt) {
+      this.$refs.chatSuitMain.emojiAction(evt.msg);
+    },
+
+    callVideo() {
+      this.$refs.chatSuitPtopcall.show();
+    },
+
+    onMakeVideoCall() {
+      console.log("onMakeVideoCall -> inputbar");
+      this.$emit("makeVideoCall", null, "single");
+    },
+
+    openFunModal() {
+      this.setData({
+        showFunModal: FUNMODAL_STATUS.OPENED
+      });
+      this.cancelEmoji();
+    },
+    closeFunModal() {
+      this.setData({
+        showFunModal: FUNMODAL_STATUS.CLOSED
+      });
+      this.cancelEmoji();
+    },
+    closeAllModal() {
+      this.cancelEmoji();
+      this.closeFunModal();
+    },
+    edit_group() {
+      var nameList = {
+        myName: this.username.myName,
+        groupName: this.username.your,
+        roomId: this.username.groupId
+      };
+      uni.navigateTo({
+        url:
+          "../groupSetting/groupSetting?groupInfo=" + JSON.stringify(nameList)
+      });
+    }
+  }
+};
+</script>
+<style>
+@import "./inputbar.css";
+</style>

+ 114 - 0
chat/components/chat/inputbar/suit/attach/index.vue

@@ -0,0 +1,114 @@
+<template>
+  <view>
+    <lsj-upload
+      ref="lsjUpload"
+      childId="upload1"
+      :width="width"
+      :height="height"
+      :option="option"
+      :size="size"
+      :formats="formats"
+      :debug="debug"
+      :instantly="instantly"
+      @uploadEnd="onUploadEnd"
+    >
+      <view :style="{ width: width, height: height }">
+        <slot></slot>
+      </view>
+    </lsj-upload>
+  </view>
+</template>
+
+<script>
+let WebIM = require("../../../../../../hxChatSDK/utils/WebIM")["default"];
+let msgType = require("../../../msgtype");
+let disp = require("../../../../../../hxChatSDK/utils/broadcast");
+let msgStorage = require("../../../msgstorage");
+const str = WebIM.config.appkey.split("#");
+const token = WebIM.conn.context.accessToken;
+export default {
+  data() {
+    return {
+      option: {
+        url: `https://a1.easemob.com/${str[0]}/${str[1]}/chatfiles `,
+        name: "file",
+        header: {
+          Authorization: `Bearer${token}`
+        }
+      },
+      // 选择文件后是否立即自动上传,true=选择后立即上传
+      instantly: true,
+      // 必传宽高且宽高应与slot宽高保持一致
+      width: "108rpx",
+      height: "155rpx",
+      // 限制允许选择的格式,空串=不限制,默认为空
+      formats: "",
+      // 文件上传大小限制
+      size: 10,
+      // 是否打印日志
+      debug: true
+    };
+  },
+  props: {
+    username: {
+      type: Object,
+      default: () => ({})
+    },
+    chatType: {
+      type: String,
+      default: msgType.chatType.SINGLE_CHAT
+    }
+  },
+  onReady() {},
+  methods: {
+    getSendToParam() {
+      return this.isGroupChat() ? this.username.groupId : this.username.your;
+    },
+    isGroupChat() {
+      return this.chatType == msgType.chatType.CHAT_ROOM;
+    },
+
+    saveSendMsg(evt) {
+      msgStorage.saveMsg(evt.msg, evt.type);
+    },
+
+    onUploadEnd(res) {
+      console.log(res);
+      let id = WebIM.conn.getUniqueId();
+      let msg = new WebIM.message(msgType.FILE, id);
+      let dataObj = JSON.parse(res.responseText); // 接收上传文件对象
+      let me = this;
+
+      msg.set({
+        apiUrl: WebIM.config.apiURL,
+        accessToken: token,
+        body: {
+          type: msgType.FILE,
+          url: dataObj.uri + "/" + dataObj.entities[0].uuid,
+          filename: res.name,
+          accessToken: token,
+          file_length: res.size
+        },
+        from: me.username.myName,
+        to: me.getSendToParam(),
+        roomType: false,
+        chatType: me.chatType,
+        success: function (argument) {
+          disp.fire("em.chat.sendSuccess", id);
+        }
+      });
+
+      if (this.isGroupChat()) {
+        msg.setGroup("groupchat");
+      }
+
+      WebIM.conn.send(msg.body);
+      let obj = {
+        msg: msg,
+        type: msgType.FILE
+      };
+      this.saveSendMsg(obj);
+    }
+  }
+};
+</script>

+ 73 - 0
chat/components/chat/inputbar/suit/audio/audio.css

@@ -0,0 +1,73 @@
+.modal {
+	position: absolute;
+	top: 0;
+	right: 0;
+	left: 0;
+	bottom: 0;
+	width: 100%;
+	height: 100%;
+	z-index: 999;
+	/*display: flex;
+	justify-content: center;
+	align-items: center;*/
+}
+
+.modal-record .modal-body {
+	width: 480rpx;
+	height: 440rpx;
+	background-color: #fff;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	border-radius: 3px;
+	box-shadow: 0 0 32rpx rgba(0, 0, 0, 0.15);
+	position: fixed;
+	bottom: 480rpx;
+	left: 50%;
+	margin-left: -240rpx;
+}
+
+.modal-record .desc {
+	color: rgb(112, 126, 137);
+	font-size: 13px;
+	margin-bottom: 40rpx;
+	display: block;
+	height: 30rpx;
+	width: 100%;
+	text-align: center;
+}
+
+.modal-record .dot {
+	width: 128rpx;
+	height: 128rpx;
+	border-radius: 50%;
+	background-color: #0873DE;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+
+.dot image {
+	width: 44rpx;
+	height: 64rpx;
+}
+
+.sound-waves {
+  width: 100%;
+  box-sizing: border-box;
+  padding-left:10%;
+  margin-top: 80rpx;
+  height: 80rpx;
+  text-align: center;
+}
+ 
+.sound-waves view {
+  transition: all 0.5s;
+  width: 1%;
+  margin-left: 1.5%;
+  margin-right: 1.5%;
+  height: 160rpx;
+  background-color: #aaa;
+  float: left;
+}

+ 409 - 0
chat/components/chat/inputbar/suit/audio/audio.vue

@@ -0,0 +1,409 @@
+<template>
+  <view
+    v-if="recordStatus != RecordStatus.HIDE"
+    class="modal modal-record"
+    @tap="toggleRecordModal"
+  >
+    <view class="modal-body" @tap.stop="toggleWithoutAction">
+      <view class="sound-waves">
+        <view
+          v-for="(item, index) in radomHeight"
+          :key="index"
+          :style="'height:' + item + 'rpx;margin-top:-' + item / 2 + 'rpx'"
+        ></view>
+        <view style="clear: both; width: 0; height: 0"></view>
+      </view>
+      <text class="desc">{{ RecordDesc[recordStatus] }}</text>
+      <view
+        class="dot"
+        @touchstart="handleRecording"
+        @touchmove="handleRecordingMove"
+        @touchend="handleRecordingCancel"
+      >
+        <image class="icon-mic" src="../../../../../static/send.png"></image>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+let WebIM = require("../../../../../../hxChatSDK/utils/WebIM")["default"];
+let msgType = require("../../../msgtype");
+let RECORD_CONST = require("./record_status");
+let RecordStatus = RECORD_CONST.RecordStatus;
+let RecordDesc = RECORD_CONST.RecordDesc;
+let disp = require("../../../../../../hxChatSDK/utils/broadcast");
+let msgStorage = require("../../../msgstorage");
+let RunAnimation = false;
+let recordTimeInterval = null;
+let waveTimer = null;
+const InitHeight = [
+  50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+  50, 50, 50
+];
+export default {
+  data() {
+    return {
+      changedTouches: null,
+      recordStatus: RecordStatus.HIDE,
+      RecordStatus,
+      RecordDesc,
+      radomHeight: InitHeight,
+      recorderManager: uni.getRecorderManager(),
+      recordClicked: false,
+      isLongPress: false,
+      recordTime: 0,
+      rec: null // h5 audio record
+    };
+  },
+  components: {},
+  props: {
+    username: {
+      type: Object,
+      default: () => ({})
+    },
+    chatType: {
+      type: String,
+      default: msgType.chatType.SINGLE_CHAT
+    }
+  },
+
+  destroyed() {
+    clearInterval(recordTimeInterval);
+    clearTimeout(waveTimer);
+    this.recordTime = 0;
+  },
+
+  methods: {
+    toggleWithoutAction(e) {
+      // 阻止 tap 冒泡
+    },
+
+    toggleRecordModal() {
+      if (this.recordStatus === RecordStatus.HIDE) {
+        this.recordStatus = RecordStatus.SHOW;
+      } else {
+        this.recordStatus = RecordStatus.HIDE;
+      }
+      this.radomHeight = InitHeight;
+    },
+
+    handleRecordingMove(e) {
+      var touches = e.touches[0];
+      var changedTouches = this.changedTouches;
+
+      if (!changedTouches) {
+        return;
+      }
+
+      if (this.recordStatus == RecordStatus.SWIPE) {
+        if (changedTouches.pageY - touches.pageY < 20) {
+          this.recordStatus = RecordStatus.HOLD;
+        }
+      }
+
+      if (this.recordStatus == RecordStatus.HOLD) {
+        if (changedTouches.pageY - touches.pageY > 20) {
+          this.recordStatus = RecordStatus.SWIPE;
+        }
+      }
+    },
+    // 初始化开始录音状态
+    initStartRecord(e) {
+      clearInterval(recordTimeInterval);
+      this.recordTime = 0;
+      this.changedTouches = e.touches[0];
+      this.recordStatus = RecordStatus.HOLD;
+      RunAnimation = true;
+      this.startWave();
+    },
+    // 记录录音时长
+    saveRecordTime() {
+      recordTimeInterval = setInterval(() => {
+        this.recordTime++;
+        if (this.recordTime === 100) {
+          this.handleRecordingCancel();
+          RunAnimation = false;
+        }
+      }, 1000);
+    },
+    startRecord(e) {
+      let me = this;
+      me.initStartRecord(e);
+      let recorderManager = me.recorderManager || uni.getRecorderManager();
+      recorderManager.onStart(() => {
+        this.saveRecordTime();
+      });
+      recorderManager.start({
+        format: "mp3"
+      });
+    },
+
+    executeRecord(e) {
+      let me = this;
+      if (uni.getSetting) {
+        uni.getSetting({
+          success: (res) => {
+            clearInterval(recordTimeInterval);
+            me.recordTime = 0;
+            let recordAuth = res.authSetting["scope.record"];
+
+            if (recordAuth == false) {
+              // 已申请过授权,但是用户拒绝
+              uni.openSetting({
+                success: function (res) {
+                  let recordAuth = res.authSetting["scope.record"];
+
+                  if (recordAuth == true) {
+                    uni.showToast({
+                      title: "授权成功",
+                      icon: "success"
+                    });
+                  } else {
+                    uni.showToast({
+                      title: "请授权录音",
+                      icon: "none"
+                    });
+                  }
+
+                  me.isLongPress = false;
+                }
+              });
+            } else if (recordAuth == true) {
+              // 用户已经同意授权
+              me.startRecord(e);
+            } else {
+              // 第一次进来,未发起授权
+              uni.authorize({
+                scope: "scope.record",
+                success: () => {
+                  // 授权成功
+                  uni.showToast({
+                    title: "授权成功",
+                    icon: "success"
+                  });
+                }
+              });
+            }
+          },
+          fail: function () {
+            uni.showToast({
+              title: "鉴权失败,请重试",
+              icon: "none"
+            });
+          }
+        });
+        return;
+      } else {
+        me.startRecord(e);
+        return;
+      }
+    },
+
+    handleRecording(e) {
+      const sysInfo = uni.getSystemInfoSync();
+      console.log("getSystemInfoSync", sysInfo);
+      if (sysInfo.app === "alipay") {
+        // https://forum.alipay.com/mini-app/post/7301031?ant_source=opendoc_recommend
+        uni.showModal({
+          content: "支付宝小程序不支持语音消息,请查看支付宝相关api了解详情"
+        });
+        return;
+      }
+      let me = this;
+      me.recordClicked = true;
+      // h5不支持uni.getRecorderManager, 需要单独处理
+      if (sysInfo.uniPlatform === "web") {
+        import("../../../../../recorderCore/src/recorder-core").then((Recorder) => {
+          require("../../../../../recorderCore/src/engine/mp3");
+          require("../../../../../recorderCore/src/engine/mp3-engine");
+          if (me.recordClicked == true) {
+            clearInterval(recordTimeInterval);
+            me.initStartRecord(e);
+            me.rec = new Recorder.default({
+              type: "mp3"
+            });
+            me.rec.open(
+              () => {
+                me.saveRecordTime();
+                me.rec.start();
+              },
+              (msg, isUserNotAllow) => {
+                if (isUserNotAllow) {
+                  uni.showToast({
+                    title: "鉴权失败,请重试",
+                    icon: "none"
+                  });
+                } else {
+                  uni.showToast({
+                    title: `开启失败,请重试`,
+                    icon: "none"
+                  });
+                }
+              }
+            );
+          }
+        });
+      } else {
+        setTimeout(() => {
+          if (me.recordClicked == true) {
+            me.executeRecord(e);
+          }
+        }, 350);
+      }
+    },
+
+    // 取消录音
+    handleRecordingCancel() {
+      RunAnimation = false;
+      let recorderManager = this.recorderManager; // 向上滑动状态停止:取消录音发放
+      let me = this;
+      if (this.recordStatus == RecordStatus.SWIPE) {
+        this.recordStatus = RecordStatus.RELEASE;
+      } else {
+        this.recordStatus = RecordStatus.HIDE;
+        this.recordClicked = false;
+      }
+      if (uni.getSystemInfoSync().uniPlatform === "web") {
+        this.rec.stop(
+          function (blob) {
+            clearInterval(recordTimeInterval);
+            let duration = me.recordTime * 1000;
+            if (me.recordStatus == RecordStatus.RELEASE) {
+              console.log("user canceled");
+              me.recordStatus = RecordStatus.HIDE;
+              return;
+            }
+            if (duration <= 1000) {
+              uni.showToast({
+                title: "录音时间太短",
+                icon: "none"
+              });
+            } else {
+              let blobURL = window.URL.createObjectURL(blob);
+              me.uploadRecord(blobURL, duration);
+            }
+            me.recordStatus = RecordStatus.HIDE;
+            me.recordTime = 0;
+          },
+          function (s) {
+            console.log("结束出错:" + s, 1);
+          },
+          true
+        );
+      } else {
+        recorderManager.onStop((res) => {
+          clearInterval(recordTimeInterval);
+          let duration = this.recordTime * 1000;
+          if (this.recordStatus == RecordStatus.RELEASE) {
+            console.log("user canceled");
+            this.recordStatus = RecordStatus.HIDE;
+            return;
+          }
+
+          if (duration <= 1000) {
+            uni.showToast({
+              title: "录音时间太短",
+              icon: "none"
+            });
+          } else {
+            // 上传
+            this.uploadRecord(res.tempFilePath, duration);
+          }
+          clearInterval(recordTimeInterval);
+          this.recordStatus = RecordStatus.HIDE;
+          this.recordTime = 0;
+        }); // 停止录音
+
+        recorderManager.stop();
+      }
+    },
+
+    isGroupChat() {
+      return this.chatType == msgType.chatType.CHAT_ROOM;
+    },
+
+    getSendToParam() {
+      return this.isGroupChat() ? this.username.groupId : this.username.your;
+    },
+
+    uploadRecord(tempFilePath, dur) {
+      var str = WebIM.config.appkey.split("#");
+      var me = this;
+      var token = WebIM.conn.context.accessToken;
+      uni.uploadFile({
+        url: "https://a1.easemob.com/" + str[0] + "/" + str[1] + "/chatfiles",
+        filePath: tempFilePath,
+        fileType: "audio",
+        name: "file",
+        header: {
+          Authorization: "Bearer " + token
+        },
+
+        success(res) {
+          var id = WebIM.conn.getUniqueId();
+          var msg = new WebIM.message(msgType.AUDIO, id);
+          var dataObj = JSON.parse(res.data); // 接收消息对象
+
+          msg.set({
+            apiUrl: WebIM.config.apiURL,
+            accessToken: token,
+            body: {
+              type: msgType.AUDIO,
+              url: dataObj.uri + "/" + dataObj.entities[0].uuid,
+              filetype: "",
+              filename: `${new Date().getTime()}.mp3`,
+              accessToken: token,
+              length: Math.ceil(dur / 1000)
+            },
+            from: me.username.myName,
+            to: me.getSendToParam(),
+            roomType: false,
+            chatType: me.chatType,
+            success: function (argument) {
+              disp.fire("em.chat.sendSuccess", id);
+            }
+          });
+
+          if (me.isGroupChat()) {
+            msg.setGroup("groupchat");
+          }
+
+          msg.body.length = Math.ceil(dur / 1000); //console.log('发送的语音消息', msg.body)
+
+          WebIM.conn.send(msg.body);
+          let obj = {
+            msg: msg,
+            type: msgType.AUDIO
+          };
+          me.saveSendMsg(obj);
+        }
+      });
+    },
+    saveSendMsg(evt) {
+      msgStorage.saveMsg(evt.msg, evt.type);
+    },
+    // 波纹动画
+    startWave() {
+      const that = this;
+      var _radomHeight = [...that.radomHeight];
+      for (var i = 0; i < that.radomHeight.length; i++) {
+        //+1是为了避免为0
+        _radomHeight[i] = 100 * Math.random().toFixed(2) + 10;
+      }
+      that.radomHeight = _radomHeight;
+      if (RunAnimation) {
+        waveTimer = setTimeout(function () {
+          that.startWave();
+        }, 500);
+      } else {
+        clearInterval(waveTimer);
+        return;
+      }
+    }
+  }
+};
+</script>
+<style>
+@import "./audio.css";
+</style>

+ 14 - 0
chat/components/chat/inputbar/suit/audio/record_status.js

@@ -0,0 +1,14 @@
+module.exports = {
+  RecordDesc: {
+    0: "长按开始录音",
+    2: "向上滑动取消",
+    3: "松开手取消"
+  },
+  RecordStatus: {
+    SHOW: 0,
+    HIDE: 1,
+    HOLD: 2,
+    SWIPE: 3,
+    RELEASE: 4
+  }
+};

+ 39 - 0
chat/components/chat/inputbar/suit/emoji/emoji.css

@@ -0,0 +1,39 @@
+.emoji_list {
+	margin-top: 30px;
+	width: 100%;
+	height: 145px;
+	background-color: #dddddd;
+	padding-top: 10px;
+	padding-left: 3%;
+	display: none;
+}
+
+.showEmoji {
+	width: 100%;
+	height: 145px;
+	padding-top: 10px;
+	padding-left: 3%;
+	display: block;
+}
+
+.emoji_list image,
+.showEmoji image {
+	width: 26px;
+	height: 26px;
+	margin: 5px 2%;
+}
+
+.emoji {
+	width: 26px;
+	height: 26px;
+	margin: 0 0;
+}
+
+.emoji_item {
+	display: flex;
+	justify-content: space-around;
+	margin-right: 20px;
+}
+.last_item{
+	justify-content:flex-end !important
+}

+ 85 - 0
chat/components/chat/inputbar/suit/emoji/emoji.vue

@@ -0,0 +1,85 @@
+<template>
+<swiper :class="show" :indicator-dots="indicatorDots" :autoplay="autoplay" :interval="interval" :duration="duration">
+	<block>
+		<swiper-item>
+			<view class="emoji_item">
+				<image v-for="(item, index) in emojiObj.map1" :key="index" :src="'../../../../' + emojiObj.path + item" @tap="sendEmoji" :data-emoji="index"></image>
+			</view>
+			<view class="emoji_item">
+				<image v-for="(item, index) in emojiObj.map2" :key="index" :src="'../../../../' +emojiObj.path + item" @tap="sendEmoji" :data-emoji="index"></image>
+			</view>
+			<view class="emoji_item">
+				<image v-for="(item, index) in emojiObj.map3" :key="index" :src="'../../../../' +emojiObj.path + item" @tap="sendEmoji" :data-emoji="index"></image>
+			</view>
+		</swiper-item>
+	</block>
+	<block class="second">
+		<swiper-item>
+			<view class="emoji_item">
+				<image v-for="(item, index) in emojiObj.map4" :key="index" :src="'../../../../' +emojiObj.path + item" @tap="sendEmoji" :data-emoji="index"></image>
+			</view>
+			<view class="emoji_item">
+				<image v-for="(item, index) in emojiObj.map5" :key="index" :src="'../../../../' +emojiObj.path + item" @tap="sendEmoji" :data-emoji="index"></image>
+			</view>
+			<view class="emoji_item last_item">
+				<image v-for="(item, index) in emojiObj.map6" :key="index" :src="'../../../../' +emojiObj.path + item" @tap="sendEmoji" :data-emoji="index"></image>
+			</view>
+		</swiper-item>
+	</block>
+</swiper>
+</template>
+
+<script>
+let WebIM = require("../../../../../../hxChatSDK/utils/WebIM")["default"];
+let msgType = require("../../../msgtype");
+let EMOJI_STATUS = {
+  OPENED: "showEmoji",
+  CLOSED: "emoji_list"
+};
+
+export default {
+  data() {
+    return {
+      show: EMOJI_STATUS.CLOSED,
+      emoji: WebIM.Emoji,
+      emojiObj: WebIM.EmojiObj,
+      interval: 5000,
+      duration: 1000,
+      autoplay: false,
+      indicatorDots: true // 显示面板指示点
+    };
+  },
+
+  components: {},
+  props: {},
+  methods: {
+    openEmoji() {
+      this.setData({
+        show: EMOJI_STATUS.OPENED
+      });
+    },
+
+    cancelEmoji() {
+      this.setData({
+        show: EMOJI_STATUS.CLOSED
+      });
+    },
+
+    // 输出 emoji
+    sendEmoji(event) {
+      var emoji = event.target.dataset.emoji;
+      this.$emit("newEmojiStr", {
+        msg: emoji,
+        type: msgType.EMOJI
+      }, {
+        bubbles: true,
+        composed: true
+      });
+    }
+
+  }
+};
+</script>
+<style>
+@import "./emoji.css";
+</style>

+ 169 - 0
chat/components/chat/inputbar/suit/image/image.vue

@@ -0,0 +1,169 @@
+<template>
+	<view></view>
+</template>
+<script>
+	let WebIM = require("../../../../../../hxChatSDK/utils/WebIM")["default"];
+	let msgType = require("../../../msgtype");
+	let disp = require("../../../../../../hxChatSDK/utils/broadcast");
+	let msgStorage = require("../../../msgstorage");
+	export default {
+		data() {
+			return {};
+		},
+
+		components: {},
+		props: {
+			username: {
+				type: Object,
+				default: () => ({})
+			},
+			chatType: {
+				type: String,
+				default: msgType.chatType.SINGLE_CHAT
+			}
+		},
+		methods: {
+			openCamera() {
+				var me = this;
+				uni.chooseImage({
+					count: 1,
+					sizeType: ["original", "compressed"],
+					sourceType: ["camera"],
+
+					success(res) {
+						me.upLoadImage(res);
+					}
+
+				});
+			},
+
+			sendImage() {
+				var me = this;
+				uni.chooseImage({
+					count: 1,
+					sizeType: ["original", "compressed"],
+					sourceType: ["album"],
+
+					success(res) {
+						console.log('选择的图片', res)
+						me.upLoadImage(res);
+					}
+
+				});
+			},
+
+			isGroupChat() {
+				return this.chatType == msgType.chatType.CHAT_ROOM;
+			},
+
+			getSendToParam() {
+				return this.isGroupChat() ? this.username.groupId : this.username.your;
+			},
+
+			upLoadImage(res) {
+				var me = this;
+				var tempFilePaths = res.tempFilePaths;
+				var token = WebIM.conn.context.accessToken;
+				uni.getImageInfo({
+					src: res.tempFilePaths[0],
+
+					success(res) {
+						var allowType = {
+							jpg: true,
+							jpeg: true,
+							gif: true,
+							png: true,
+							bmp: true
+						};
+						var str = WebIM.config.appkey.split("#");
+						var width = res.width;
+						var height = res.height;
+						var index = res.path.lastIndexOf(".");
+						console.log('index>>',index);
+						var filetype = ~index && res.path.slice(index + 1) || "";
+						if (!res.type) {
+							uni.showToast({
+								title: "H5端,uni-app暂未支持",
+                      			icon: "none"
+							})
+						}
+						if (filetype.toLowerCase() in allowType || res.type in allowType) {
+							uni.uploadFile({
+								url: "https://a1.easemob.com/" + str[0] + "/" + str[1] + "/chatfiles",
+								filePath: tempFilePaths[0],
+								fileType: 'image',
+								name: "file",
+								header: {
+									// "Content-Type": "multipart/form-data",
+									'Content-Type': 'application/x-www-form-urlencoded',
+									Authorization: "Bearer " + token
+								},
+								success: (res)=>{
+									console.log('上传图片成功', res)
+									if (res.statusCode == 400) {
+										// 图片上传阿里云检验不合法
+										// var errData = JSON.parse(res.data);
+										// if (errData.error === 'content improper') {
+										uni.showToast({
+											title: "图片检测不合法",
+											duration: 1000
+										});
+										return false
+										// }
+									}
+									var data = res.data;
+									var dataObj = JSON.parse(data);
+									var id = WebIM.conn.getUniqueId(); // 生成本地消息 id
+
+									var msg = new WebIM.message(msgType.IMAGE, id);
+									var file = {
+										type: msgType.IMAGE,
+										size: {
+											width: width,
+											height: height
+										},
+										url: dataObj.uri + "/" + dataObj.entities[0].uuid,
+										filetype: filetype,
+										filename: tempFilePaths[0]
+									};
+									msg.set({
+										apiUrl: WebIM.config.apiURL,
+										body: file,
+										from: me.username.myName,
+										to: me.getSendToParam(),
+										roomType: false,
+										chatType: me.chatType,
+										success: function(argument) {
+											disp.fire('em.chat.sendSuccess', id);
+										}
+									});
+
+									if (me.chatType == msgType.chatType.CHAT_ROOM) {
+										msg.setGroup("groupchat");
+									}
+
+									WebIM.conn.send(msg.body);
+									let obj = {
+										msg: msg,
+										type: msgType.IMAGE
+									}
+									me.saveSendMsg(obj);
+								},
+								fail: (err) => {
+									console.log('上传失败', err)
+								},
+								complete: (err) => {
+									console.log('上传完成', err)
+								}
+							});
+						}
+					}
+				});
+			},
+			saveSendMsg(evt) {
+				msgStorage.saveMsg(evt.msg, evt.type);
+			}
+
+		}
+	};
+</script>

+ 95 - 0
chat/components/chat/inputbar/suit/location/location.vue

@@ -0,0 +1,95 @@
+<template>
+<view></view>
+</template>
+<script>
+let WebIM = require("../../../../../../hxChatSDK/utils/WebIM")["default"];
+let msgType = require("../../../msgtype");
+let msgStorage = require("../../../msgstorage");
+export default {
+  data() {
+    return {};
+  },
+
+  components: {},
+  props: {
+    username: {
+      type: Object,
+      default: () => ({})
+    },
+    chatType: {
+      type: String,
+      default: msgType.chatType.SINGLE_CHAT
+    }
+  },
+  methods: {
+    isGroupChat() {
+      return this.chatType == msgType.chatType.CHAT_ROOM;
+    },
+
+    getSendToParam() {
+      return this.isGroupChat() ? this.username.groupId : this.username.your;
+    },
+
+    sendLocation() {
+      var me = this;
+      uni.authorize({
+        scope: "scope.userLocation",
+
+        fail() {
+          uni.showToast({
+            title: "已拒绝",
+            icon: "none"
+          });
+        },
+
+        success() {
+          uni.chooseLocation({
+            fail() {
+              console.log(arguments);
+            },
+
+            complete() {
+              console.log(arguments);
+            },
+
+            success(respData) {
+              var id = WebIM.conn.getUniqueId();
+              var msg = new WebIM.message(msgType.LOCATION, id);
+              msg.set({
+                msg: "",
+                from: me.username.myName,
+                to: me.getSendToParam(),
+                roomType: false,
+                lng: respData.longitude,
+                lat: respData.latitude,
+                addr: respData.address,
+                chatType: me.chatType,
+
+                success(id, serverMsgId) {}
+
+              });
+
+              if (me.chatType == msgType.chatType.CHAT_ROOM) {
+                msg.setGroup("groupchat");
+              }
+
+              WebIM.conn.send(msg.body);
+                let obj = {
+                  msg: msg,
+                  type: msgType.IMAGE
+                }
+              me.saveSendMsg(obj);
+            }
+
+          });
+        }
+
+      });
+    },
+    saveSendMsg(evt) {
+      msgStorage.saveMsg(evt.msg, evt.type);
+    }
+
+  }
+};
+</script>

+ 65 - 0
chat/components/chat/inputbar/suit/main/main.css

@@ -0,0 +1,65 @@
+
+.text-input {
+	width: 100%;
+	/* height: 100rpx; */
+	padding: 0;
+	display: block;
+}
+
+.news {
+	width: 100%;
+	font-size: 14px;
+	padding: 0 10px;
+	display: inline-block;
+	margin: 10rpx;
+	line-height: 48rpx;
+	position:relative;
+	top: 0;
+	background-color: #fff;
+	border-radius: 16px;
+	flex: 1;
+	max-height: 200rpx;
+	min-height: 60rpx;
+}
+
+.send_btn {
+	width: 80rpx;
+	height: 60rpx;
+	line-height: 60rpx;
+	font-size: 17px;
+	color: #000;
+	padding: 0;
+	display: inline-block;
+	float: right;
+	margin: 8rpx 16rpx auto auto;
+	background-color: #fff;
+}
+
+.f-row{
+	/* height:100rpx; */
+	display:flex;
+	align-items:center;
+	background-color: #f2f2f2;
+}
+.send-btn-style{
+	font-size: 10px;
+	background-color: #2196F3;
+	color: #fff;
+    margin-right: 10px;
+}
+.hover{
+	background-color:#075ca1
+}
+.icon-mic{
+	width: 22px;
+    height: 22px;
+	padding: 5px 10px;
+	position: relative;
+	top: 2px;
+}
+.f-row-x{
+	padding-bottom: 30px;
+	display:flex;
+	align-items:center;
+	background-color: #f2f2f2;
+}

+ 193 - 0
chat/components/chat/inputbar/suit/main/main.vue

@@ -0,0 +1,193 @@
+<template>
+  <!-- <chat-suit-emoji id="chat-suit-emoji" bind:newEmojiStr="emojiAction"></chat-suit-emoji> -->
+  <form class="text-input">
+    <view :class="isIPX ?'f-row-x' :'f-row'">
+      <!-- 发送语音 -->
+      <view>
+        <image class="icon-mic" src="../../../../../static/voice.png" @tap="openRecordModal"></image>
+      </view>
+      <!-- 输入框 -->
+      <textarea
+        class="f news"
+        type="text"
+        cursor-spacing="65"
+        confirm-type='done'
+        v-model="inputMessage"
+        @confirm="sendMessage"
+        @input="bindMessage"
+        @tap="focus"
+        @focus="focus"
+        @blur="blur"
+        :confirm-hold="isIPX ? true : false"
+        auto-height
+        :show-confirm-bar='false'
+        maxlength="300"
+      />
+      <view>
+        <image class="icon-mic" src="../../../../../static/Emoji.png" @tap="openEmoji"></image>
+      </view>
+      <view v-show="!inputMessage" @tap="openFunModal">
+        <image class="icon-mic" src="../../../../../static/ad.png"></image>
+      </view>
+       <button 
+        class="send-btn-style" 
+        hover-class='hover'
+        @tap="sendMessage"
+        v-show="inputMessage" 
+      >发送</button>
+    </view>
+  </form>
+</template>
+
+<script>
+let WebIM = require("../../../../../../hxChatSDK/utils/WebIM")["default"];
+let msgType = require("../../../msgtype");
+let disp = require("../../../../../../hxChatSDK/utils/broadcast");
+let msgStorage = require("../../../msgstorage");
+
+export default {
+  data() {
+    return {
+      inputMessage: "",
+      // render input 的值
+      userMessage: "", // input 的实时值
+      isIPX: false,
+    };
+  },
+
+  components: {},
+  props: {
+    username: {
+      type: Object,
+      default: () => ({}),
+    },
+    chatType: {
+      type: String,
+      default: msgType.chatType.SINGLE_CHAT,
+    },
+  },
+
+  // lifetimes
+  created() {
+    this.setData({
+      isIPX: getApp().globalData.isIPX,
+    });
+  },
+
+  beforeMount() {},
+
+  moved() {},
+
+  destroyed() {},
+
+  mounted() {},
+
+  methods: {
+    focus() {
+      this.$emit("inputFocused", null, {
+        bubbles: true,
+      });
+    },
+    blur() {
+      this.$emit("inputBlured", null, {
+        bubbles: true,
+      });
+    },
+
+    isGroupChat() {
+      return this.chatType == msgType.chatType.CHAT_ROOM;
+    },
+
+    getSendToParam() {
+      return this.isGroupChat() ? this.username.groupId : this.username.your;
+    },
+
+    bindMessage(e) {
+      this.setData({
+        userMessage: e.detail.value,
+      });
+    },
+
+    emojiAction(emoji) {
+      var str;
+      var msglen = this.userMessage.length - 1;
+
+      if (emoji && emoji != "[del]") {
+        str = this.userMessage + emoji;
+      } else if (emoji == "[del]") {
+        let start = this.userMessage.lastIndexOf("[");
+        let end = this.userMessage.lastIndexOf("]");
+        let len = end - start;
+
+        if (end != -1 && end == msglen && len >= 3 && len <= 4) {
+          str = this.userMessage.slice(0, start);
+        } else {
+          str = this.userMessage.slice(0, msglen);
+        }
+      }
+      this.userMessage = str;
+      this.inputMessage = str;
+    },
+
+    sendMessage() {
+      let me = this;
+      String.prototype.trim = function () {
+        return this.replace(/(^\s*)|(\s*$)/g, "");
+      };
+      if (!this.userMessage.trim()) {
+        return;
+      }
+      let id = WebIM.conn.getUniqueId();
+      let msg = new WebIM.message(msgType.TEXT, id);
+      msg.set({
+        msg: this.userMessage,
+        from: this.username.myName,
+        to: this.getSendToParam(),
+        // roomType: false,
+        chatType: this.chatType,
+        success(id, serverMsgId) {
+          console.log("成功了");
+          // 关闭表情弹窗
+          me.$parent.cancelEmoji()
+          me.$parent.closeFunModal()
+          disp.fire("em.chat.sendSuccess", id, me.userMessage);
+        },
+        fail(id, serverMsgId) {
+          console.log("失败了");
+        },
+      });
+	  
+      if (this.chatType == msgType.chatType.CHAT_ROOM) {
+        // msg.setGroup("groupchat");
+		msg.setChatType("groupchat");
+      }
+        WebIM.conn.send(msg.body);
+        let obj = {
+          msg: msg,
+          type: msgType.TEXT,
+        };
+        this.saveSendMsg(obj);
+      this.userMessage = "";
+      this.inputMessage = "";
+      uni.hideKeyboard();
+    },
+
+    saveSendMsg(evt) {
+      msgStorage.saveMsg(evt.msg, evt.type);
+    },
+
+    openEmoji(){
+      this.$emit('openEmoji')
+    },
+    openRecordModal(){
+      this.$emit('openRecordModal')
+    },
+    openFunModal(){
+      this.$emit('openFunModal')
+    }
+  },
+};
+</script>
+<style>
+@import "./main.css";
+</style>

+ 66 - 0
chat/components/chat/inputbar/suit/ptopcall/ptopcall.vue

@@ -0,0 +1,66 @@
+<template>
+	<view v-if="visible" class="wraper">
+		<view>
+			<button type="default" @tap="callVideo">会议模式</button>
+			<button
+				type="default"
+				@tap="cancel"
+			> 取消 </button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				visible: false
+			}
+		},
+		methods: {
+			show(){
+				this.setData({
+					visible: true
+				})
+			},
+			cancel(){
+				console.log('取消')
+				this.setData({
+					visible: false
+				})
+			},
+	
+			callAudio(){
+				//this.triggerEvent('makeAudioCall', 'single')
+				this.$emit("makeAudioCall", null, 'single');
+				this.cancel()
+			},
+	
+			callVideo(){
+				console.log('callVideo')
+				this.$emit("makeVideoCall", null, 'single');
+				//this.triggerEvent('makeVideoCall', 'single')
+				this.cancel()
+			}
+		}
+	}
+</script>
+
+<style>
+.wraper{
+	position: fixed;
+	bottom: 0;
+	height: 200rpx;
+	width: 100%;
+	z-index: 9;
+	background: #FFFFFF;
+}
+.other-button-hover {
+  background-color: blue;
+}
+.button-cancel {
+	position: absolute;
+	width: 100%;
+	bottom: 0;
+}
+</style>

+ 110 - 0
chat/components/chat/inputbar/suit/videoComp/videoComp.vue

@@ -0,0 +1,110 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+let WebIM = require("../../../../../../hxChatSDK/utils/WebIM")["default"];
+let msgType = require("../../../msgtype");
+let msgStorage = require("../../../msgstorage");
+let disp = require("../../../../../../hxChatSDK/utils/broadcast");
+
+export default {
+  data() {
+    return {};
+  },
+
+  components: {},
+  props: {
+    username: {
+      type: Object,
+      default: () => ({}),
+    },
+    chatType: {
+      type: String,
+      default: msgType.chatType.SINGLE_CHAT,
+    },
+  },
+  methods: {
+    isGroupChat() {
+      return this.chatType == msgType.chatType.CHAT_ROOM;
+    },
+
+    getSendToParam() {
+      return this.isGroupChat() ? this.username.groupId : this.username.your;
+    },
+
+    // 未启用
+    sendVideo() {
+      var me = this;
+      var token = WebIM.conn.context.accessToken;
+      uni.chooseVideo({
+        sourceType: ["album", "camera"],
+        maxDuration: 60,
+        camera: "back",
+        success(res) {
+          var tempFilePaths = res.tempFilePath;
+          var str = WebIM.config.appkey.split("#");
+          uni.uploadFile({
+            url:
+              "https://a1.easemob.com/" + str[0] + "/" + str[1] + "/chatfiles",
+            filePath: tempFilePaths,
+            name: "file",
+            header: {
+              "Content-Type": "multipart/form-data",
+              Authorization: "Bearer " + token,
+            },
+            success(res) {
+              var data = res.data;
+              var dataObj = JSON.parse(data);
+              var id = WebIM.conn.getUniqueId(); // 生成本地消息id
+              var msg = new WebIM.message(msgType.VIDEO, id);
+              msg.set({
+                apiUrl: WebIM.config.apiURL,
+                accessToken: token,
+                body: {
+                  type: msgType.VIDEO,
+                  url: dataObj.uri + "/" + dataObj.entities[0].uuid,
+                  filetype: "mp4",
+                  filename: tempFilePaths,
+									accessToken: token,
+                },
+                from: me.username.myName,
+                to: me.getSendToParam(),
+                roomType: false,
+                chatType: me.chatType,
+                success: function (argument) {
+									disp.fire('em.chat.sendSuccess', id);
+								}
+              });
+              if (me.isGroupChat()) {
+                msg.setGroup("groupchat");
+              }
+              WebIM.conn.send(msg.body);
+              let obj = {
+                msg: msg,
+                type: msgType.VIDEO,
+              };
+              me.saveSendMsg(obj);
+            },
+          });
+        },
+      });
+    },
+    saveSendMsg(evt) {
+      msgStorage.saveMsg(evt.msg, evt.type);
+    },
+  },
+
+  // lifetimes
+  created() {},
+
+  beforeMount() {},
+
+  moved() {},
+
+  destroyed() {},
+
+  mounted() {},
+};
+</script>
+

+ 110 - 0
chat/components/chat/longPressModal/index.vue

@@ -0,0 +1,110 @@
+<template>
+  <view v-if="showPop" class="shade" @tap="hidePop">
+    <view class="pop" :style="popStyle" :class="{ show: showPop }">
+      <view
+        v-for="(item, index) in popButton"
+        :key="index"
+        @tap="pickerMenu"
+        :data-index="index"
+        >{{ item }}</view
+      >
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  name: "long-press-modal",
+  props: {
+    /* 窗口尺寸 */
+    winSize: {
+      type: Object,
+      default() {
+        return {
+          witdh: 375,
+          height: 603,
+        };
+      },
+    },
+    /* 显示操作弹窗 */
+    showPop: {
+      type: Boolean,
+      default: false,
+    },
+    /* 弹窗按钮列表 */
+    popButton: {
+      type: Array,
+      default() {
+        // 如下
+        // return ["标为关注", "置顶聊天", "删除该聊天"]
+        return [];
+      },
+    },
+    /* 弹窗定位样式 */
+    popStyle: {
+      type: String,
+      default: "",
+    },
+    change:{
+      type:Function,
+    }
+  },
+  methods: {
+    /* 隐藏弹窗 */
+    hidePop() {
+      this.$emit('hidePop')
+    },
+    /* 选择菜单 */
+    pickerMenu() {
+      this.$emit('change')
+      this.hidePop();
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+/* 遮罩 */
+.shade {
+  position: fixed;
+  z-index: 100;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  -webkit-touch-callout: none;
+
+  .pop {
+    position: fixed;
+    z-index: 101;
+    width: 200upx;
+    box-sizing: border-box;
+    font-size: 28upx;
+    text-align: left;
+    color: #333;
+    background-color: #fff;
+    box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
+    line-height: 80upx;
+    transition: transform 0.15s ease-in-out 0s;
+    user-select: none;
+    -webkit-touch-callout: none;
+    transform: scale(0, 0);
+
+    &.show {
+      transform: scale(1, 1);
+    }
+
+    & > view {
+      padding: 0 20upx;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      user-select: none;
+      -webkit-touch-callout: none;
+
+      &:active {
+        background-color: #f3f3f3;
+      }
+    }
+  }
+}
+</style>

+ 154 - 0
chat/components/chat/msglist/msglist.css

@@ -0,0 +1,154 @@
+/*.chat-bg{
+	position:fixed;
+	width: 100%;
+	height: 100%;
+	z-index: 0;
+	top: 50px;
+}*/
+.scroll_view,.scroll_view_change {
+	/* width: 100%;
+	position: absolute;
+	left: 0;
+	right: 0;
+	bottom: 170rpx; */
+	background-color: #FAFAFA;
+	padding-bottom: 100rpx;
+}
+
+.scroll_view_X,
+.scroll_view_change_X{
+	padding-bottom: 230rpx;
+}
+
+.tips{
+	position: fixed;
+	top: 200px;
+	left: 40px;
+	right: 40px;
+	color: #bbb;
+}
+.message {
+	width: 100%;
+	height: auto;
+	padding: 0 30rpx;
+	position: relative;
+
+}
+
+.time {
+	margin: 14rpx 0;
+	text-align: center;
+}
+
+.time .time-text {
+	display: inline-block;
+	padding: 6rpx 20rpx 0 20rpx;
+	font-size: 24rpx;
+	color: #fff;
+	line-height: 28rpx;
+	border-radius: 4rpx;
+	background-color: #dcdcdc;
+}
+
+.user .user-text {
+	margin: auto 100rpx 8rpx;
+	font-size: 20rpx;
+	color: #dcdcdc;
+	display: block;
+}
+
+.avatar {
+	width: 72rpx;
+	height: 72rpx;
+	margin: 0 20rpx 0 0;
+	border-radius: 6rpx;
+	float: left;
+}
+
+.msg {
+	display: inline-block;
+	padding: 20rpx;
+	max-width: calc(85% - 80rpx);
+	min-height: 40rpx; 
+	font-size: 24rpx;
+	/*overflow: hidden;*/
+	text-align: left;
+	word-break: break-all;
+	background-color: #fff;
+	border-radius: 26rpx;
+	position: relative;
+	margin-top: 24rpx;
+}
+
+.msg .msg_poprightarrow {
+	position: absolute;
+	right: -10rpx;
+	height: 18rpx;
+	width: 18rpx;
+	margin-top: -10rpx;
+}
+
+.msg .msg_popleftarrow{
+	position:absolute;
+	left: -14rpx;
+	height: 18rpx;
+	width: 18rpx;
+	margin-top: -10rpx;
+}
+
+.msg .msg-text {
+	line-height: 40rpx;
+	font-size: 32rpx;
+	margin: 0;
+}
+
+/*.msg:before {
+	content: " ";
+	position: absolute;
+	top: 9px;
+	right: 100%;
+	border: 6px solid transparent;
+	border-right-color: #EDEDED;
+}*/
+
+.self {
+	text-align: right;
+}
+
+.self .avatar {
+	float: right;
+	margin: 0 0 0 20rpx;
+}
+
+.user {
+	position: relative;
+	bottom: -30rpx;
+}
+
+.self .msg {
+	background-color: #0873DE;
+	color: #fff;
+}
+
+.self .msg:before {
+	right: inherit;
+	left: 100%;
+	border-right-color: transparent;
+	border-left-color: #b2e281;
+}
+
+.template {
+	display: inline;
+}
+.err{
+	width: 32rpx;
+	height: 32rpx;
+	position: absolute;
+	left: -40rpx;
+}
+.hide{
+	display: none;
+}
+.show{
+	display: block;
+}

+ 449 - 0
chat/components/chat/msglist/msglist.vue

@@ -0,0 +1,449 @@
+<template>
+	<view scroll-y="true" :class="view + ' wrap ' + (isIPX?'scroll_view_X': '')" @tap="onTap" upper-threshold="-50"
+		:scroll-into-view="toView">
+		<view>
+			<u-modal v-model="show" title="消息举报" ref="uModal" confirm-text="举报" :async-close="true"
+				@confirm="reportMsg">
+				<view class="slot-content">
+					<u-field v-model="reason" label="举报原因" placeholder="请填写举报原因" type="textarea" :auto-height="false"
+						:clearable="false" maxlength="100">
+					</u-field>
+				</view>
+			</u-modal>
+			<u-action-sheet :list="list" @click="onMenuClick" v-model="showRpt"></u-action-sheet>
+			<u-action-sheet :list="typeList" @click="onReportTypeClick" v-model="showRptType"></u-action-sheet>
+		</view>
+		<!-- <view class="tips">本应用仅用于环信产品功能开发测试,请勿用于非法用途。任何涉及转账、汇款、裸聊、网恋、网购退款、投资理财等统统都是诈骗,请勿相信!</view> -->
+		<view @longtap="onMsgTap(item)" class="message" v-for="item in chatMsg" :key="item.mid" :id="item.mid">
+			<view class="main" :class="item.style">
+				<view class="user">
+					<!-- yourname:就是消息的 from -->
+					<text v-if="!item.style"
+						class="user-text">{{ showMessageListNickname(item.yourname) + ' ' + handleTime(item)}}</text>
+				</view>
+				<image class="avatar" :src="showMessageListAvatar(item)" />
+				<view class="msg">
+					<image class="err" :class="(item.style == 'self' && item.isFail) ?  'show' : 'hide'"
+						src="../../../static/msgerr.png" />
+
+					<image v-if="item.style == 'self'" src="../../../static/poprightarrow2x.png"
+						class="msg_poprightarrow" />
+					<image v-if="item.style == ''" src="../../../static/popleftarrow2x.png" class="msg_popleftarrow" />
+					<view v-if="item.msg.type == 'img' || item.msg.type == 'video'">
+						<image v-if="item.msg.type == 'img'" class="avatar" :src="item.msg.data"
+							style="width:90px; height:120px; margin:2px auto;" mode="aspectFit" @tap="previewImage"
+							:data-url="item.msg.data" />
+						<video v-if="item.msg.type == 'video'" :src="item.msg.data" controls style="width:300rpx;" />
+					</view>
+					<audio-msg v-if="item.msg.type == 'audio'" :msg="item"></audio-msg>
+					<file-msg v-if="item.msg.type == 'file'" :msg="item"></file-msg>
+					<view v-else-if="item.msg.type == 'txt' || item.msg.type == 'emoji'">
+						<view class="template" v-for="(d_item, d_index) in item.msg.data" :key="d_index">
+							<text :data-msg="item" @tap="clickMsg" v-if="d_item.type == 'txt'" class="msg-text"
+								style="float:left;" selectable="true">{{ d_item.data }}</text>
+
+							<image v-if="d_item.type == 'emoji'" class="avatar"
+								:src="'../../../static/faces/' + d_item.data"
+								style="width:25px; height:25px; margin:0 0 2px 0; float:left;" />
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+	<!-- <view style="height: 1px;"></view> -->
+</template>
+
+
+<script>
+	let msgStorage = require("../msgstorage");
+	let disp = require("../../../../hxChatSDK/utils/broadcast");
+	let LIST_STATUS = {
+		SHORT: "scroll_view_change",
+		NORMAL: "scroll_view"
+	};
+	let page = 0;
+	let Index = 0;
+	let curMsgMid = '';
+	let isFail = false;
+	import audioMsg from "./type/audio/audio";
+	import fileMsg from "./type/file";
+	let WebIM = require("../../../../hxChatSDK/utils/WebIM")["default"];
+	export default {
+		data() {
+			return {
+				view: LIST_STATUS.NORMAL,
+				toView: "",
+				chatMsg: [],
+				__visibility__: false,
+				isIPX: false,
+				title: '消息举报',
+				list: [{
+					text: '举报'
+				}],
+				show: false,
+				showRpt: false,
+				showRptType: false,
+				rptMsgId: '', // 举报消息id
+				rptType: '', // 举报类型
+				reason: '',
+				typeList: [{
+						text: "涉政"
+					},
+					{
+						text: "涉黄"
+					},
+					{
+						text: "广告"
+					},
+					{
+						text: "辱骂"
+					},
+					{
+						text: "暴恐"
+					},
+					{
+						text: "违禁"
+					}
+				],
+				defaultAvatar: "../../../static/theme2x.png",
+				defaultGroupAvatar: "../../..//static/groupTheme.png"
+			};
+		},
+
+		components: {
+			audioMsg,
+			fileMsg
+		},
+		props: {
+			username: {
+				type: Object,
+				default: () => ({})
+			}
+		},
+
+		// lifetimes
+		created() {},
+
+		beforeMount() {
+			this.__visibility__ = true;
+			page = 0;
+			Index = 0;
+		},
+
+		moved() {},
+
+		destroyed() {
+			this.__visibility__ = false;
+			msgStorage.off("newChatMsg", this.dispMsg)
+		},
+
+		mounted(event) {
+
+			let me = this;
+			if (getApp().globalData.isIPX) {
+				this.setData({
+					isIPX: true
+				});
+			}
+
+			this.username = uni.username;
+			let username = this.username;
+			let myUsername = uni.getStorageSync("myUsername");
+			let sessionKey = username.groupId ? username.groupId + myUsername : username.your + myUsername;
+			let chatMsg = uni.getStorageSync(sessionKey) || [];
+			this.renderMsg(null, null, chatMsg, sessionKey);
+			uni.setStorageSync(sessionKey, null);
+			disp.on('em.error.sendMsgErr', function(err) {
+				// curMsgMid = err.data.mid;
+				isFail = true;
+				// return;
+				console.log('发送失败了');
+				return;
+				let msgList = me.chatMsg;
+				msgList.map(item => {
+					if (item.mid.substring(item.mid.length - 10) == curMsgMid.substring(curMsgMid.length -
+							10)) {
+						item.msg.data[0].isFail = true;
+						item.isFail = true;
+						me.setData({
+							chatMsg: msgList
+						});
+					}
+				});
+
+				// if (me.curChatMsg[0].mid == curMsgMid) {
+				//   me.curChatMsg[0].msg.data[0].isShow = false;
+				//   me.curChatMsg[0].isShow = false;
+				// }
+
+				uni.setStorageSync("rendered_" + sessionKey, msgList);
+			});
+			msgStorage.on("newChatMsg", this.dispMsg);
+		},
+		computed: {
+			//消息列表头像展示
+			showMessageListAvatar() {
+				const friendUserInfoMap = getApp().globalData.friendUserInfoMap;
+				const myUserInfos = getApp().globalData.userInfoFromServer;
+				return (item) => {
+					if (!item.style) {
+						if (friendUserInfoMap.has(item.username) && friendUserInfoMap.get(item.username)?.avatarurl) {
+							return friendUserInfoMap.get(item.username).avatarurl
+						} else {
+							return this.defaultAvatar
+						}
+					} else {
+						if (myUserInfos?.avatarurl) {
+							return myUserInfos.avatarurl
+						} else {
+							return this.defaultAvatar
+						}
+					}
+
+				}
+			},
+			//消息列表昵称显示
+			showMessageListNickname() {
+				const friendUserInfoMap = getApp().globalData.friendUserInfoMap;
+				return (hxId) => {
+					if (friendUserInfoMap.has(hxId) && friendUserInfoMap.get(hxId)?.nickname) {
+						return friendUserInfoMap.get(hxId).nickname
+					} else {
+						return hxId
+					}
+
+				}
+			},
+			//处理时间显示
+			handleTime() {
+				return (item) => {
+					return this.$u.timeFormat(item.time, 'mm/dd/hh:MM')
+				}
+			}
+		},
+		methods: {
+			normalScroll() {
+				this.setData({
+					view: LIST_STATUS.NORMAL
+				});
+			},
+			dispMsg(renderableMsg, type, curChatMsg, sesskey) {
+				let me = this;
+				let username = this.username;
+				let myUsername = uni.getStorageSync("myUsername");
+				let sessionKey = username.groupId ? username.groupId + myUsername : username.your + myUsername;
+				me.curChatMsg = curChatMsg;
+
+				if (!me.__visibility__) return; // 判断是否属于当前会话
+
+				if (username.groupId) {
+					// 群消息的 to 是 id,from 是 name
+					if (renderableMsg.info.from == username.groupId || renderableMsg.info.to == username.groupId) {
+						if (sesskey == sessionKey) {
+							me.renderMsg(renderableMsg, type, curChatMsg, sessionKey, 'newMsg');
+						}
+					}
+				} else if (renderableMsg.info.from == username.your || renderableMsg.info.to == username.your) {
+					if (sesskey == sessionKey) {
+						me.renderMsg(renderableMsg, type, curChatMsg, sessionKey, 'newMsg');
+					}
+				}
+			},
+			onInput(e) {
+				this.setData({
+					reason: e.target.value
+				})
+			},
+			shortScroll() {
+				this.setData({
+					view: LIST_STATUS.SHORT
+				});
+			},
+
+			onTap() {
+				this.$emit("msglistTap", null, {
+					bubbles: true
+				});
+			},
+
+			onMsgTap(item) {
+				// 别人发的消息
+				if (item.style !== 'self') {
+					this.setData({
+						showRpt: true,
+						rptMsgId: item.id
+					})
+				}
+			},
+
+			onMenuClick(idx) {
+				if (idx === 0) {
+					this.setData({
+						showRptType: true
+					})
+				}
+			},
+
+			reportMsg() {
+				let that = this
+				if (this.reason === '') {
+					this.$refs.uModal.clearLoading();
+					uni.showToast({
+						title: "请填写举报原因",
+						icon: 'none'
+					});
+					return false
+				}
+				WebIM.conn
+					.reportMessage({
+						reportType: that.rptType, // 举报类型
+						reportReason: that.reason, // 举报原因。
+						messageId: that.rptMsgId // 上报消息id
+					})
+					.then(() => {
+						uni.showToast({
+							title: "举报成功",
+							icon: 'none'
+						});
+					})
+					.catch((e) => {
+						uni.showToast({
+							title: "举报失败",
+							icon: 'none'
+						});
+					}).finally(() => {
+						that.setData({
+							reason: '',
+							rptType: '',
+							rptMsgId: '',
+							show: false
+						})
+					});
+			},
+
+			onReportTypeClick(idx) {
+				// 设置举报类型
+				this.setData({
+					rptType: this.typeList[idx].text,
+					show: true
+				})
+			},
+
+
+			previewImage(event) {
+				var url = event.target.dataset.url;
+				uni.previewImage({
+					urls: [url] // 需要预览的图片 http 链接列表
+				});
+			},
+
+			getHistoryMsg() {
+				let me = this;
+				let username = this.username;
+				let myUsername = uni.getStorageSync("myUsername");
+				let sessionKey = username.groupId ? username.groupId + myUsername : username.your + myUsername;
+				let historyChatMsgs = uni.getStorageSync("rendered_" + sessionKey) || [];
+
+				if (Index < historyChatMsgs.length) {
+					let timesMsgList = historyChatMsgs.slice(-Index - 10, -Index);
+					this.setData({
+						chatMsg: timesMsgList.concat(me.chatMsg),
+						toView: timesMsgList[timesMsgList.length - 1].mid
+					});
+					Index += timesMsgList.length;
+
+					if (timesMsgList.length == 10) {
+						page++;
+					}
+
+					uni.stopPullDownRefresh();
+				}
+			},
+
+			renderMsg(renderableMsg, type, curChatMsg, sessionKey, isnew) {
+				let me = this;
+
+				var historyChatMsgs = uni.getStorageSync("rendered_" + sessionKey) || [];
+
+				historyChatMsgs = historyChatMsgs.concat(curChatMsg);
+
+				if (!historyChatMsgs.length) return;
+
+				if (isnew == 'newMsg') {
+					this.setData({
+						chatMsg: this.chatMsg.concat(curChatMsg),
+						// 跳到最后一条
+						toView: historyChatMsgs[historyChatMsgs.length - 1].mid
+					});
+				} else {
+					this.setData({
+						chatMsg: historyChatMsgs.slice(-10),
+						// 跳到最后一条
+						toView: historyChatMsgs[historyChatMsgs.length - 1].mid
+					});
+				}
+
+				uni.setStorageSync("rendered_" + sessionKey, historyChatMsgs);
+				let chatMsg = uni.getStorageSync(sessionKey) || [];
+				chatMsg.map(function(item, index) {
+					curChatMsg.map(function(item2, index2) {
+						if (item2.mid == item.mid) {
+							chatMsg.splice(index, 1);
+						}
+					});
+				});
+				uni.setStorageSync(sessionKey, chatMsg);
+				Index = historyChatMsgs.slice(-10).length;
+				// setTimeout 兼容支付宝小程序
+				setTimeout(() => {
+					uni.pageScrollTo({
+						scrollTop: 5000,
+						duration: 300,
+						fail: (e) => {
+							//console.log('滚失败了', e)
+						}
+					});
+				}, 100)
+
+				if (isFail) {
+					this.renderFail(sessionKey);
+				}
+			},
+
+			renderFail(sessionKey) {
+				let me = this;
+				let msgList = me.chatMsg;
+				msgList.map(item => {
+					if (item.mid.substring(item.mid.length - 10) == curMsgMid.substring(curMsgMid.length - 10)) {
+						item.msg.data[0].isFail = true;
+						item.isFail = true;
+						me.setData({
+							chatMsg: msgList
+						});
+					}
+				});
+
+				if (me.curChatMsg[0].mid == curMsgMid) {
+					me.curChatMsg[0].msg.data[0].isShow = false;
+					me.curChatMsg[0].isShow = false;
+				}
+
+				uni.setStorageSync("rendered_" + sessionKey, msgList);
+				isFail = false;
+			},
+
+			clickMsg(event) {
+				if (typeof(event.target.dataset.msg) == 'object' &&
+					event.target.dataset.msg.msg.ext &&
+					event.target.dataset.msg.msg.ext.msg_extension) {
+					this.$emit("clickMsg", event.target.dataset.msg.msg.ext)
+				}
+			}
+
+		}
+	};
+</script>
+<style>
+	@import "./msglist.css";
+</style>

+ 25 - 0
chat/components/chat/msglist/type/audio/audio.css

@@ -0,0 +1,25 @@
+.audio-player {
+	position: relative;
+	overflow: hidden;
+	display: flex;
+	transition: opacity .5s;
+}
+
+.audio-player .controls {
+	height:40rpx;
+	align-items:center;
+	display:flex;
+}
+
+.audio-player .controls image {
+	height: 48rpx;
+	width: 30rpx;
+	margin: 0 8rpx 0 100rpx;
+}
+
+.audio-player .time {
+	font-size: 34rpx;
+	line-height: 40rpx;
+	flex: 1;
+	text-align: left;
+}

+ 192 - 0
chat/components/chat/msglist/type/audio/audio.vue

@@ -0,0 +1,192 @@
+<template>
+<view class="audio-player" @tap="audioPlay" :style="'opacity: ' + opcity">
+	<text class="time">语音消息 {{ time }}</text>
+	<view class="controls play-btn">
+		<image :src="style == 'self'? '../../../../../static/voicemsgmy.png' : '../../../../../static/voicemsg.png'"></image>
+	</view>
+</view>
+</template>
+
+<script>
+let audioCtxFc = require("./audioCtxFactory");
+let playStatus = require("./playStatus");
+
+export default {
+  data() {
+    return {
+      playStatus: playStatus,
+      curStatus: playStatus.STOP,
+      time: "0'",
+      opcity: 1,
+      __comps__: {
+        audioCtx: null
+      },
+      style: ""
+    };
+  },
+
+  components: {},
+  props: {
+    msg: {
+      type: Object,
+      val: {}
+    }
+  },
+  obeyMuteSwitch: false,
+  autoplay: true,
+
+  // lifetimes
+  created() {},
+
+  beforeMount() {
+    this.setData({
+      time: this.msg.msg.length + "''",
+      style: this.msg.style
+    });
+  },
+
+  moved() {},
+
+  destroyed() {
+    let audioCtx = this.$data.__comps__.audioCtx = audioCtxFc.getCtx(this.msg.mid);
+    this.audioPause(audioCtx);
+    this.delEvent(); //audioCtx.destroy();
+  },
+
+  mounted() {
+    let self = this;
+    let curl = '';
+    let audioCtx = this.$data.__comps__.audioCtx = audioCtxFc.getCtx(this.msg.mid);
+    audioCtx.autoplay = false;
+    audioCtx.loop = false; //
+
+    this.onPlaying = () => {
+      //console.log("onPlaying", JSON.stringify(this.data));
+      this.setData({
+        curStatus: playStatus.PLAYING
+      });
+      uni.inter && clearInterval(uni.inter);
+      uni.inter = setInterval(() => {
+        let opcity = this.opcity;
+        this.setData({
+          opcity: opcity == 1 ? 0.4 : 1
+        });
+      }, 500);
+    };
+
+    this.onPause = () => {
+      // console.log("onPause", JSON.stringify(this.data));
+      // 第二次播放会立即抛出一个异常的 onPause
+      if (parseInt(this.time, 10) < 1) {
+        return;
+      }
+
+      this.setData({
+        curStatus: playStatus.PAUSE,
+        opcity: 1 //time: "0'",
+
+      });
+    };
+
+    this.onDone = () => {
+      // console.log("onDone", JSON.stringify(this.data));
+      this.setData({
+        curStatus: playStatus.STOP,
+        opcity: 1 //time: "0'",
+
+      });
+      clearInterval(uni.inter);
+    }; // 多次播放会丢失这个回调
+
+
+    this.onTimeUpdate = () => {
+      this.setData({
+        time: (audioCtx.currentTime >> 0) + "'"
+      });
+    };
+
+    this.onWait = () => {
+      uni.showToast({
+        title: "下载中...",
+        duration: 1000
+      });
+    };
+
+    this.addEvent();
+  },
+
+  methods: {
+    audioPlay() {
+	if(uni.getSystemInfo().app === 'alipay'){
+		// https://forum.alipay.com/mini-app/post/7301031?ant_source=opendoc_recommend
+		uni.showToast({
+			duration: 2000,
+			title: '支付宝小程序不支持音频消息'
+		})
+		return 
+	}
+      uni.inter && clearInterval(uni.inter);
+      let audioCtx = this.$data.__comps__.audioCtx;
+      var curl = '';
+      uni.downloadFile({
+        url: this.msg.msg.data,
+        header: {
+          "X-Requested-With": "XMLHttpRequest",
+          Accept: "audio/mp3",
+          Authorization: "Bearer " + this.msg.msg.token
+        },
+
+        success(res) {
+          curl = res.tempFilePath;
+          console.log('音频本地', audioCtx); //renderableMsg.msg.url = res.tempFilePath;
+
+          audioCtx.src = curl;
+          audioCtx.play();
+        },
+
+        fail(e) {
+          console.log("downloadFile failed", e);
+          uni.showToast({
+            title: "下载失败",
+            duration: 1000
+          });
+        }
+
+      });
+    },
+
+    audioPause(auCtx) {
+      //let audioCtx = this.data.__comps__.audioCtx;
+      let audioCtx = this.$data.__comps__.audioCtx = audioCtxFc.getCtx(this.msg.mid) || auCtx;
+      audioCtx && audioCtx.pause();
+    },
+
+    addEvent() {
+      let audioCtx = this.$data.__comps__.audioCtx;
+      audioCtx.onPlay(this.onPlaying);
+      audioCtx.onPause(this.onPause);
+      audioCtx.onWaiting(this.onPause);
+      audioCtx.onStop(this.onDone);
+      audioCtx.onEnded(this.onDone);
+      audioCtx.onError(this.onDone);
+      audioCtx.onWaiting(this.onWait); //audioCtx.onTimeUpdate(this.onTimeUpdate);
+    },
+
+    delEvent() {
+      let audioCtx = this.$data.__comps__.audioCtx;
+      audioCtx.offPlay(this.onPlaying);
+      audioCtx.offPause(this.onPause);
+      audioCtx.offWaiting(this.onPause);
+      audioCtx.offStop(this.onDone);
+      audioCtx.offEnded(this.onDone);
+      audioCtx.offError(this.onDone);
+      audioCtx.offWaiting(this.onWait); // 多次播放会丢失这个回调,所以不用卸载
+      // audioCtx.offTimeUpdate(this.onTimeUpdate);
+    }
+
+  }
+};
+</script>
+<style>
+@import "./audio.css";
+</style>

+ 62 - 0
chat/components/chat/msglist/type/audio/audioCtxFactory.js

@@ -0,0 +1,62 @@
+// 每一个音频消息都有自己的 ctx。
+// 可以有多个 ctx,每次播放都能知道是哪个 ctx 在调用,从而让其他的 ctx pause。
+// 消息销毁,记得处理 ctx。
+// 主要是同步跨 ctx 的操作,保证只有一个 ctx 播放
+let allCtx = {};
+let inUseCtx = null;
+let allComm = {};
+
+function proxier(ctx) {
+  let __play__ = ctx.play;
+  let __pause__ = ctx.pause;
+  ctx.play = playProxier;
+  ctx.pause = pauseProxier;
+
+  function playProxier() {
+    // 如果正在播放的不是自己,暂停
+    if (inUseCtx && inUseCtx != this) {
+      inUseCtx.pause();
+    }
+
+    __play__.call(this);
+
+    inUseCtx = this;
+  }
+
+  function pauseProxier() {
+    // 只有是自己才 pause
+    if (inUseCtx == this) {
+      __pause__.call(this);
+    }
+  }
+}
+
+module.exports = {
+  getCtx(mid) {
+    let returnCtx = allCtx[mid];
+
+    if (!returnCtx) {
+      returnCtx = uni.createInnerAudioContext();
+      allCtx[mid] = returnCtx;
+      proxier(returnCtx);
+    }
+
+    return returnCtx;
+  },
+
+  getAllCtx() {
+    uni.setStorageSync("allCtx", JSON.stringify(Object.keys(allCtx)));
+    return allCtx;
+  },
+
+  getCommponet(mid, comm) {
+    let curComm = allComm[mid];
+
+    if (!curComm) {
+      allComm[mid] = comm;
+    }
+
+    return allComm;
+  }
+
+};

+ 5 - 0
chat/components/chat/msglist/type/audio/playStatus.js

@@ -0,0 +1,5 @@
+module.exports = {
+  PLAYING: "playing",
+  PAUSE: "pause",
+  STOP: "stop"
+};

+ 10 - 0
chat/components/chat/msglist/type/file/index.css

@@ -0,0 +1,10 @@
+.attach-msg {
+  display: flex;
+  align-items: center;
+}
+
+.fileIcon {
+  width: 40rpx;
+  height: 40rpx;
+  margin-right: 8rpx;
+}

+ 139 - 0
chat/components/chat/msglist/type/file/index.vue

@@ -0,0 +1,139 @@
+<template>
+  <view class="attach-msg" @tap="previewFile">
+    <image class="fileIcon" src="../../../../../static/msgFile.png" />
+    <text class="time"
+      >{{ msg.msg.filename }}{{ formatMoney(msg.msg.size) }}</text
+    >
+  </view>
+</template>
+
+<script>
+const ATTACH_KEY = "filePathCache";
+export default {
+  data() {
+    return {};
+  },
+
+  components: {},
+  props: {
+    msg: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+
+  created() {},
+
+  moved() {},
+
+  methods: {
+    formatMoney(value) {
+      return `(${(value / 1024 / 1024).toFixed(2)}M)`;
+    },
+    storageFilePath({ id, path }) {
+      uni.getStorage({
+        key: ATTACH_KEY,
+        success: (res) => {
+          let dt = res.data || {};
+          uni.setStorage({
+            key: ATTACH_KEY,
+            data: {
+              ...dt,
+              [id]: path
+            }
+          });
+        },
+        fail: (e) => {
+          uni.setStorage({
+            key: ATTACH_KEY,
+            data: {
+              [id]: path
+            }
+          });
+        }
+      });
+    },
+    downloadFile() {
+      let _this = this;
+      // 下载完存储filePath,下次预览直接访问
+      uni.downloadFile({
+        url: `${this.msg.msg?.url}`,
+        success: function (res) {
+          let filePath = res.tempFilePath;
+          uni.saveFile({
+            tempFilePath: filePath,
+            success: function (res) {
+              let savedFilePath = res.savedFilePath;
+              _this.storageFilePath({
+                id: _this.msg.id,
+                path: savedFilePath
+              });
+              _this.openFile(savedFilePath);
+            },
+            fail: function (e) {
+              uni.hideLoading();
+              uni.showToast({
+                icon: "none",
+                title: "保存失败"
+              });
+            }
+          });
+        },
+        fail: () => {
+          uni.hideLoading();
+          uni.showToast({
+            icon: "none",
+            title: "失败请重新下载"
+          });
+        }
+      });
+    },
+    openFile(filePath) {
+      uni.openDocument({
+        filePath,
+        success: function (res) {
+          uni.hideLoading();
+        },
+        fail(e) {
+          uni.showToast({
+            title: "暂不支持此类型",
+            duration: 2000
+          });
+          uni.hideLoading();
+        }
+      });
+    },
+    previewFile() {
+      let sysInfo = uni.getSystemInfoSync();
+      if (sysInfo.uniPlatform === "web") {
+        uni.showToast({
+          title: "H5暂不支持预览文件消息",
+          duration: 2000,
+          icon: "none"
+        });
+        return;
+      }
+      uni.showLoading();
+      uni.getStorage({
+        key: ATTACH_KEY,
+        success: (res) => {
+          let dt = res.data || {};
+          let path = dt[this.msg.id];
+          if (path) {
+            this.openFile(path);
+          } else {
+            this.downloadFile();
+          }
+        },
+        fail: (e) => {
+          this.downloadFile();
+        }
+      });
+    }
+  }
+};
+</script>
+
+<style>
+@import "./index.css";
+</style>

+ 64 - 0
chat/components/chat/msgpackager.js

@@ -0,0 +1,64 @@
+let WebIM = require("../../../hxChatSDK/utils/WebIM.js")["default"];
+
+let msgType = require("./msgtype.js");
+
+
+function getMsgData(sendableMsg, type) {
+  if (type == msgType.TEXT) {
+    return WebIM.parseEmoji(sendableMsg.value.replace(/\n/gm, ""));
+  } else if (type == msgType.EMOJI) {
+    return sendableMsg.value;
+  } else if (
+    type == msgType.IMAGE ||
+    type == msgType.VIDEO ||
+    type == msgType.AUDIO
+  ) {
+    return sendableMsg.body.body.url;
+  } else if (type == msgType.FILE) {
+    return sendableMsg.body.body.url;
+  }
+
+  return "";
+}
+module.exports = function (sendableMsg, type, myName) {
+  console.log(sendableMsg, 'sendableMsg')
+//   var time = WebIM.time();
+  var renderableMsg = {
+    info: {
+      from: sendableMsg.body.from,
+      to: sendableMsg.body.to
+    },
+    username:
+      sendableMsg.body.from == myName
+        ? sendableMsg.body.to
+        : sendableMsg.body.from,
+    yourname: sendableMsg.body.from,
+    msg: {
+      type: type,
+      url: sendableMsg.body.url ? sendableMsg.body.url : "",
+      data: getMsgData(sendableMsg, type),
+      ext: sendableMsg.body.ext
+    },
+    style: sendableMsg.body.from == myName ? "self" : "",
+    time: sendableMsg.time || Date.now(),
+    mid: sendableMsg.type + sendableMsg.id,
+    id: sendableMsg.id,
+    chatType: sendableMsg.body.chatType
+  };
+
+  if (type == msgType.IMAGE) {
+    renderableMsg.msg.size = {
+      width: sendableMsg.body.body.size.width,
+      height: sendableMsg.body.body.size.height
+    };
+  } else if (type == msgType.AUDIO) {
+    renderableMsg.msg.length = sendableMsg.body.length;
+  } else if (type == msgType.FILE) {
+    renderableMsg.msg.url = sendableMsg?.body?.body.url || "";
+    renderableMsg.msg.filename = sendableMsg?.body?.body.filename || "";
+    renderableMsg.msg.size = sendableMsg?.body?.body.file_length || 0;
+  }
+
+  return renderableMsg;
+
+};

+ 201 - 0
chat/components/chat/msgstorage.js

@@ -0,0 +1,201 @@
+let Disp = require("../../../hxChatSDK/utils/Dispatcher.js");
+
+let msgPackager = require("./msgpackager.js");
+
+let msgType = require("./msgtype.js");
+
+let msgStorage = new Disp();
+
+let disp = require("../../../hxChatSDK/utils/broadcast.js");
+
+msgStorage.saveReceiveMsg = function (receiveMsg, type) {
+  console.log(receiveMsg, 'receiveMsg')
+  let sendableMsg;
+
+  if (type == msgType.IMAGE) {
+    sendableMsg = {
+      id: receiveMsg.id,
+      type: type,
+      body: {
+        id: receiveMsg.id,
+        from: receiveMsg.from,
+        to: receiveMsg.to,
+        type: receiveMsg.type,
+        ext: receiveMsg.ext,
+        chatType: receiveMsg.type,
+        toJid: "",
+        body: {
+          type: type,
+          url: receiveMsg.url,
+          filename: receiveMsg.filename,
+          filetype: receiveMsg.filetype,
+          size: {
+            width: receiveMsg.width,
+            height: receiveMsg.height
+          }
+        }
+      },
+      time:receiveMsg.time
+    };
+  } else if (type == msgType.TEXT || type == msgType.EMOJI) {
+    sendableMsg = {
+      id: receiveMsg.id,
+      type: type,
+      body: {
+        id: receiveMsg.id,
+        from: receiveMsg.from,
+        to: receiveMsg.to,
+        type: receiveMsg.type,
+        ext: receiveMsg.ext,
+        chatType: receiveMsg.type,
+        toJid: "",
+        body: {
+          type: type,
+          msg: receiveMsg.data
+        }
+      },
+      value: receiveMsg.data,
+      time:receiveMsg.time
+    };
+  } 
+  else if (type == 'INFORM') { // 通知消息
+    sendableMsg = {
+      body: {
+        from: receiveMsg.from,
+        to: receiveMsg.to,
+        chatType: 'INFORM',
+        gid:receiveMsg.gid ? receiveMsg.gid:'',
+        type:receiveMsg.type
+      },
+    };
+  } else if (type == msgType.FILE) {
+    sendableMsg = {
+      id: receiveMsg.id,
+      type: type,
+      body: {
+        id: receiveMsg.id,
+        length: receiveMsg.file_length,
+        from: receiveMsg.from,
+        to: receiveMsg.to,
+        type: receiveMsg.type,
+        ext: receiveMsg.ext,
+        chatType: receiveMsg.type,
+        toJid: "",
+        body: {
+          type: type,
+          url: receiveMsg.url,
+          filename: receiveMsg.filename,
+          file_length: receiveMsg.file_length
+        }
+      },
+      value: receiveMsg.data,
+      time:receiveMsg.time
+    };
+  } else if (type == msgType.AUDIO) {
+    sendableMsg = {
+      id: receiveMsg.id,
+      type: type,
+      accessToken: receiveMsg.token || receiveMsg.accessToken,
+      body: {
+        id: receiveMsg.id,
+        length: receiveMsg.length,
+        from: receiveMsg.from,
+        to: receiveMsg.to,
+        type: receiveMsg.type,
+        ext: receiveMsg.ext,
+        chatType: receiveMsg.type,
+        toJid: "",
+        body: {
+          type: type,
+          url: receiveMsg.url,
+          filename: receiveMsg.filename,
+          filetype: receiveMsg.filetype,
+          from: receiveMsg.from,
+          to: receiveMsg.to
+        }
+      },
+      time:receiveMsg.time
+    };
+  } else if (type == msgType.VIDEO) {
+    sendableMsg = {
+      id: receiveMsg.id,
+      type: type,
+      accessToken: receiveMsg.token || receiveMsg.accessToken,
+      body: {
+        id: receiveMsg.id,
+        length: receiveMsg.length,
+        from: receiveMsg.from,
+        to: receiveMsg.to,
+        type: receiveMsg.type,
+        ext: receiveMsg.ext,
+        chatType: receiveMsg.type,
+        toJid: "",
+        body: {
+          type: type,
+          url: receiveMsg.url,
+          filename: receiveMsg.filename,
+          filetype: receiveMsg.filetype,
+          from: receiveMsg.from,
+          to: receiveMsg.to
+        },
+      },
+      time:receiveMsg.time
+    };
+  } else {
+    return;
+  }
+
+  this.saveMsg(sendableMsg, type, receiveMsg);
+};
+
+msgStorage.saveMsg = function (sendableMsg, type, receiveMsg) {
+  let me = this;
+  let myName = uni.getStorageSync("myUsername");
+  let sessionKey; // 仅用作群聊收消息,发消息没有 receiveMsg
+
+  if (receiveMsg && receiveMsg.type == "groupchat") {
+    sessionKey = receiveMsg.to + myName;
+  } else if (sendableMsg.body.chatType === 'INFORM'){
+    sessionKey = 'INFORM'
+  }
+  
+  // 群聊发 & 单发 & 单收
+  else {
+    sessionKey = sendableMsg.body.from == myName ? sendableMsg.body.to + myName : sendableMsg.body.from + myName;
+  }
+
+  let curChatMsg = uni.getStorageSync(sessionKey) || [];
+  let renderableMsg = msgPackager(sendableMsg, type, myName);
+
+  if (type == msgType.AUDIO) {
+    renderableMsg.msg.length = sendableMsg.body.length;
+    renderableMsg.msg.token = sendableMsg.accessToken;
+  }
+
+  curChatMsg.push(renderableMsg); //console.log('renderableMsgrenderableMsg', renderableMsg)
+
+  if (type == msgType.VIDEO) {
+    renderableMsg.msg.token = sendableMsg.accessToken; //如果是音频则请求服务器转码
+  }
+
+
+  save();
+
+  function save() {
+    uni.setStorage({
+      key: sessionKey,
+      data: curChatMsg,
+
+      success() {
+        if (type == msgType.AUDIO || type == msgType.VIDEO) {
+          disp.fire('em.chat.audio.fileLoaded');
+        }
+
+        me.fire("newChatMsg", renderableMsg, type, curChatMsg, sessionKey);
+      }
+
+    });
+  }
+};
+
+module.exports = msgStorage;

+ 14 - 0
chat/components/chat/msgtype.js

@@ -0,0 +1,14 @@
+module.exports = {
+  IMAGE: "img",
+  TEXT: "txt",
+  LOCATION: "location",
+  VIDEO: "video",
+  AUDIO: "audio",
+  EMOJI: "emoji",
+  FILE: "file",
+  //
+  chatType: {
+    SINGLE_CHAT: "singleChat",
+    CHAT_ROOM: "chatRoom"
+  }
+};

+ 0 - 0
chat/components/chat/multiemedia/index.css


+ 508 - 0
chat/components/chat/multiemedia/index.nvue

@@ -0,0 +1,508 @@
+<template>
+	<view class="wraper">
+		<view class="infoConnecting">
+			11多人会议
+		</view>
+		<live-pusher class="live-pusher" :url="pubUrl" @netstatus="netstatusChange" :muted="false" :enable-camera="true" id="livePusher"
+		 ref="livePusher" @statechange="statechange" mode="HD" @error="error"></live-pusher>
+		</live-pusher>
+	
+			<view  
+				v-if="subUrls.length > 0"
+				v-for="item in subUrls">
+	
+				<video
+					:id="item.streamId"
+					:src="item.subUrl" 
+					object-fit="contain"
+					autoplay 
+				>
+					<view class="userName">{{item.memName}}</view>
+				</video>
+	
+			</view>
+		<!-- </scroll-view> -->
+	
+	
+		<!-- <view class="controlContent">
+			<view class="emediaContrContent">
+				<view class="controlItem" @tap="toggleCamera" style="{color: devicePositionColor}">
+					<image
+						class="icon-record"
+						:src="'../../../static/images/'+devicePositionIcon+'2x.png'" style="{width:'22px'; height: '24px'}"/>
+					切换摄像头
+				</view>
+				<view class="controlItem" @tap="toggleMuted" style="{color: micphoneColor}">
+					<image
+						class="icon-record"
+						:src="'../../../static/images/'+micphoneIcon+'2x.png'" style="{width:'22px'; height: '24px'}"/>
+					麦克风</view>
+				<view class="controlItem" @tap="togglePlay" style="{color: videoColor}">
+					<image
+						class="icon-record"
+						:src="'../../../static/images/'+videoIcon+'2x.png'" style="{width:'22px'; height: '24px'}"/>
+					视频</view>
+				<view class="controlItem" @tap="toggleBeauty" style="{color: beautyColor}">
+					<image
+						class="icon-record"
+						:src="'../../../static/images/'+beautyIcon+'.png'" style="{width:'16px'; height: '24px'}"/>
+					美颜</view>
+				<view class="controlItem" @tap="inviteMember">
+					<image
+						class="icon-record"
+						src='../../../static/images/invite_white2x.png' style="{width:'22px'; height: '24px'"/>
+					邀请</view>
+			</view>
+	
+			<view class="hangup" @tap="hangup">
+				<image
+					class="icon-record"
+					src='../../../static/images/hangup2x.png'/>
+			</view>
+		</view>
+	 -->
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				pubUrl: "",
+				subUrls: [],
+				showInvite: true,
+				devicePosition: "front",
+				muted: false,
+				playVideoMuted: false,
+		
+				devicePositionIcon: 'switchCamera_white',
+				devicePositionColor: '#fff',
+				micphoneIcon: 'micphone_white',
+				beautyIcon: 'beauty',
+				micphoneColor: '#fff',
+				videoIcon: 'video_white',
+				videoColor: '#fff',
+				beauty: 9,
+				beautyColor: '#fff',
+				myName: '',
+				confrId: '',
+				enableCamera: true,
+				time: '',
+				context: {}
+			}
+		},
+		props: {
+		  username: {
+		    type: Object,
+		    default: () => ({}),
+		  },
+		  action: {
+		    type: Object,
+		    default: {},
+		  },
+		  groupId:{
+		    type: String,
+		    default: '',
+		  },
+		},
+		methods: {
+			joinRoom(data){
+				let me = this;
+				console.log('joinRoom', data)
+				me.context = uni.createLivePusherContext("livePusher", me);
+				console.log('我的播放组件', me.context)
+				var context = this.context
+				if (context && context.ctx && context.ctx.attr && context.ctx.attr.url) {
+					context.stop()
+				}
+				let id = wx.WebIM.conn.getUniqueId();
+				let roomName = 'wxConfr' + id //随机的房间名,防止和别人的房间名冲突
+				let rec = wx.getStorageSync("rec") || false;
+				let recMerge = wx.getStorageSync("recMerge") || false;
+				let params = {
+					roomName,
+					password: '',
+					role: 7,
+					config: {
+						rec,
+						recMerge
+					}
+				}
+				if (data) {
+					params.roomName = data.roomName
+					params.password = data.password
+				}
+				
+				wx.emedia.mgr.joinRoom(params).then((res) => {
+					console.log('res', res)
+					let confrId = res.confrId
+					me.setData({
+						confrId: confrId
+					})
+					me.$emit('createConfrSuccess', {confrId: confrId, groupId: me.username.groupId, roomName: params.roomName, password: params.password})
+					
+					console.log('--------+--+----+---+--------')
+					let rtcId = wx.emedia.util.getRtcId()
+					console.log(11111111111111, confrId)
+					wx.emedia.mgr.pubStream(rtcId).then(function(res){
+						console.log('pubUrl-------------', res.data.rtmp)
+						console.log(2222222222222)
+						me.setData({
+							pubUrl: res.data.rtmp
+						})
+						setTimeout(() => {
+							console.log('55555555555', me)
+							console.log(me.context)
+							me.context.start()
+						}, 500)
+					}).catch(e => {
+					    console.log(4444444, e)   //404
+					    return f(false);  //即便返回了一个成功的promise,下面的finally也会执行,如果返回的是失败的promise,控制台最后一行会报错uncaught (in promise) 404
+					})
+					.finally( (e) => {
+					    console.log(100,e)  //100
+					})
+					console.log(33333333333333, me.pubUrl)
+				})
+				
+				
+			},
+			createConf(){
+				console.log('>>> createConf');
+				
+				var me = this
+				let rec = wx.getStorageSync("rec") || false;
+				let recMerge = wx.getStorageSync("recMerge") || false;
+				//参数:会议类型 密码 是否录制 是否合并
+				wx.emedia.mgr.createConference(10, '', rec, recMerge).then(function(data){
+					console.log('成功', data)
+					let ticket = data.data.ticket
+					let ticketJosn = JSON.parse(ticket)
+					let confrId = ticketJosn.confrId
+	
+					wx.emedia.mgr.joinConferenceWithTicket(confrId, ticket).then(function(res){
+						console.log('加入会议成功', res)
+					})
+					// wx.emedia.mgr.joinConference(confrId, '').then(function(res){
+					// 	console.log('加入会议成功', res)
+					// })
+					
+					me.setData({
+						confrId
+					})
+					me.$emit('createConfrSuccess', {confrId: confrId, groupId: me.username.groupId})
+				})
+			},
+	
+			joinConf(data){
+				console.log('加入会议 ————-------————')
+				console.log(data)
+				let me = this
+				wx.emedia.mgr.getConferenceTkt(data.confrId, data.password).then(function(res){
+					console.log('申请reqTkt成功', res.data)
+					let ticket = res.data.ticket || ''
+					let tktObj = JSON.parse(ticket)
+					wx.emedia.mgr.joinConferenceWithTicket(data.confrId, ticket).then(function(res){
+						console.log('加入会议成功', res)
+					})
+					me.setData({
+						confrId: tktObj.confrId
+					})
+					me.$emit('createConfrSuccess', {confrId: tktObj.confrId, groupId: me.username.groupId})
+				})
+			},
+			
+			togglePlay(){
+				let me = this
+				console.log("%c togglePlay", "color:green")
+	
+				// this.LivePusherContext.stop()
+				this.setData({
+					enableCamera: !me.enableCamera,
+					pubUrl: me.pubUrl,
+					videoIcon: this.videoIcon == 'video_white'?'video_gray': 'video_white',
+					videoColor: this.videoColor == '#fff'? '#aaa': '#fff'
+				}, () => {
+					this.LivePusherContext.start()
+				})
+			},
+	
+			toggleCamera(){
+				console.log("%c toggleCamera", "color:green")
+				let me = this
+				// me.LivePusherContext.stop()
+				me.LivePusherContext.switchCamera({
+					success: function(){
+						me.setData({
+							devicePosition: me.devicePosition == 'fron' ? 'back' : 'front',
+							devicePositionIcon: me.devicePositionIcon =='switchCamera_white'?'switchCamera_gray': 'switchCamera_white',
+							devicePositionColor: me.devicePositionColor == '#fff'? '#aaa':'#fff'
+						}, () => {
+							// me.LivePusherContext.start()
+						})
+					}
+				})
+
+			},
+	
+			toggleMuted(){
+				console.log("%c toggleMuted", "color:green")
+				this.setData({
+					muted: !this.muted,
+					micphoneIcon: this.micphoneIcon == 'micphone_white'? 'micphone_gray': 'micphone_white',
+					micphoneColor: this.micphoneColor == '#fff'? '#aaa': '#fff'
+				})
+			},
+	
+			toggleBeauty(){
+				this.setData({
+					beauty: this.beauty == 0 ? 9 : 0,
+					beautyColor: this.beautyColor == '#fff'? '#aaa': '#fff',
+					beautyIcon: this.beautyIcon == 'beauty' ? 'beauty_gray': 'beauty'
+				})
+			},
+	
+			hangup(){
+				console.log('挂断', this.confrId)
+				wx.emedia.mgr.exitConference(this.confrId)
+				this.$emit('hangup')
+				this.stopTimer()
+			},
+			inviteMember(){
+				this.$emit('inviteMember', this.groupId)
+			},
+			// statechange(e){
+			// 	console.log('>>>>>>>>>live-pusher code:', e.detail)
+			// 	if (e.detail.code === 5001) {
+			// 		// 部分安卓手机在接电话时会停止推拉流报错,状态码5001,此时退出会议。 https://developers.weixin.qq.com/community/develop/doc/0006ac6d7a4968fa675a49fef53c00
+			// 		this.hangup()
+			// 		console.error('由于有电话接入,已退出会议')
+			// 	}
+			// },
+			netstatusChange(e){
+				console.log('>>>>>>>>>>net status:', e.detail)
+			},
+	
+			getTimer(){
+				let count = 0;
+				let time = '00:00:00'
+				this.timer = setInterval(() => {
+					count++;
+					let s = showNum(count % 60);
+					let m = showNum(parseInt((count / 60)) % 60)
+					let h = showNum(parseInt(count / 60 / 60))
+					time = `${h}:${m}:${s}`
+					this.setData({
+						time
+					})
+				}, 1000)
+	
+				function showNum(num) {
+					if (num < 10) {
+						return '0' + num
+					}
+					return num
+				}
+	
+			},
+			stopTimer(){
+				clearInterval(this.timer)
+			},
+			error(err){
+				console.log('EEEEEEEEEEEEE', err)
+			},
+			statechange(state){
+				console.log("SSSSSSSSSSS", state)
+			}
+		},
+		
+		mounted() {
+			console.log('进入')
+			wx.setKeepScreenOn({
+			  keepScreenOn: true
+			})
+			console.log('进入111')
+			this.setData({
+				myName: wx.WebIM.conn.context.userId
+			})
+			console.log('进入222')
+			this.getTimer()
+			var me = this
+			let subUrls = []
+			let obj = {};
+			console.log('进入333')
+			this.LivePusherContext = uni.createLivePusherContext('livePusher',this)
+			console.log('this.LivePusherContext ..', this.LivePusherContext)
+			if(this.action&&this.action.action == 'join'){
+				console.log('join')
+				// 音视频sdk提供两种创建、加入会议的api 使用任意一种都可以:(1) 一种是通过会议id ticket 或者 会议id 密码加入会议 如使用下面 joinConf
+				// (2)另一种是通过房间名 密码加入 如使用下面joinRoom
+
+				//this.joinConf(this.action) // (1)
+				//this.joinRoom(this.action) // (2)
+				if (this.action.roomName) {
+					console.log('使用joinroom')
+					this.joinRoom(this.action)
+				}else{
+					console.log('使用joinConf')
+					this.joinConf(this.action)
+				}
+			}else{
+				// 创建会议同样是两种api (1)一种是使用createConf 单纯创建一个会议,需要再申请ticket 或者用密码加入会议 如使用下面的createConf
+				// (2)也可以使用joinRoom,通过房间名、密码创建房间并直接加入 不需再进行加入会议的操作
+				this.joinRoom() // (1)
+				//this.createConf() // (2)
+			}
+			wx.emedia.mgr.onMediaChanaged = function(e){
+				console.log('onMediaChanaged', e)
+			}
+			wx.emedia.mgr.onConferenceExit = function(e){
+				console.log('onConferenceExit', e)
+			}
+			wx.emedia.mgr.onMemberExited = function(reason){
+				console.log('onMemberExited', reason)
+			};
+ 
+			wx.emedia.mgr.onStreamControl = function(mem){
+				console.log('onStreamControl', mem)
+			}
+
+			wx.emedia.mgr.onStreamControl.onSoundChanage = function(a, b, c, d){
+				console.log('onSoundChanage')
+			}
+
+			wx.emedia.mgr.onReconnect = function (res, ent){
+				// 发生断网重连,相当于重新加入会议
+
+				// 清空live-player 否则在原来的后面追加,导致原来的黑屏显示
+				subUrls = []
+
+				// 重新加入恢复到初始状态,防止和控制按钮状态不符
+
+				// 重连后摄像头方向不会改变
+				me.setData({
+					subUrls: [],
+					showInvite: true,
+					devicePosition: "front",
+					muted: false,
+					playVideoMuted: false,
+
+					
+					micphoneIcon: 'micphone_white',
+					micphoneColor: '#fff',
+					videoIcon: 'video_white',
+					beautyIcon: 'beauty',
+					videoColor: '#fff',
+					beauty: 9,
+					beautyColor: '#fff',
+					enableCamera: true
+				})
+			}
+
+			wx.emedia.mgr.onMemberJoined = function(mem){
+				console.log("++++++++++ member", mem)
+				var jid = wx.WebIM.conn.context.jid
+				let identityName = jid.appKey + '_' + jid.name+ '@' + jid.domain
+				// let identityName = wx.WebIM.conn.context.jid.split("/")[0]
+				// 如果是自己进入会议了,开始发布流
+				if(mem.name == identityName){
+					let rtcId = wx.emedia.util.getRtcId()
+					wx.emedia.mgr.pubStream(rtcId).then(function(res){
+						me.setData({
+							pubUrl: res.data.rtmp
+						})
+					})
+
+					var enableCamera = me.enableCamera;
+					console.warn("begin enable camera", me.enableCamera);
+
+					//默认enableCamera为false  关闭摄像头时声音不会有延迟,否则有延迟
+					//所以最好别用autopush
+					me.setData({
+						enableCamera: false,
+						pubUrl: me.url + 'record_type=audio' || 'https://domain/push_stream',
+					}, () => {
+						// var enableCameraDefault = true
+						//if(!enableCameraDefault){ //治疗不推流的毛病
+							// console.log('关闭摄像头推流')
+							// setTimeout(() => {
+							// 	me.LivePusherContext.start({
+							// 		success: function () {
+										// console.log('关闭摄像头推流', enableCamera)
+										enableCamera && me.setData({enableCamera: enableCamera})
+							// 		}
+							// 	})
+							// }, 1500)
+						// }else{
+						// 	me.LivePusherContext.start({
+						   //  	success: function () {
+						   //  		console.log('开始推流了', enableCamera)
+						   //    		enableCamera && me.setData({enableCamera: enableCamera})
+						   //  	}
+						   //  })
+						// }
+					})
+				}
+			}
+			wx.emedia.mgr.onStreamAdded = function(stream){
+				console.log('%c onAddStream', 'color: green', stream)
+				let streamId = stream.id
+				// setTimeout(() => {
+					if(subUrls.length > 8){
+						return
+					}
+					wx.emedia.mgr.subStream(streamId).then(function(data){
+						console.log('%c 订阅流成功', 'color:green', data)
+						// let playContext = wx.createLivePlayerContext(streamId, me)
+						let subUrl = {
+							streamId: streamId,
+							subUrl: data.data.rtmp,
+							memName: stream.memName.split("_")[1].split("@")[0],
+							// playContext: playContext
+						}
+						subUrls.push(subUrl)
+						console.log('%c subUrls 11 ....', "background:yellow")
+						console.log(subUrls)
+	
+						me.setData({
+							subUrls: subUrls,
+							showInvite: false
+						})
+					})
+				// }, 2000)
+
+			}
+			wx.emedia.mgr.onStreamRemoved = function(stream){
+				console.log('%c onRemoveStream', 'color: red', stream)
+				subUrls = subUrls.filter((item) => {
+					if(item.streamId != stream.id){
+						return item
+					}else{
+						console.log('%c ------', 'backgroukd:yellow')
+						console.log(item)
+						// item.playContext.stop({
+						// 	success: function(){
+						// 		console.log('关闭成功')
+						// 	},
+						// 	complete: function(){
+						// 		console.log('关闭成功')
+						// 	}
+						// })
+					}
+				})
+				obj[stream.id] = false
+				me.setData({
+					subUrls: subUrls,
+				})
+				console.log('subUrls', subUrls)
+			},
+			wx.emedia.mgr.onConfrAttrsUpdated = function(e){
+				console.log('onConfrAttrsUpdated: ', e)
+			}
+			
+		},
+	
+	}
+</script>
+
+

+ 45 - 0
chat/components/chat/pushStorage.js

@@ -0,0 +1,45 @@
+
+var WebIM = require("../../../hxChatSDK/utils/WebIM")["default"];
+export function pushStorageSave(data) {
+    // JSON.parse(uni.getStorageSync("pushStorageData")) ||
+    let currentLoginUser = WebIM.conn.context.userId
+    let newObj = uni.getStorageSync("pushStorageData") || {};
+    let newAry = newObj[currentLoginUser] || [];
+    const { userId, type } = data; 
+    if (!newAry.includes(userId) && type === "add") {      
+        newAry = newAry.concat(userId);
+    } else if (newAry.includes(userId)  && type === "remove") {
+        newAry = newAry.filter(item => item !== userId);
+    }
+    newObj[currentLoginUser] = newAry;
+    uni.setStorage({
+        key: 'pushStorageData',
+        data: newObj,
+        success: function (params) {
+            console.log('uni.setStorage>>>', uni.getStorageSync("pushStorageData"));
+        }
+    });
+};
+
+export function onGetSilentConfig(message) {
+    const currentLoginUser = WebIM.conn.context.userId;
+    const { from, to, type } = message;
+    let pushObj = uni.getStorageSync('pushStorageData') || {};
+    let pushAry = pushObj[currentLoginUser] || [];
+    const option = {
+        conversationId: type === "chat" ? from : to,
+        type: type === "chat" ? "singleChat" : "groupChat",
+    };
+    WebIM.conn.getSilentModeForConversation(option).then((res) => {
+        if (res.data.type === "NONE") {
+            if (!pushAry.includes(option.conversationId)) {
+                pushAry.push(option.conversationId)
+            }
+        }
+        pushObj.pushAry = pushAry;
+        uni.setStorage({
+            key: 'pushStorageData',
+            data: pushObj
+        });
+    });
+}

+ 29 - 0
chat/components/chat/swipedelete/swipedelete.css

@@ -0,0 +1,29 @@
+
+.swipedelete-wrapper {
+  transition: all .4s ease;
+  position: relative;
+}
+.swipedelete-btn {
+/*  position:absolute;
+  top:0;
+  right:-180rpx;
+  text-align:center;
+  background: #f00;
+  color:#fff;
+  width:160rpx;
+  height:100%;
+  display:flex;
+  justify-content:center;
+  align-items:center;*/
+  position:absolute;
+	right: -160rpx; 
+	top:0;
+	background-color:#D0021B;
+	width:160rpx;
+	height:100%;
+	text-align:center;
+	color: #fff;
+  display:flex;
+  align-items:center;
+  justify-content:center;
+}

+ 74 - 0
chat/components/chat/swipedelete/swipedelete.vue

@@ -0,0 +1,74 @@
+<template>
+<view class="swipedelete-wrapper" @touchmove="touchMoveHandler" @touchstart="touchStartHandler" :style="'transform:translateX(' + translateX + 'rpx)'">
+	<slot></slot>
+  <view class="swipedelete-btn" @tap="deleteItem">删除</view>
+</view>
+</template>
+
+<script>
+let startX = 0;
+
+export default {
+  data() {
+    return {
+      translateX: 0
+    };
+  },
+
+  components: {},
+  props: {},
+  methods: {
+    deleteItem: function (e) {
+      this.setData({
+        translateX: 0
+      });
+      this.$emit('deleteChatItem', {}, {
+        bubbles: true
+      });
+    },
+
+    /**
+     * 滑动删除事件-滑动开始
+     */
+    touchStartHandler: function (e) {
+      startX = e.touches[0].pageX;
+    },
+
+    /**
+     * 滑动删除事件-滑动
+     */
+    touchMoveHandler: function (e) {
+      let pageX = e.touches[0].pageX;
+      let moveX = pageX - startX;
+
+      if (Math.abs(moveX) < 80) {
+        return;
+      } // e.target.style.WebkitTransform = `translateX(${moveX}px)`
+
+
+      if (moveX > 0) {
+        // 右滑 隐藏删除
+        if (Math.abs(this.translateX) == 0) {
+          return;
+        } else {
+          this.setData({
+            translateX: 0
+          });
+        }
+      } else {
+        // 左滑 显示删除
+        if (Math.abs(this.translateX) >= 160) {
+          return;
+        } else {
+          this.setData({
+            translateX: -160
+          });
+        }
+      }
+    }
+  }
+};
+</script>
+<style>
+@import "./swipedelete.css";
+</style>

+ 234 - 0
chat/conversation/chat.css

@@ -0,0 +1,234 @@
+page{
+	width: 100%;
+	overflow: hidden;
+}
+.chat_title{
+	position: fixed;
+	background-color: #fff;
+	width: 100%;
+	z-index: 9;
+	top: 0;
+	height: 128rpx;
+}
+.chat_title text{
+	line-height: 96rpx;
+	font-size: 64rpx;
+	font-weight: 400;
+	margin-left: 32rpx;
+}
+
+.search,.search_input {
+	width: 100%;
+	height: 88rpx;
+	background-color: #f2f2f2;
+	display: flex;
+	align-items: center;
+	top: 0;
+	left: 0;
+}
+
+
+.search {
+	justify-content: space-around;
+}
+.search_input {
+	justify-content: space-around;	
+}
+.search view, .search_input view {
+	height: 64rpx;
+	line-height: 64rpx;
+	background-color: #fff;
+	border-radius: 17px;
+	text-align: center;
+	display: flex;
+	align-items: center;
+	margin: 0 32rpx;
+}
+.search view {
+	width: 100%;
+	justify-content: center;
+	align-items: center;
+	background-color: #fff;
+}
+.search image {
+	display: block;
+	width: 5%;
+	height: 50%;
+}
+.search_input view {
+	padding-left: 24rpx;
+	text-align: left;
+	flex: 1;
+	margin: 0 28rpx 0 32rpx;
+}
+.search icon,.search_input icon {
+	display: inline-block;
+	margin: 4rpx 12rpx 0;
+	font-size: 24rpx;
+}
+.search text {
+	font-size: 30rpx;
+	color: #9B9B9B;
+}
+
+.search_input text {
+	display: inline-block;
+	height: 60rpx;
+	line-height: 60rpx;
+	font-size: 30rpx;
+	color: #0873DE;
+	margin-right: 32rpx;
+}
+.search_input input {
+	font-size: 28rpx;
+	width: 90%;
+}
+.mask {
+	background-color: black;
+	opacity: 0.4;
+	position: fixed;
+	top: 80rpx;
+	left: 0;
+	right: 0;
+	bottom: 0;
+}
+.chat_list_wraper{
+	padding-bottom: 270rpx; 
+	box-sizing: border-box; 
+	height: 100%;
+	position: fixed;
+	overflow: hidden;
+}
+
+.chat_list {
+	width: 100%;
+	height: 128rpx;
+	overflow: hidden;
+}
+.tap_mask{
+	width: 100%;
+	overflow: hidden;
+}
+.list_box{
+	margin: 0 32rpx;
+	height: 126rpx;
+	border-bottom: 0.5px #E5E5E5 solid;
+	display: flex;
+}
+.list_left {
+	width: 63%;
+	height: 100%;
+	float: left;
+	display: flex;
+	flex-direction: row;
+	align-items: center;
+	flex: 1;
+}
+.list_text {
+	width: 60%;
+	height: 100%;
+	float: left;
+	font-size: 28rpx;
+}
+.list_pic {
+	margin-right: 20rpx;
+}
+.list_pic image {
+	display: block;
+	width: 88rpx;
+	height: 88rpx;
+}
+.list_user,.list_user2{
+	font-size: 34rpx;
+	color: #000;
+	position: relative;
+	top: 22rpx;
+	overflow: hidden;
+	width: 350rpx;
+	display: inline-block;
+	text-overflow: ellipsis;
+}
+.list_user2{
+	top: 40rpx !important;
+}
+.em-msgNum {
+	position: absolute;
+	left: 84rpx;
+	top: 20rpx;
+	min-width: 28rpx;
+	border-radius: 14rpx;
+	background: #f04134;
+	color: #fff;
+	line-height: 28rpx;
+	font-size: 25rpx;
+	text-align: center;
+	padding: 3rpx;
+	z-index: 999;
+}
+.list_word {
+	height: 40rpx;
+	display: block;
+	margin-top: 0;
+	overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    position: absolute;
+    width: 70%;
+    font-size: 24rpx;
+    color: #9B9B9B;
+    top: 66rpx;
+}
+.list_right {
+	width: 240rpx;
+	height: 100%;
+	float: right;
+	font-size: 24rpx;
+	text-align: right;
+}
+.list_right text:first-child {
+	display: block;
+	line-height: 60rpx;
+	margin: 12rpx auto auto auto;
+}
+.list_right text:last-child {
+	height: 40rpx; 
+	font-size: 26rpx;
+	color: #CFCFCF;
+	display: block;
+	margin-top: 4rpx;
+}
+
+.chat_noChat{
+	text-align: center;
+	font-size: 30rpx;
+	color: #9B9B9B;
+	margin-top: 400rpx;
+}
+.ctbg{
+	width: 138px;
+    height: 106px;
+    display: block;
+    margin: 20rpx auto;
+}
+/* .main_title_hide{
+	top: -128rpx;
+	transition: top 0.5s;
+}
+.main_title_show{
+	top: 0;
+	transition: top 0.5s;
+} */
+/* .goTop{
+	top: 0rpx;
+	transition: top 0.5s;
+	padding-bottom: 104rpx!important;
+}
+.goTopX{
+	top: 0rpx;
+	transition: top 0.5s;
+	padding-bottom: 140rpx!important;
+}
+.goback{
+	top: 128rpx;
+	transition: top 0.5s;
+} */

+ 666 - 0
chat/conversation/conversation.vue

@@ -0,0 +1,666 @@
+<template>
+	<view>
+		<view>
+			<view class="search" v-if="search_btn">
+				<view @tap="openSearch">
+					<icon type="search" size="12"></icon>
+					<text>搜索</text>
+				</view>
+			</view>
+		</view>
+
+		<!-- <view class="chat_list_wraper" > -->
+		<scroll-view scroll-y="true" :class="
+      'chat_list_wraper ' + (gotop ? (isIPX ? 'goTopX' : 'goTop') : 'goback')
+    " :style="'padding-bottom: ' + (isIPX ? '270rpx' : '226rpx')">
+			<view class="search_input" v-if="search_chats">
+				<view>
+					<icon type="search" size="12"></icon>
+					<input placeholder="搜索" placeholder-style="color:#9B9B9B;line-height:21px;font-size:15px;"
+						auto-focus confirm-type="search" type="text" @confirm="onSearch" @input="onInput"
+						v-model="input_code" />
+					<icon type="clear" size="12" @tap.stop="clearInput" v-if="show_clear"></icon>
+				</view>
+				<text @tap="cancel">取消</text>
+			</view>
+			<view v-for="(item, index) in conversationList" :key="index" class="chat_list" :data-item="item"
+				@tap.stop="del_chat" @longpress="longpress">
+				<swipe-delete>
+					<!-- 通知模块 -->
+					<!-- <view class="tap_mask" @tap.stop="into_inform" :data-item="item" v-if="item.chatType == 'INFORM'">
+						<view class="list_box">
+							<view class="list_left">
+								<view class="list_pic">
+									<view v-if="unReadTotalNotNum > 0" class="em-unread-spot2">
+										{{ unReadTotalNotNum}}
+									</view>
+									<image :class="unReadTotalNotNum > 0 ? 'haveSpot' : ''" src="../static/inform.png">
+									</image>
+								</view>
+								<view class="list_text">
+									<text class="list_user"> 系统通知 </text>
+									<text class="list_word"
+										v-if="item.chatType == 'INFORM'">申请通知来自:{{ item.info.from }}</text>
+								</view>
+							</view>
+							<view class="list_right">
+								<text :data-username="item.username">{{ handleTime(item) }}</text>
+							</view>
+						</view>
+					</view> -->
+
+					<view class="tap_mask" @tap.stop="into_chatRoom" :data-item="JSON.stringify(item)" v-else>
+						<!-- 消息列表 -->
+						<view class="list_box">
+							<view class="list_left" :data-username="item.username">
+								<view class="list_pic">
+									<view class="em-msgNum"
+										v-if="item.unReadCount > 0  && !pushConfigData.includes(item.chatType === 'chat' ? item.username : item.info.to)">
+										{{ item.unReadCount > 99 ? '99+':item.unReadCount}}
+									</view>
+
+									<image :src="showConversationAvatar(item)"></image>
+								</view>
+								<view class="list_text">
+									<text class="list_user">{{ showConversationName(item) }}</text>
+									<text class="list_word" v-if="item.msg.data[0].data">
+										{{item.msg.data[0].data}}
+									</text>
+									<text class="list_word" v-if="item.msg.type == 'img'">[图片]</text>
+									<text class="list_word" v-if="item.msg.type == 'audio'">[语音]</text>
+									<text class="list_word" v-if="item.msg.type == 'file'">[附件]</text>
+									<text class="list_word" v-if="item.msg.type == 'video'">[视频]</text>
+								</view>
+							</view>
+							<view class="list_right">
+								<text :data-username="item.username">{{ handleTime(item) }}</text>
+							</view>
+						</view>
+					</view>
+				</swipe-delete>
+			</view>
+
+			<long-press-modal :winSize="winSize" :popButton="popButton" @change="pickerMenuChange" :showPop="showPop"
+				@hidePop="hidePop" :popStyle="popStyle" />
+			<view v-if="conversationList && conversationList.length == 0" class="chat_noChat">
+				<image class="ctbg" src="../static/ctbg.png"></image>
+				暂无聊天消息
+			</view>
+			<!-- </view> -->
+		</scroll-view>
+		<!-- bug: margin-bottom 不生效 需要加一个空标签-->
+		<view style="height: 1px"></view>
+		<view class="mask" @tap="close_mask" v-if="show_mask"></view>
+	</view>
+</template>
+
+<script>
+	let disp = require("../../hxChatSDK/utils/broadcast");
+	var WebIM = require("../../hxChatSDK/utils/WebIM")["default"];
+	let isfirstTime = true;
+	import swipeDelete from "../../chat/components/chat/swipedelete/swipedelete";
+	import longPressModal from "../../chat/components/chat/longPressModal/index";
+	export default {
+		data() {
+			return {
+				search_btn: true,
+				search_chats: false,
+				show_mask: false,
+				yourname: "",
+				unReadSpotNum: 0,
+				unReadNoticeNum: 0,
+				messageNum: 0,
+				unReadTotalNotNum: 0,
+				conversationList: [],
+				show_clear: false,
+				member: "",
+				isIPX: false,
+				gotop: false,
+				input_code: "",
+				groupName: {},
+				winSize: {},
+				popButton: ["删除该聊天"],
+				showPop: false,
+				popStyle: "",
+				currentVal: '',
+				pushConfigData: [],
+				defaultAvatar: "../static/theme2x.png",
+				defaultGroupAvatar: "../static/groupTheme.png"
+			};
+		},
+
+		components: {
+			swipeDelete,
+			longPressModal,
+		},
+		props: {},
+
+		onLoad() {
+			this.getWindowSize();
+			//监听加好友申请
+			disp.on("em.subscribe", this.onChatPageSubscribe);
+			//监听解散群
+			disp.on("em.invite.deleteGroup", this.onChatPageDeleteGroup);
+			//监听未读消息数
+			disp.on("em.unreadspot", this.onChatPageUnreadspot);
+			//监听未读加群“通知”
+			disp.on("em.invite.joingroup", this.onChatPageJoingroup);
+			//监听好友删除
+			disp.on("em.contacts.remove", this.onChatPageRemoveContacts);
+			//监听好友关系解除
+			disp.on("em.unsubscribed", this.onChatPageUnsubscribed)
+			if (!uni.getStorageSync('listGroup')) {
+				this.listGroups()
+			}
+			if (!uni.getStorageSync('member')) {
+				this.getRoster()
+			}
+			this.readJoinedGroupName()
+		},
+
+		onShow: function() {
+			uni.hideHomeButton && uni.hideHomeButton();
+			setTimeout(() => {
+				this.getLocalConversationlist();
+			}, 100)
+			this.setData({
+				unReadSpotNum: getApp().globalData.unReadMessageNum > 99 ?
+					"99+" : getApp().globalData.unReadMessageNum,
+				messageNum: getApp().globalData.saveFriendList.length,
+				unReadNoticeNum: getApp().globalData.saveGroupInvitedList.length,
+				unReadTotalNotNum: getApp().globalData.saveFriendList.length +
+					getApp().globalData.saveGroupInvitedList.length,
+			});
+
+			if (getApp().globalData.isIPX) {
+				this.setData({
+					isIPX: true,
+				});
+			}
+		},
+		onUnload() {
+			//页面卸载同步取消onload中的订阅,防止重复订阅事件。
+			disp.off('em.subscribe', this.onChatPageSubscribe)
+			disp.off("em.invite.deleteGroup", this.onChatPageDeleteGroup)
+			disp.off("em.unreadspot", this.onChatPageUnreadspot)
+			disp.off("em.invite.joingroup", this.onChatPageJoingroup)
+			disp.off("em.contacts.remove", this.onChatPageRemoveContacts)
+			disp.off("em.unsubscribed", this.onChatPageUnsubscribed)
+		},
+		computed: {
+			//会话头像展示
+			showConversationAvatar() {
+				const friendUserInfoMap = getApp().globalData.friendUserInfoMap;
+				return (item) => {
+					if (item.chatType === 'singleChat' || item.chatType === 'chat') {
+						if (friendUserInfoMap.has(item.username) && friendUserInfoMap.get(item.username)?.avatarurl) {
+							return friendUserInfoMap.get(item.username).avatarurl
+						} else {
+							return this.defaultAvatar
+						}
+					} else if (item.chatType === 'groupchat' || item.chatType === 'chatRoom') {
+						return this.defaultGroupAvatar
+					}
+				}
+			},
+			//会话name展示
+			showConversationName() {
+				const friendUserInfoMap = getApp().globalData.friendUserInfoMap;
+				return (item) => {
+					if (item.chatType === 'singleChat' || item.chatType === 'chat') {
+						if (friendUserInfoMap.has(item.username) && friendUserInfoMap.get(item.username)?.nickname) {
+							return friendUserInfoMap.get(item.username).nickname
+						} else {
+							return item.username
+						}
+					} else if (item.chatType === 'groupchat' || item.chatType === 'chatRoom') {
+						return item.groupName
+					}
+				}
+			},
+			//处理时间显示
+			handleTime() {
+				return (item) => {
+					return this.$u.timeFormat(item.time, 'mm/dd/hh:MM')
+				}
+			}
+		},
+		methods: {
+			listGroups() {
+				var me = this;
+				return WebIM.conn.getGroup({
+					limit: 50,
+					success: function(res) {
+						uni.setStorage({
+							key: "listGroup",
+							data: res.data,
+						});
+						me.readJoinedGroupName()
+						me.getLocalConversationlist();
+					},
+					error: function(err) {
+						console.log(err);
+					},
+				});
+			},
+			getRoster() {
+				let me = this;
+				let rosters = {
+					success(roster) {
+						console.log('roster', roster)
+						var member = [];
+						for (let i = 0; i < roster.length; i++) {
+							if (roster[i].subscription == "both") {
+								member.push(roster[i]);
+							}
+						}
+						uni.setStorage({
+							key: "member",
+							data: member,
+						});
+						me.setData({
+							member: member
+						});
+						//if(!systemReady){
+						disp.fire("em.main.ready");
+						//systemReady = true;
+						//}
+						me.getLocalConversationlist()
+						me.setData({
+							unReadSpotNum: getApp().globalData.unReadMessageNum > 99 ?
+								"99+" : getApp().globalData.unReadMessageNum,
+						});
+					},
+					error(err) {
+						console.log(err);
+					},
+				};
+				WebIM.conn.getContacts(rosters);
+			},
+			readJoinedGroupName() {
+				const joinedGroupList = uni.getStorageSync('listGroup')
+				const groupList = joinedGroupList?.data || joinedGroupList || []
+				let groupName = {};
+				groupList.forEach((item) => {
+					groupName[item.groupid] = item.groupname;
+				});
+				this.setData({
+					groupName: groupName,
+				});
+			},
+
+			// 包含陌生人版本
+			getLocalConversationlist() {
+				const myName = uni.getStorageSync("myUsername");
+				const me = this;
+				uni.getStorageInfo({
+					success: function(res) {
+						let storageKeys = res.keys;
+						let newChatMsgKeys = [];
+						let historyChatMsgKeys = [];
+						let len = myName.length;
+						storageKeys.forEach((item) => {
+							if (item.slice(-len) == myName && item.indexOf("rendered_") == -1) {
+								newChatMsgKeys.push(item);
+							} else if (
+								item.slice(-len) == myName &&
+								item.indexOf("rendered_") > -1
+							) {
+								historyChatMsgKeys.push(item);
+							} else if (item === "INFORM") {
+								newChatMsgKeys.push(item);
+							}
+						});
+						me.packageConversation(newChatMsgKeys, historyChatMsgKeys);
+					},
+				});
+			},
+			//组件会话列表方法
+			packageConversation(newChatMsgKeys, historyChatMsgKeys) {
+				const me = this;
+				const myName = uni.getStorageSync("myUsername");
+				let conversationList = [];
+				let lastChatMsg; //最后一条消息
+				for (let i = historyChatMsgKeys.length; i > 0, i--;) {
+					let index = newChatMsgKeys.indexOf(historyChatMsgKeys[i].slice(9));
+					if (index > -1) {
+						let newChatMsgs = uni.getStorageSync(newChatMsgKeys[index]) || [];
+						if (newChatMsgs.length) {
+							lastChatMsg = newChatMsgs[newChatMsgs.length - 1];
+							lastChatMsg.unReadCount = newChatMsgs.length;
+							newChatMsgKeys.splice(index, 1);
+						} else {
+							let historyChatMsgs = uni.getStorageSync(historyChatMsgKeys[i]);
+							if (historyChatMsgs.length) {
+								lastChatMsg = historyChatMsgs[historyChatMsgs.length - 1];
+							}
+						}
+					} else {
+						let historyChatMsgs = uni.getStorageSync(historyChatMsgKeys[i]);
+						if (historyChatMsgs.length) {
+							lastChatMsg = historyChatMsgs[historyChatMsgs.length - 1];
+						}
+					}
+					if (
+						lastChatMsg &&
+						(lastChatMsg.chatType == "groupchat" ||
+							lastChatMsg.chatType == "chatRoom")
+					) {
+						lastChatMsg.groupName = me.groupName[lastChatMsg.info.to];
+					}
+					lastChatMsg && lastChatMsg.username != myName &&
+						conversationList.push(lastChatMsg);
+				}
+				for (let i = newChatMsgKeys.length; i > 0, i--;) {
+					let newChatMsgs = uni.getStorageSync(newChatMsgKeys[i]) || [];
+					if (newChatMsgs.length) {
+						lastChatMsg = newChatMsgs[newChatMsgs.length - 1];
+						lastChatMsg.unReadCount = newChatMsgs.length;
+						if (
+							lastChatMsg.chatType == "groupchat" ||
+							lastChatMsg.chatType == "chatRoom"
+						) {
+							lastChatMsg.groupName = me.groupName[lastChatMsg.info.to];
+						}
+						lastChatMsg.username != myName && conversationList.push(lastChatMsg);
+					}
+				}
+				conversationList.sort((a, b) => {
+					return b.time - a.time;
+				});
+				this.setData({
+					conversationList: conversationList,
+				});
+			},
+			openSearch: function() {
+				this.setData({
+					search_btn: false,
+					search_chats: true,
+					gotop: true,
+				});
+			},
+
+			onSearch: function(val) {
+				let searchValue = val.detail.value;
+				var myName = uni.getStorageSync("myUsername");
+				const me = this;
+				let serchList = [];
+				let conversationList = [];
+				uni.getStorageInfo({
+					success: function(res) {
+						let storageKeys = res.keys;
+						let chatKeys = [];
+						let len = myName.length;
+						storageKeys.forEach((item) => {
+							if (item.slice(-len) == myName) {
+								chatKeys.push(item);
+							}
+						});
+						chatKeys.forEach((item, index) => {
+							if (item.indexOf(searchValue) != -1) {
+								serchList.push(item);
+							}
+						});
+						let lastChatMsg = "";
+						serchList.forEach((item, index) => {
+							let chatMsgs = uni.getStorageSync(item) || [];
+							if (chatMsgs.length) {
+								lastChatMsg = chatMsgs[chatMsgs.length - 1];
+								conversationList.push(lastChatMsg);
+							}
+						});
+						me.setData({
+							conversationList: conversationList
+						});
+					},
+				});
+			},
+
+			cancel: function() {
+				this.getLocalConversationlist();
+				this.setData({
+					search_btn: true,
+					search_chats: false,
+					unReadSpotNum: getApp().globalData.unReadMessageNum > 99 ?
+						"99+" : getApp().globalData.unReadMessageNum,
+					gotop: false,
+				});
+			},
+			clearInput: function() {
+				this.setData({
+					input_code: "",
+					show_clear: false,
+				});
+			},
+			onInput: function(e) {
+				let inputValue = e.detail.value;
+				if (inputValue) {
+					this.setData({
+						show_clear: true,
+					});
+				} else {
+					this.setData({
+						show_clear: false,
+					});
+				}
+			},
+
+			close_mask: function() {
+				this.setData({
+					search_btn: true,
+					search_chats: false,
+					show_mask: false,
+				});
+			},
+
+			into_chatRoom: function(event) {
+				let detail = JSON.parse(event.currentTarget.dataset.item);
+				if (
+					detail.chatType == "groupchat" ||
+					detail.chatType == "chatRoom" ||
+					detail.groupName
+				) {
+					this.into_groupChatRoom(detail);
+				} else {
+					this.into_singleChatRoom(detail);
+				}
+			},
+			// 单聊
+			into_singleChatRoom: function(detail) {
+				var my = uni.getStorageSync("myUsername");
+				var nameList = {
+					myName: my,
+					your: detail.username,
+				};
+				const friendUserInfoMap = getApp().globalData.friendUserInfoMap;
+				if (friendUserInfoMap.has(nameList.your) && friendUserInfoMap.get(nameList.your)?.nickname) {
+					nameList.yourNickName = friendUserInfoMap.get(nameList.your).nickname;
+				}
+				uni.navigateTo({
+					url: "../chatroom/chatroom?username=" + JSON.stringify(nameList),
+				});
+			},
+			// 群聊 和 聊天室 (两个概念)
+			into_groupChatRoom: function(detail) {
+				var my = uni.getStorageSync("myUsername");
+				var nameList = {
+					myName: my,
+					your: detail.groupName,
+					groupId: detail.info.to,
+				};
+				uni.navigateTo({
+					url: "../groupChatRoom/groupChatRoom?username=" + JSON.stringify(nameList),
+				});
+			},
+
+			// into_inform: function() {
+			// 	uni.redirectTo({
+			// 		url: "../notification/notification",
+			// 	});
+			// },
+
+			removeAndRefresh: function(event) {
+				let removeId = event.currentTarget.dataset.item.info.from
+				let ary = getApp().globalData.saveFriendList
+				let idx
+				if (ary.length > 0) {
+					ary.forEach((v, k) => {
+						if (v.from == removeId) {
+							idx = k
+						}
+					})
+					getApp().globalData.saveFriendList.splice(idx, 1);
+				}
+				uni.removeStorageSync('INFORM')
+			},
+
+			del_chat: function(event) {
+				let detail = event.currentTarget.dataset.item;
+				let nameList = {};
+				let me = this;
+				// 删除当前选中群组聊天列表
+				if (detail.chatType == "groupchat" || detail.chatType == "chatRoom") {
+					nameList = {
+						your: detail.info.to,
+					};
+					//删除当前选中通知列表
+				} else if (detail.chatType === "INFORM") {
+					nameList = {
+						your: "INFORM",
+					};
+				}
+				//删除当前选中好友聊天列表
+				else {
+					nameList = {
+						your: detail.username,
+					};
+				}
+				var myName = uni.getStorageSync("myUsername");
+				var currentPage = getCurrentPages();
+
+				uni.showModal({
+					title: "确认删除?",
+					confirmText: "删除",
+					success: function(res) {
+						if (res.confirm) {
+							uni.removeStorageSync(nameList.your + myName);
+							uni.removeStorageSync("rendered_" + nameList.your + myName);
+							nameList.your === 'INFORM' && me.removeAndRefresh(event);
+							// if (Object.keys(currentPage[0]).length>0) {
+							//   currentPage[0].onShow();
+							// }
+							disp.fire("em.chat.session.remove");
+							me.getLocalConversationlist();
+						}
+					},
+					fail: function(err) {
+						console.log('删除列表', err);
+					},
+				});
+			},
+			removeLocalStorage: function(yourname) {
+				var myName = uni.getStorageSync("myUsername");
+				uni.removeStorageSync(yourname + myName);
+				uni.removeStorageSync("rendered_" + yourname + myName);
+			},
+			longpress: function(e) {
+				//将当前选中的值存在data中方便后续操作
+				this.currentVal = e
+				let [touches, style, index] = [e.touches[0], "", e.currentTarget.dataset.index, ];
+
+				/* 因 非H5端不兼容 style 属性绑定 Object ,所以拼接字符 */
+				if (touches.clientY > this.winSize.height / 2) {
+					style = `bottom:${this.winSize.height - touches.clientY}px;`;
+				} else {
+					style = `top:${touches.clientY}px;`;
+				}
+				if (touches.clientX > this.winSize.witdh / 2) {
+					style += `right:${this.winSize.witdh - touches.clientX}px`;
+				} else {
+					style += `left:${touches.clientX}px`;
+				}
+
+				this.popStyle = style;
+				// this.pickerUserIndex = Number(index);
+				this.showShade = true;
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.showPop = true;
+					}, 10);
+				});
+			},
+			/* 获取窗口尺寸 */
+			getWindowSize() {
+				uni.getSystemInfo({
+					success: (res) => {
+						this.winSize = {
+							witdh: res.windowWidth,
+							height: res.windowHeight,
+						};
+					},
+				});
+			},
+			hidePop() {
+				this.showPop = false;
+			},
+			pickerMenuChange() {
+				this.del_chat(this.currentVal)
+			},
+			/*  disp event callback function */
+			onChatPageSubscribe() {
+				this.getLocalConversationlist();
+				this.setData({
+					messageNum: getApp().globalData.saveFriendList.length,
+					unReadTotalNotNum: getApp().globalData.saveFriendList.length +
+						getApp().globalData.saveGroupInvitedList.length,
+				});
+			},
+			onChatPageDeleteGroup(infos) {
+				this.listGroups();
+				this.getRoster();
+				this.getLocalConversationlist();
+				this.setData({
+					messageNum: getApp().globalData.saveFriendList.length,
+				});
+				//如果会话存在则执行删除会话
+				this.removeLocalStorage(infos.gid)
+			},
+			onChatPageUnreadspot(message) {
+				this.getLocalConversationlist();
+				let currentLoginUser = WebIM.conn.context.userId;
+				let id = message && message.chatType === 'groupchat' ? message?.to : message?.from;
+				let pushObj = uni.getStorageSync("pushStorageData");
+				let pushAry = pushObj[currentLoginUser] || []
+				this.setData({
+					pushConfigData: pushAry,
+				});
+				// if (message && pushValue.includes(id)) return
+				this.setData({
+					unReadSpotNum: getApp().globalData.unReadMessageNum > 99 ?
+						"99+" : getApp().globalData.unReadMessageNum,
+				});
+			},
+			onChatPageJoingroup() {
+				this.setData({
+					unReadNoticeNum: getApp().globalData.saveGroupInvitedList.length,
+					unReadTotalNotNum: getApp().globalData.saveFriendList.length +
+						getApp().globalData.saveGroupInvitedList.length,
+				});
+				this.getLocalConversationlist();
+			},
+			onChatPageRemoveContacts() {
+				this.getLocalConversationlist();
+				this.getRoster();
+			},
+			onChatPageUnsubscribed(message) {
+				uni.showToast({
+					title: `与${message.from}好友关系解除`,
+					icon: "none",
+				});
+			}
+		},
+	};
+</script>
+<style>
+	@import "./chat.css";
+</style>

BIN
chat/static/Emoji.png


BIN
chat/static/ad.png


BIN
chat/static/camora.png


BIN
chat/static/ctbg.png


BIN
chat/static/faces/btn_del.png


BIN
chat/static/faces/del.png


BIN
chat/static/faces/ee_1.png


BIN
chat/static/faces/ee_10.png


BIN
chat/static/faces/ee_11.png


BIN
chat/static/faces/ee_12.png


BIN
chat/static/faces/ee_13.png


BIN
chat/static/faces/ee_14.png


BIN
chat/static/faces/ee_15.png


BIN
chat/static/faces/ee_16.png


BIN
chat/static/faces/ee_17.png


BIN
chat/static/faces/ee_18.png


BIN
chat/static/faces/ee_19.png


BIN
chat/static/faces/ee_2.png


BIN
chat/static/faces/ee_20.png


BIN
chat/static/faces/ee_21.png


BIN
chat/static/faces/ee_22.png


BIN
chat/static/faces/ee_23.png


BIN
chat/static/faces/ee_24.png


BIN
chat/static/faces/ee_25.png


BIN
chat/static/faces/ee_26.png


BIN
chat/static/faces/ee_27.png


BIN
chat/static/faces/ee_28.png


BIN
chat/static/faces/ee_29.png


BIN
chat/static/faces/ee_3.png


BIN
chat/static/faces/ee_30.png


BIN
chat/static/faces/ee_31.png


BIN
chat/static/faces/ee_32.png


BIN
chat/static/faces/ee_33.png


BIN
chat/static/faces/ee_34.png


BIN
chat/static/faces/ee_35.png


BIN
chat/static/faces/ee_4.png


BIN
chat/static/faces/ee_5.png


BIN
chat/static/faces/ee_6.png


BIN
chat/static/faces/ee_7.png


BIN
chat/static/faces/ee_8.png


BIN
chat/static/faces/ee_9.png


BIN
chat/static/file.png


BIN
chat/static/groupTheme.png


BIN
chat/static/inform.png


BIN
chat/static/msgFile.png


BIN
chat/static/msgerr.png


BIN
chat/static/pic.png


BIN
chat/static/popleftarrow2x.png


BIN
chat/static/poprightarrow2x.png


BIN
chat/static/send.png


BIN
chat/static/theme2x.png


BIN
chat/static/voice.png


BIN
chat/static/voicemsg.png


BIN
chat/static/voicemsgmy.png


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
hxChatSDK/Easemob-chat-4.1.7.js


+ 37 - 0
hxChatSDK/utils/Dispatcher.js

@@ -0,0 +1,37 @@
+var dispCbs = [];
+var dispIns = [];
+
+function Dispatcher() {
+  dispIns.push(this);
+  dispCbs.push({});
+}
+
+Dispatcher.prototype = {
+  on(type, cb) {
+    let cbtypes = dispCbs[dispIns.indexOf(this)];
+    let cbs = cbtypes[type] = cbtypes[type] || [];
+    if (!~cbs.indexOf(cb)) {
+		cbs.push(cb);
+    }
+  },
+
+  off(type, cb) {
+    let cbtypes = dispCbs[dispIns.indexOf(this)];
+    let cbs = cbtypes[type] = cbtypes[type] || [];
+    let curTypeCbIdx = cbs.indexOf(cb);
+    if (~curTypeCbIdx) {
+      cbs.splice(curTypeCbIdx, 1);
+    }
+  },
+
+  fire(type, ...args) {
+    let cbtypes = dispCbs[dispIns.indexOf(this)];
+    let cbs = cbtypes[type] = cbtypes[type] || [];
+
+    for (let i = 0; i < cbs.length; i++) {
+      cbs[i].apply(null, args);
+    }
+  }
+
+};
+module.exports = Dispatcher;

+ 58 - 0
hxChatSDK/utils/Observe.js

@@ -0,0 +1,58 @@
+var obsCbs = obsCbs || [];
+var obsObjs = obsObjs || [];
+var cloneObjs = cloneObjs || [];
+
+function newOne(obj) {
+  obsObjs.push(obj);
+  obsCbs.push([]);
+  cloneObjs.push(Object.assign({}, obj));
+}
+
+module.exports = {
+  del(obj, cb) {
+    let curObjIdx = obsObjs.indexOf(obj);
+
+    if (~curObjIdx) {
+      let cbs = obsCbs[curObjIdx];
+      let curCbIdx = cbs.indexOf(cb);
+
+      if (~curCbIdx) {
+        cbs.splice(curCbIdx, 1);
+
+        if (!cbs.length) {
+          obsObjs.splice(curObjIdx, 1);
+        }
+      }
+    }
+  },
+
+  add(obj, cb) {
+    let curIdx = obsObjs.indexOf(obj);
+
+    if (!~curIdx) {
+      curIdx = obsObjs.length;
+      newOne(obj);
+    }
+
+    let cbs = obsCbs[curIdx];
+    cbs.push(cb);
+
+    for (let key in obj) {
+      Object.defineProperty(obj, key, {
+        set: function (val) {
+          cloneObjs[curIdx][key] = val;
+
+          for (let i = 0; i < cbs.length; i++) {
+            cbs[i].apply(obj, [val, key]);
+          }
+        },
+        get: function () {
+          return cloneObjs[curIdx][key];
+        }
+      });
+    }
+
+    return obj;
+  }
+
+};

+ 194 - 0
hxChatSDK/utils/WebIM.js

@@ -0,0 +1,194 @@
+import websdk from "../../hxChatSDK/Easemob-chat-4.1.7";
+import config from "./WebIMConfig";
+console.group = console.group || {};
+console.groupEnd = console.groupEnd || {};
+var window = {};
+let WebIM = window.WebIM = uni.WebIM = websdk;
+window.WebIM.config = config; 
+
+WebIM.isDebug = function (option) {
+  if (option) {
+    WebIM.config.isDebug = option.isDebug;
+    openDebug(WebIM.config.isDebug);
+  }
+
+  function openDebug(value) {
+    function ts() {
+      var d = new Date();
+      var Hours = d.getHours(); // 获取当前小时数(0-23)
+
+      var Minutes = d.getMinutes(); // 获取当前分钟数(0-59)
+
+      var Seconds = d.getSeconds(); // 获取当前秒数(0-59)
+
+      return (Hours < 10 ? "0" + Hours : Hours) + ":" + (Minutes < 10 ? "0" + Minutes : Minutes) + ":" + (Seconds < 10 ? "0" + Seconds : Seconds) + " ";
+    }
+  }
+};
+/**
+ * Set autoSignIn as true (autoSignInName and autoSignInPwd are configured below),
+ * You can auto signed in each time when you refresh the page in dev model.
+ */
+
+
+WebIM.parseEmoji = function (msg) {
+  if (typeof WebIM.Emoji === "undefined" || typeof WebIM.Emoji.map === "undefined") {
+    return msg;
+  }
+
+  var emoji = WebIM.Emoji,
+      reg = null;
+  var msgList = [];
+  var objList = [];
+
+  for (var face in emoji.map) {
+    if (emoji.map.hasOwnProperty(face)) {
+      while (msg.indexOf(face) > -1) {
+        msg = msg.replace(face, "^" + emoji.map[face] + "^");
+      }
+    }
+  }
+
+  var ary = msg.split("^");
+  var reg = /^e.*g$/;
+
+  for (var i = 0; i < ary.length; i++) {
+    if (ary[i] != "") {
+      msgList.push(ary[i]);
+    }
+  }
+
+  for (var i = 0; i < msgList.length; i++) {
+    if (reg.test(msgList[i])) {
+      var obj = {};
+      obj.data = msgList[i];
+      obj.type = "emoji";
+      objList.push(obj);
+    } else {
+      var obj = {};
+      obj.data = msgList[i];
+      obj.type = "txt";
+      objList.push(obj);
+    }
+  }
+
+  return objList;
+};
+
+WebIM.time = function () {
+  var date = new Date();
+  var Hours = date.getHours();
+  var Minutes = date.getMinutes();
+  var Seconds = date.getSeconds();
+  var time = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + (Hours < 10 ? "0" + Hours : Hours) + ":" + (Minutes < 10 ? "0" + Minutes : Minutes) + ":" + (Seconds < 10 ? "0" + Seconds : Seconds);
+  return time;
+};
+
+WebIM.Emoji = {
+  path: "../static/faces/",
+  map: {
+    "[):]": "ee_1.png",
+    "[:D]": "ee_2.png",
+    "[;)]": "ee_3.png",
+    "[:-o]": "ee_4.png",
+    "[:p]": "ee_5.png",
+    "[(H)]": "ee_6.png",
+    "[:@]": "ee_7.png",
+    "[:s]": "ee_8.png",
+    "[:$]": "ee_9.png",
+    "[:(]": "ee_10.png",
+    "[:'(]": "ee_11.png",
+    "[<o)]": "ee_12.png",
+    "[(a)]": "ee_13.png",
+    "[8o|]": "ee_14.png",
+    "[8-|]": "ee_15.png",
+    "[+o(]": "ee_16.png",
+    "[|-)]": "ee_17.png",
+    "[:|]": "ee_18.png",
+    "[*-)]": "ee_19.png",
+    "[:-#]": "ee_20.png",
+    "[^o)]": "ee_21.png",
+    "[:-*]": "ee_22.png",
+    "[8-)]": "ee_23.png",
+    "[del]": "btn_del.png",
+    "[(|)]": "ee_24.png",
+    "[(u)]": "ee_25.png",
+    "[(S)]": "ee_26.png",
+    "[(*)]": "ee_27.png",
+    "[(#)]": "ee_28.png",
+    "[(R)]": "ee_29.png",
+    "[({)]": "ee_30.png",
+    "[(})]": "ee_31.png",
+    "[(k)]": "ee_32.png",
+    "[(F)]": "ee_33.png",
+    "[(W)]": "ee_34.png",
+    "[(D)]": "ee_35.png"
+  }
+};
+WebIM.EmojiObj = {
+  // 相对 emoji.js 路径
+  path: "../static/faces/",
+  map1: {
+    "[):]": "ee_1.png",
+    "[:D]": "ee_2.png",
+    "[;)]": "ee_3.png",
+    "[:-o]": "ee_4.png",
+    "[:p]": "ee_5.png",
+    "[(H)]": "ee_6.png",
+    "[:@]": "ee_7.png"
+  },
+  map2: {
+    "[:s]": "ee_8.png",
+    "[:$]": "ee_9.png",
+    "[:(]": "ee_10.png",
+    "[:'(]": "ee_11.png",
+    "[<o)]": "ee_12.png",
+    "[(a)]": "ee_13.png",
+    "[8o|]": "ee_14.png"
+  },
+  map3: {
+    "[8-|]": "ee_15.png",
+    "[+o(]": "ee_16.png",
+    "[|-)]": "ee_17.png",
+    "[:|]": "ee_18.png",
+    "[*-)]": "ee_19.png",
+    "[:-#]": "ee_20.png",
+    "[del]": "del.png"
+  },
+  map4: {
+    "[^o)]": "ee_21.png",
+    "[:-*]": "ee_22.png",
+    "[8-)]": "ee_23.png",
+    "[(|)]": "ee_24.png",
+    "[(u)]": "ee_25.png",
+    "[(S)]": "ee_26.png",
+    "[(*)]": "ee_27.png"
+  },
+  map5: {
+    "[(#)]": "ee_28.png",
+    "[(R)]": "ee_29.png",
+    "[({)]": "ee_30.png",
+    "[(})]": "ee_31.png",
+    "[(k)]": "ee_32.png",
+    "[(F)]": "ee_33.png",
+    "[(W)]": "ee_34.png",
+    "[(D)]": "ee_35.png"
+  },
+  map6: {
+    "[del]": "del.png"
+  }
+}; 
+// uni.connectSocket({url: WebIM.config.xmppURL, method: "GET"})
+
+
+WebIM.conn = new WebIM.connection({
+  appKey: WebIM.config.appkey,
+  url: WebIM.config.xmppURL,
+  apiUrl: WebIM.config.apiURL,
+});
+
+module.exports = {
+  "default": WebIM
+};
+
+

+ 96 - 0
hxChatSDK/utils/WebIMConfig.js

@@ -0,0 +1,96 @@
+/**
+ * git do not control webim.config.js
+ * everyone should copy webim.config.js to webim.config.js
+ * and have their own configs.
+ * In this way , others won't be influenced by this config while git pull.
+ *
+ */
+// for react native
+let location = {
+  protocol: "https"
+};
+let config = {
+  /*
+   * XMPP server
+   */
+
+  // xmppURL: "wss://im-api-new-hsb.easemob.com/websocket",// websdk 3.0 server 沙箱(测试用)
+  
+  // xmppURL: "wss://im-api.easemob.com/ws",  //小程序sdk 2.0 server
+
+  xmppURL: 'wss://im-api-wechat.easemob.com/websocket', //小程序3.0 server 线上 小程序和原生客户端使用这个
+  
+  // xmppURL: 'wss://im-api-v2-hsb-alipay.easemob.com/websocket', // 支付宝沙箱 (测试用)
+  
+    // xmppURL: 'wss://im-api-alipay.easemob.com/websocket', // 支付宝线上 支付宝小程序请使用这个地址
+  
+  /*
+   * Backend REST API URL
+   */
+  // apiURL: (location.protocol === 'https:' ? 'https:' : 'http:') + '//a1.easemob.com',
+  // ios must be https!!! by lwz
+  apiURL: "https://a1.easemob.com",
+  
+  // apiURL: 'https://192.168.43.137:8080', // 支付宝测试
+  // apiURL: (location.protocol === 'https:' ? 'https:' : 'http:') + '//172.17.3.155:8080',
+
+  /*
+   * Application AppKey
+   */
+  appkey: "easemob#easeim",
+
+  /*
+   * Whether to use HTTPS      '1177161227178308#xcx'
+   * @parameter {Boolean} true or false
+   */
+  https: false,
+
+  /*
+   * isMultiLoginSessions
+   * true: A visitor can sign in to multiple webpages and receive messages at all the webpages.
+   * false: A visitor can sign in to only one webpage and receive messages at the webpage.
+   */
+  isMultiLoginSessions: false,
+
+  /**
+   * Whether to use window.doQuery()
+   * @parameter {Boolean} true or false
+   */
+  isWindowSDK: false,
+
+  /**
+   * isSandBox=true:  xmppURL: 'im-api.sandbox.easemob.com',  apiURL: '//a1.sdb.easemob.com',
+   * isSandBox=false: xmppURL: 'im-api.easemob.com',          apiURL: '//a1.easemob.com',
+   * @parameter {Boolean} true or false
+   */
+  isSandBox: false,
+
+  /**
+   * Whether to console.log in strophe.log()
+   * @parameter {Boolean} true or false
+   */
+  isDebug: false,
+
+  /**
+   * will auto connect the xmpp server autoReconnectNumMax times in background when client is offline.
+   * won't auto connect if autoReconnectNumMax=0.
+   */
+  autoReconnectNumMax: 15,
+
+  /**
+   * the interval secons between each atuo reconnectting.
+   * works only if autoReconnectMaxNum >= 2.
+   */
+  autoReconnectInterval: 2,
+
+  /**
+   * webrtc supports WebKit and https only
+   */
+  isWebRTC: false,
+
+  /*
+   * Set to auto sign-in
+   */
+  isAutoLogin: true
+};
+export default config;

+ 3 - 0
hxChatSDK/utils/broadcast.js

@@ -0,0 +1,3 @@
+var Dispatcher = require("./Dispatcher.js");
+
+module.exports = new Dispatcher();

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä