GoeasyExchange.vue 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. <template>
  2. <view class="chatInterface" @contextmenu.prevent="">
  3. <view class="scroll-view">
  4. <image v-if="history.loading" class="history-loaded" src="/static/goimge/loading.svg"/>
  5. <view v-else :class="history.allLoaded ? 'history-loaded':'load'" @click="loadHistoryMessage(false)">
  6. <view>{{ history.allLoaded ?$t('GoeasyExchange.nohistory'):$t('GoeasyExchange.gethistory') }}</view>
  7. </view>
  8. <checkbox-group @change="selectMessages">
  9. <!--消息记录-->
  10. <view v-for="(message,index) in history.messages" :key="message.messageId">
  11. <!--时间显示,类似于微信,隔5分钟不发言,才显示时间-->
  12. <view class="time-lag">
  13. {{ renderMessageDate(message, index) }}
  14. </view>
  15. <view class="message-recalled" v-if="message.recalled">
  16. <view v-if="message.recaller.id === currentUser.id" class="message-recalled-self">
  17. <view>{{$t('GoeasyExchange.nichlytxx')}}</view>
  18. <span v-if="message.type === 'text' && Date.now()-message.timestamp< 60 * 1000 "
  19. @click="editRecalledMessage(message.payload.text)">{{$t('GoeasyExchange.chongxbj')}}</span>
  20. </view>
  21. <view v-else>{{ message.recaller.data.name }}{{$t('GoeasyExchange.nichlytxx')}}</view>
  22. </view>
  23. <view class="message-item" v-else>
  24. <view class="message-item-checkbox">
  25. <checkbox v-show="messageSelector.visible && message.status !== 'sending'" :value="message.messageId"
  26. :checked="messageSelector.messages.includes(message)"/>
  27. </view>
  28. <view class="message-item-content" :class="{'self' : message.senderId === currentUser.id}">
  29. <view class="avatar">
  30. <image :src="message.senderId === currentUser.id? SplicingImgUrl(currentUser.avatar) : SplicingImgUrl(friend.avatar)"></image>
  31. </view>
  32. <view class="content" @click.right="showActionPopup(message)" @longpress="showActionPopup(message)">
  33. <view class="message-payload">
  34. <b class="pending" v-if="message.status === 'sending'"></b>
  35. <b class="send-fail" v-if="message.status === 'fail'"></b>
  36. <view v-if="message.type === 'text'" v-html="renderTextMessage(message)"></view>
  37. <image v-if="message.type === 'image'"
  38. :data-url="message.payload.url"
  39. :src="message.payload.thumbnail"
  40. class="image-content"
  41. @click="showImageFullScreen"
  42. ></image>
  43. <image v-if="message.type === 'Privateimage'"
  44. :data-url="SplicingImgUrl(message.payload.image)"
  45. :src="SplicingImgUrl(message.payload.image)"
  46. class="image-content"
  47. @click="showImageFullScreen"
  48. ></image>
  49. <view class="video-snapshot" v-if="message.type === 'video'" :data-url="message.payload.video.url"
  50. @click="playVideo">
  51. <image
  52. :src="message.payload.thumbnail.url"
  53. :style="{height: getImageHeight(message.payload.thumbnail.width,message.payload.thumbnail.height)+'rpx' }"
  54. mode="heightFix"
  55. ></image>
  56. <view class="video-play-icon"></view>
  57. </view>
  58. <view class="file-content" v-if="message.type === 'file'">
  59. <view class="file-info">
  60. <span class="file-name">{{ message.payload.name }}</span>
  61. <span class="file-size">{{ (message.payload.size / 1024).toFixed(2) }}KB</span>
  62. </view>
  63. <image class="file-img" src="/static/goimge/file-icon.png"></image>
  64. </view>
  65. <view v-if="message.type ==='audio'" class="audio-content" @click="playAudio(message)">
  66. <view class="audio-facade" :style="{width:Math.ceil(message.payload.duration)*7 + 50 + 'px'}">
  67. <view
  68. class="audio-facade-bg"
  69. :class="{'play-icon':audioPlayer.playingMessage && audioPlayer.playingMessage.messageId === message.messageId}"
  70. ></view>
  71. <view>{{Math.ceil(message.payload.duration) || 1}}<span>"</span></view>
  72. </view>
  73. </view>
  74. <view v-if="message.type === 'order'" class="order-content">
  75. <view class="order-id">{{$t('GoeasyExchange.dingdanhao')}}:{{ message.payload.id }}</view>
  76. <view class="order-body">
  77. <image :src="message.payload.url" class="order-img"></image>
  78. <view>
  79. <view class="order-name">{{ message.payload.name }}</view>
  80. <view class="order-info">
  81. <view class="order-price">{{ message.payload.price }}</view>
  82. <view class="order-count">{{$t('GoeasyExchange.gong')}}{{ message.payload.count }}{{$t('GoeasyExchange.jian')}}</view>
  83. </view>
  84. </view>
  85. </view>
  86. </view>
  87. </view>
  88. <view v-if="message.senderId === currentUser.id" :class="message.read ?'message-read':'message-unread'">
  89. <view v-if="message.status === 'success'">{{ message.read ? $t('GoeasyExchange.yidu') :$t('GoeasyExchange.weidu')}}</view>
  90. </view>
  91. </view>
  92. </view>
  93. </view>
  94. </view>
  95. </checkbox-group>
  96. </view>
  97. <view class="action-box" v-if="!videoPlayer.visible && !messageSelector.visible">
  98. <view class="action-top">
  99. <view @click="switchAudioKeyboard">
  100. <image class="more" v-if="audio.visible" src="/static/goimge/jianpan.png"></image>
  101. <image class="more" v-else src="/static/goimge/audio.png"></image>
  102. </view>
  103. <view v-if="audio.visible" class="record-input" @click="onRecordStart" @touchend.stop="onRecordEnd" @touchstart.stop="onRecordStart">
  104. {{ audio.recording ? $t('GoeasyExchange.songkfs') : $t('GoeasyExchange.anzhuly') }}
  105. </view>
  106. <!-- GoEasyIM最大支持3k的文本消息,如需发送长文本,需调整输入框maxlength值 -->
  107. <input v-else v-model="text" @confirm="sendTextMessage" class="consult-input" maxlength="700" :placeholder="$t('GoeasyExchange.fasongxx')" type="text" />
  108. <view @click="switchEmojiKeyboard">
  109. <image class="more" v-if="emoji.visible" src="/static/goimge/jianpan.png"></image>
  110. <image class="more" v-else src="/static/goimge/emoji.png"></image>
  111. </view>
  112. <view>
  113. <image @click="showOtherTypesMessagePanel()" class="more" src="/static/goimge/more.png"/>
  114. </view>
  115. <view v-if="text" class="send-btn-box">
  116. <text class="btn" @click="sendTextMessage()">{{$t('GoeasyExchange.fasong')}}</text>
  117. </view>
  118. </view>
  119. <!--展示表情列表-->
  120. <view class="action-bottom action-bottom-emoji" v-if="emoji.visible">
  121. <image class="emoji-item" v-for="(emojiItem, emojiKey, index) in emoji.map" :key="index"
  122. :src="emoji.url + emojiItem" @click="chooseEmoji(emojiKey)"></image>
  123. </view>
  124. <!--其他类型消息面板-->
  125. <view v-if="otherTypesMessagePanelVisible" class="action-bottom">
  126. <view class="more-icon">
  127. <image @click="sendPrivateMessage()" class="operation-icon" src="/static/goimge/picture.png"></image>
  128. <view class="operation-title">{{$t('GoeasyExchange.tupian')}}</view>
  129. </view>
  130. <view class="more-icon">
  131. <image @click="imcall()" class="operation-icon" src="/static/goimge/jietong.png"></image>
  132. <view class="operation-title">{{$t('GoeasyExchange.tonghua')}}</view>
  133. </view>
  134. </view>
  135. </view>
  136. <view class="action-popup" @touchmove.stop.prevent v-if="actionPopup.visible">
  137. <view class="layer"></view>
  138. <view class="action-list">
  139. <view class="action-item" @click="deleteSingleMessage">{{$t('GoeasyExchange.sanchu')}}</view>
  140. <view class="action-item" v-if="actionPopup.recallable" @click="recallMessage">{{$t('GoeasyExchange.chehui')}}</view>
  141. <view class="action-item" @click="showCheckBox">{{$t('GoeasyExchange.duoxuan')}}</view>
  142. <view class="action-item" @click="hideActionPopup">{{$t('GoeasyExchange.quxiao')}}</view>
  143. </view>
  144. </view>
  145. <view class="messageSelector-box" v-if="messageSelector.visible">
  146. <image class="messageSelector-btn" @click="deleteMultipleMessages" src="/static/goimge/delete.png"></image>
  147. </view>
  148. <view class="record-loading" v-if="audio.recording"></view>
  149. <video v-if="videoPlayer.visible" :src="videoPlayer.url" id="videoPlayer"
  150. @fullscreenchange="onVideoFullScreenChange"></video>
  151. <view v-if="orderList.visible" class="order-list">
  152. <view class="orders-content">
  153. <view class="title">
  154. <view>{{$t('GoeasyExchange.qingxuanzdd')}}</view>
  155. <view class="close" @click="hideOrderMessageList">×</view>
  156. </view>
  157. <view class="orders">
  158. <view
  159. v-for="(order, index) in orderList.orders"
  160. :key="index" class="order-item"
  161. @click="sendOrderMessage(order)"
  162. >
  163. <view class="order-id">{{$t('GoeasyExchange.dingdanhao')}}:{{ order.id }}</view>
  164. <view class="order-body">
  165. <image :src="order.url" class="order-img"></image>
  166. <view class="order-name">{{ order.name }}</view>
  167. <view class="order-right">
  168. <view class="order-price">{{ order.price }}</view>
  169. <view class="order-count">{{$t('GoeasyExchange.gong')}}{{ order.count }}{{$t('GoeasyExchange.jian')}}</view>
  170. </view>
  171. </view>
  172. </view>
  173. </view>
  174. </view>
  175. </view>
  176. </view>
  177. </template>
  178. <script>
  179. import EmojiDecoder from '@/pages/api/EmojiDecoder.js';
  180. import {formatDate} from '@/pages/api/utils';
  181. const IMAGE_MAX_WIDTH = 200;
  182. const IMAGE_MAX_HEIGHT = 150;
  183. const recorderManager = uni.getRecorderManager();
  184. export default {
  185. name: 'privateChat',
  186. data() {
  187. const emojiUrl = 'https://imgcache.qq.com/open/qcloud/tim/assets/emoji/';
  188. const emojiMap = {
  189. '[么么哒]': 'emoji_3@2x.png',
  190. '[乒乓]': 'emoji_4@2x.png',
  191. '[便便]': 'emoji_5@2x.png',
  192. '[信封]': 'emoji_6@2x.png',
  193. '[偷笑]': 'emoji_7@2x.png',
  194. '[傲慢]': 'emoji_8@2x.png'
  195. };
  196. return {
  197. //聊天文本框
  198. text: '',
  199. friend: null,
  200. iscaller:false,
  201. issend:false,
  202. to: {},// 作为createMessage的参数
  203. currentUser: null,
  204. audioObj:null,
  205. soundPlay:false,
  206. //定义表情列表
  207. emoji: {
  208. url: emojiUrl,
  209. map: emojiMap,
  210. visible: false,
  211. decoder: new EmojiDecoder(emojiUrl, emojiMap),
  212. },
  213. //是否展示‘其他消息类型面板’
  214. otherTypesMessagePanelVisible: false,
  215. orderList: {
  216. orders: [],
  217. visible: false
  218. },
  219. history: {
  220. messages: [],
  221. allLoaded: false,
  222. loading: false
  223. },
  224. audio: {
  225. startTime: null,
  226. //语音录音中
  227. recording: false,
  228. //录音按钮展示
  229. visible: false
  230. },
  231. audioPlayer: {
  232. innerAudioContext: null,
  233. playingMessage: null,
  234. },
  235. videoPlayer: {
  236. visible: false,
  237. url: '',
  238. context: null
  239. },
  240. // 展示消息删除弹出框
  241. actionPopup: {
  242. visible: false,
  243. message: null,
  244. recallable: false,
  245. },
  246. // 消息选择
  247. messageSelector: {
  248. visible: false,
  249. messages: []
  250. }
  251. }
  252. },
  253. onLoad(option) {
  254. //聊天对象
  255. var systemInfo = uni.getSystemInfoSync();
  256. var isAndroid = systemInfo.platform.toLowerCase() === 'android';
  257. getApp().globalData.EXViewOpen=true;
  258. if(option.caller==1){
  259. this.iscaller=true;
  260. var friendinfo = uni.getStorageSync('imUser');
  261. this.friend = {
  262. id:'userid_'+friendinfo.userId,
  263. name:friendinfo.nickName,
  264. avatar:friendinfo.avatar
  265. }
  266. }
  267. else{
  268. var payload=uni.getStorageSync('impayload');
  269. this.doPushMessage(payload);
  270. if(!isAndroid){
  271. this.audioObj=uni.createInnerAudioContext();
  272. this.audioObj.src='static/y800.mp3';
  273. this.audioObj.play();
  274. this.soundPlay=true;
  275. }
  276. }
  277. var userinfo = uni.getStorageSync('userInfo');
  278. this.currentUser={
  279. id:'userid_'+userinfo.userId,
  280. name:userinfo.nickName,
  281. avatar:userinfo.avatar
  282. };
  283. this.to = {
  284. id:this.friend.id,
  285. type: this.GoEasy.IM_SCENE.PRIVATE,
  286. data: {
  287. name: this.friend.name,
  288. avatar: this.friend.avatar
  289. }
  290. };
  291. this.initialGoEasyListeners();
  292. // 语音播放器
  293. this.initialAudioPlayer();
  294. // 录音监听器
  295. this.initRecorderListeners();
  296. },
  297. onShow() {
  298. this.otherTypesMessagePanelVisible = false;
  299. this.emoji.visible = false;
  300. },
  301. onReady() {
  302. this.loadHistoryMessage(true);
  303. this.videoPlayer.context = uni.createVideoContext('videoPlayer', this);
  304. // https://uniapp.dcloud.io/api/ui/navigationbar?id=setnavigationbartitle
  305. uni.setNavigationBarTitle({ title: this.friend.name });
  306. },
  307. onPullDownRefresh(e) {
  308. this.loadHistoryMessage(false);
  309. },
  310. onUnload() {
  311. if(this.soundPlay){
  312. this.audioObj.pause();
  313. }
  314. //退出聊天页面之前,清空监听器
  315. getApp().globalData.EXViewOpen=false;
  316. this.goEasy.im.off(this.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, this.onMessageReceived);
  317. this.goEasy.im.off(this.GoEasy.IM_EVENT.MESSAGE_DELETED, this.onMessageDeleted);
  318. //this.goEasy.im.off(this.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, this.onPrivateMessageReceived);
  319. },
  320. methods: {
  321. doPushMessage(str){
  322. if(str.indexOf("\\\"")!=-1){
  323. str=str.replace(/\\\"/g, '\"');
  324. str = str.replace(/\"{/g, '{');
  325. str = str.replace(/}\"/g, '}');
  326. }
  327. var obj = JSON.parse(str)
  328. this.friend = {
  329. id:'userid_'+obj.userId,
  330. name:obj.nickName,
  331. avatar:obj.avatar
  332. }
  333. console.log(obj);
  334. // this.imUser=obj;
  335. uni.setStorageSync('imUser',obj);
  336. },
  337. SplicingImgUrl(url){
  338. var baseUrl=imageBaseUrl;
  339. if(!url){
  340. return '';
  341. }
  342. if(url.length<8){
  343. return '';
  344. }
  345. var http= url.substring(0,8);
  346. if(http==='https://'){
  347. return url;
  348. }
  349. else if(http==='/static/'){
  350. return url;
  351. }
  352. else{
  353. return (baseUrl+url);
  354. }
  355. return '';
  356. },
  357. //渲染文本消息,如果包含表情,替换为图片
  358. //todo:本不需要该方法,可以在标签里完成,但小程序有兼容性问题,被迫这样实现
  359. renderTextMessage(message) {
  360. return '<span class="text-content">' + this.emoji.decoder.decode(message.payload.text) + '</span>'
  361. },
  362. //像微信那样显示时间,如果有几分钟没发消息了,才显示时间
  363. //todo:本不需要该方法,可以在标签里完成,但小程序有兼容性问题,被迫这样实现
  364. renderMessageDate(message, index) {
  365. if (index === 0) {
  366. return formatDate(message.timestamp)
  367. } else {
  368. if (message.timestamp - this.history.messages[index - 1].timestamp > 5 * 60 * 1000) {
  369. return formatDate(message.timestamp)
  370. }
  371. }
  372. return '';
  373. },
  374. initialGoEasyListeners() {
  375. // 监听私聊消息
  376. this.goEasy.im.on(this.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, this.onMessageReceived);
  377. //监听消息删除
  378. this.goEasy.im.on(this.GoEasy.IM_EVENT.MESSAGE_DELETED, this.onMessageDeleted);
  379. //监听自定义消息
  380. //this.goEasy.im.on(this.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, this.onPrivateMessageReceived);
  381. },
  382. onMessageReceived (message) {
  383. let senderId = message.senderId;
  384. let receiverId = message.receiverId;
  385. let friendId = this.currentUser.id === senderId ? receiverId : senderId;
  386. if (friendId === this.friend.id) {
  387. this.history.messages.push(message);
  388. //聊天时,收到消息标记为已读
  389. this.markPrivateMessageAsRead();
  390. //收到新消息,是滚动到最底部
  391. this.scrollToBottom();
  392. }
  393. },
  394. onMessageDeleted (deletedMessages) {
  395. deletedMessages.forEach(message => {
  396. let senderId = message.senderId;
  397. let receiverId = message.receiverId;
  398. let friendId = this.currentUser.id === senderId ? receiverId : senderId;
  399. if (friendId === this.friend.id) {
  400. let index = this.history.messages.indexOf(message);
  401. if (index > -1) {
  402. this.history.messages.splice(index, 1);
  403. }
  404. }
  405. });
  406. },
  407. onPrivateMessageReceived(message){
  408. let senderId = message.senderId;
  409. let receiverId = message.receiverId;
  410. let friendId = this.currentUser.id === senderId ? receiverId : senderId;
  411. if (friendId === this.friend.id) {
  412. this.history.messages.push(message);
  413. //聊天时,收到消息标记为已读
  414. this.markPrivateMessageAsRead();
  415. //收到新消息,是滚动到最底部
  416. this.scrollToBottom();
  417. }
  418. },
  419. initialAudioPlayer () {
  420. this.audioPlayer.innerAudioContext = uni.createInnerAudioContext();
  421. this.audioPlayer.innerAudioContext.onEnded(() => {
  422. this.audioPlayer.playingMessage = null;
  423. });
  424. this.audioPlayer.innerAudioContext.onStop(() => {
  425. this.audioPlayer.playingMessage = null;
  426. });
  427. },
  428. initRecorderListeners() {
  429. // 监听录音开始
  430. recorderManager.onStart(() => {
  431. this.audio.recording = true;
  432. this.audio.startTime = Date.now();
  433. });
  434. //录音结束后,发送
  435. recorderManager.onStop((res) => {
  436. let endTime = Date.now();
  437. this.audio.recording = false;
  438. let duration = endTime - this.audio.startTime;
  439. if (duration < 1000) {
  440. uni.showToast({
  441. icon: 'error',
  442. title:this.$t('GoeasyExchange.luyinsjtd'),
  443. duration: 500
  444. });
  445. return;
  446. }
  447. res.duration = duration;
  448. this.goEasy.im.createAudioMessage({
  449. to: this.to,
  450. file: res,
  451. notification: {
  452. title: this.currentUser.name + 'message',
  453. body:this.$t('GoeasyExchange.yuyinxx'), // 字段最长 50 字符message
  454. sound: 'message',
  455. badge: '+1'
  456. },
  457. onProgress: function (progress) {
  458. console.log(progress)
  459. },
  460. onSuccess: (message) => {
  461. this.sendMessage(message);
  462. },
  463. onFailed: (e) => {
  464. console.log('error :', e);
  465. }
  466. });
  467. });
  468. // 监听录音报错
  469. recorderManager.onError((res) => {
  470. this.audio.recording = false;
  471. recorderManager.stop();
  472. uni.showToast({
  473. icon: 'none',
  474. title:this.$t('GoeasyExchange.luyinshib'),
  475. duration: 1000
  476. });
  477. })
  478. },
  479. /**
  480. * 核心就是设置高度,产生明确占位
  481. *
  482. * 小 (宽度和高度都小于预设尺寸)
  483. * 设高=原始高度
  484. * 宽 (宽度>高度)
  485. * 高度= 根据宽度等比缩放
  486. * 窄 (宽度<高度)或方(宽度=高度)
  487. * 设高=MAX height
  488. *
  489. * @param width,height
  490. * @returns number
  491. */
  492. getImageHeight(width, height) {
  493. if (width < IMAGE_MAX_WIDTH && height < IMAGE_MAX_HEIGHT) {
  494. return height * 2;
  495. } else if (width > height) {
  496. return (IMAGE_MAX_WIDTH / width * height) * 2;
  497. } else if (width === height || width < height) {
  498. return IMAGE_MAX_HEIGHT * 2;
  499. }
  500. },
  501. sendMessage(message) {
  502. var that=this;
  503. this.history.messages.push(message);
  504. this.scrollToBottom();
  505. this.goEasy.im.sendMessage({
  506. message: message,
  507. onSuccess: function (message) {
  508. console.log('发送成功.', message);
  509. if(that.iscaller&&!that.issend){
  510. that.goEasypushmsg();
  511. }
  512. },
  513. onFailed: function (error) {
  514. if (error.code === 507) {
  515. console.log('发送语音/图片/视频/文件失败,没有配置OSS存储,详情参考:https://docs.goeasy.io/2.x/im/message/media/alioss');
  516. } else {
  517. console.log('发送失败:', error);
  518. }
  519. }
  520. });
  521. },
  522. sendTextMessage() {
  523. if (this.text.trim() !== '') {
  524. let body = this.text;
  525. if (this.text.length >= 50) {
  526. body = this.text.substring(0, 30) + '...';
  527. }
  528. this.goEasy.im.createTextMessage({
  529. text: this.text,
  530. to: this.to,
  531. notification: {
  532. title: this.currentUser.name + 'message',
  533. body: body,
  534. sound: 'message',
  535. badge: '+1'
  536. },
  537. onSuccess: (message) => {
  538. this.sendMessage(message);
  539. },
  540. onFailed: (e) => {
  541. console.log('error :', e);
  542. }
  543. });
  544. }
  545. this.text = '';
  546. },
  547. sendVideoMessage() {
  548. uni.chooseVideo({
  549. success: (res) => {
  550. this.goEasy.im.createVideoMessage({
  551. to: this.to,
  552. file: res,
  553. notification: {
  554. title: this.currentUser.name + 'message',
  555. body:this.$t('GoeasyExchange.shipinxx'), // 字段最长 50 字符
  556. sound: 'message',
  557. badge: '+1'
  558. },
  559. onProgress: function (progress) {
  560. console.log(progress)
  561. },
  562. onSuccess: (message) => {
  563. this.otherTypesMessagePanelVisible = false;
  564. this.sendMessage(message);
  565. },
  566. onFailed: (e) => {
  567. console.log('error :', e);
  568. }
  569. });
  570. }
  571. })
  572. },
  573. sendImageMessage() {
  574. uni.chooseImage({
  575. count: 9,
  576. success: (res) => {
  577. res.tempFiles.forEach(file => {
  578. this.goEasy.im.createImageMessage({
  579. to: this.to,
  580. file: file,
  581. notification: {
  582. title: this.currentUser.name + 'message',
  583. body:this.$t('GoeasyExchange.tupianxx'), // 字段最长 50 字符
  584. sound: 'message',
  585. badge: '+1'
  586. },
  587. beforeSend:()=>{
  588. },
  589. customizedUpload:(file,message)=> {
  590. return new Promise((resolve, reject) => {
  591. //上传图片到自己的服务器
  592. uni.uploadFile({
  593. url: 'https://www.goeasy.io/2.0/im/upload', //仅为示例,非真实的接口地址
  594. filePath: file.path,
  595. name: 'file',
  596. success: (uploadFileRes) => {
  597. let data = JSON.parse(uploadFileRes.data);
  598. if (data.code === 0) {
  599. message.url = data.data.url;
  600. resolve();
  601. } else {
  602. reject();
  603. }
  604. },
  605. fail: (e) => {
  606. reject(e);
  607. }
  608. });
  609. })
  610. },
  611. onProgress: function (progress) {
  612. console.log(progress)
  613. },
  614. onSuccess: (message) => {
  615. this.otherTypesMessagePanelVisible = false;
  616. this.sendMessage(message);
  617. },
  618. onFailed: (e) => {
  619. console.log('error :', e);
  620. }
  621. });
  622. })
  623. }
  624. });
  625. },
  626. sendPrivateMessage() {
  627. var that = this;
  628. uni.chooseImage({
  629. count: 9,
  630. success: (res) => {
  631. res.tempFiles.forEach(file => {
  632. //上传图片到自己的服务器
  633. uni.uploadFile({
  634. url:that.$upImagurl, //仅为示例,非真实的接口地址
  635. filePath: file.path,
  636. name: 'file',
  637. success: (uploadFileRes) => {
  638. let data = JSON.parse(uploadFileRes.data);
  639. console.log('1------2',data);
  640. if(data.code==200){
  641. var Pimage = {
  642. image:data.data,
  643. };
  644. var customMessage = that.goEasy.im.createCustomMessage({
  645. type: 'Privateimage', //字符串,可以任意自定义类型,比如红包'hongbao', 订单'order,处方'chufang'
  646. payload: Pimage,
  647. to: that.to
  648. });
  649. that.otherTypesMessagePanelVisible = false;
  650. that.sendMessage(customMessage);
  651. }
  652. },
  653. fail: (e) => {
  654. }
  655. });
  656. })
  657. }
  658. });
  659. },
  660. imcall(){
  661. uni.navigateTo({
  662. url:'/pages/imcall/audioCall?caller=1'
  663. })
  664. },
  665. sendOrderMessage(order) {
  666. //GoEasyIM自定义消息,实现订单发送
  667. this.goEasy.im.createCustomMessage({
  668. type: 'order',
  669. payload: order,
  670. to: this.to,
  671. notification: {
  672. title: this.currentUser.name + 'message',
  673. body:this.$t('GoeasyExchange.dingdanxx'),
  674. sound: 'message',
  675. badge: '+1'
  676. },
  677. onSuccess: (message) => {
  678. this.otherTypesMessagePanelVisible = false;
  679. this.sendMessage(message);
  680. },
  681. onFailed: (e) => {
  682. console.log('error :', e);
  683. }
  684. });
  685. this.orderList.visible = false;
  686. },
  687. showActionPopup(message) {
  688. const MAX_RECALLABLE_TIME = 3 * 60 * 1000; //3分钟以内的消息才可以撤回
  689. this.messageSelector.messages = [message];
  690. if ((Date.now() - message.timestamp) < MAX_RECALLABLE_TIME && message.senderId === this.currentUser.id && message.status === 'success') {
  691. this.actionPopup.recallable = true;
  692. } else {
  693. this.actionPopup.recallable = false;
  694. }
  695. this.actionPopup.visible = true;
  696. },
  697. hideActionPopup () {
  698. this.actionPopup.visible = false;
  699. this.actionPopup.message = null;
  700. },
  701. deleteSingleMessage() {
  702. uni.showModal({
  703. content:this.$t('GoeasyExchange.querensanc'),
  704. success: (res) => {
  705. this.actionPopup.visible = false;
  706. if (res.confirm) {
  707. this.deleteMessage();
  708. }
  709. },
  710. })
  711. },
  712. deleteMultipleMessages() {
  713. if (this.messageSelector.messages.length > 0) {
  714. uni.showModal({
  715. content: this.$t('GoeasyExchange.querensanc'),
  716. success: (res) => {
  717. this.messageSelector.visible = false;
  718. if (res.confirm) {
  719. this.deleteMessage();
  720. }
  721. },
  722. })
  723. }
  724. },
  725. deleteMessage() {
  726. this.goEasy.im.deleteMessage({
  727. messages: this.messageSelector.messages,
  728. onSuccess: (result) => {
  729. this.messageSelector.messages.forEach(message => {
  730. let index = this.history.messages.indexOf(message);
  731. if (index > -1) {
  732. this.history.messages.splice(index, 1);
  733. }
  734. });
  735. this.messageSelector.messages = [];
  736. },
  737. onFailed: (error) => {
  738. console.log('error:', error);
  739. }
  740. });
  741. },
  742. recallMessage() {
  743. this.actionPopup.visible = false;
  744. this.goEasy.im.recallMessage({
  745. messages: this.messageSelector.messages,
  746. onSuccess: () => {
  747. console.log('撤回成功');
  748. },
  749. onFailed: (error) => {
  750. console.log('撤回失败,error:', error);
  751. }
  752. });
  753. },
  754. editRecalledMessage(text) {
  755. if (this.audio.visible) {
  756. this.audio.visible = false;
  757. }
  758. this.text = text;
  759. },
  760. showCheckBox() {
  761. this.messageSelector.messages = [];
  762. this.messageSelector.visible = true;
  763. this.actionPopup.visible = false;
  764. },
  765. selectMessages(e) {
  766. const selectedMessageIds = e.detail.value;
  767. let selectedMessages = [];
  768. this.history.messages.forEach(message => {
  769. if (selectedMessageIds.includes(message.messageId)) {
  770. selectedMessages.push(message);
  771. }
  772. })
  773. this.messageSelector.messages = selectedMessages;
  774. },
  775. loadHistoryMessage(scrollToBottom) {//历史消息
  776. this.history.loading = true;
  777. let lastMessageTimeStamp = null;
  778. let lastMessage = this.history.messages[0];
  779. if (lastMessage) {
  780. lastMessageTimeStamp = lastMessage.timestamp;
  781. }
  782. this.goEasy.im.history({
  783. userId: this.friend.id,
  784. lastTimestamp: lastMessageTimeStamp,
  785. limit: 10,
  786. onSuccess: (result) => {
  787. uni.stopPullDownRefresh();
  788. this.history.loading = false;
  789. let messages = result.content;
  790. if (messages.length === 0) {
  791. this.history.allLoaded = true;
  792. } else {
  793. if (lastMessageTimeStamp) {
  794. this.history.messages = messages.concat(this.history.messages);
  795. } else {
  796. this.history.messages = messages;
  797. }
  798. if (messages.length < 10) {
  799. this.history.allLoaded = true;
  800. }
  801. if (scrollToBottom) {
  802. this.scrollToBottom();
  803. //收到的消息设置为已读
  804. this.markPrivateMessageAsRead();
  805. }
  806. }
  807. },
  808. onFailed: (error) => {
  809. //获取失败
  810. console.log('获取历史消息失败:', error);
  811. uni.stopPullDownRefresh();
  812. this.history.loading = false;
  813. }
  814. });
  815. },
  816. //语音录制按钮和键盘输入的切换
  817. switchAudioKeyboard() {
  818. this.audio.visible = !this.audio.visible;
  819. if (uni.authorize) {
  820. uni.authorize({
  821. scope: 'scope.record',
  822. fail: () => {
  823. uni.showModal({
  824. title:this.$t('GoeasyExchange.huoquqxsb'),
  825. content:this.$t('GoeasyExchange.qingxsqly')
  826. });
  827. }
  828. });
  829. }
  830. },
  831. onRecordStart() {
  832. var that = this;
  833. try {
  834. // 更多配置参考uniapp:https://uniapp.dcloud.net.cn/api/media/record-manager.html#getrecordermanager
  835. recorderManager.start({
  836. duration: 600000 // 指定录音的时长,单位 ms
  837. });
  838. } catch (e) {
  839. uni.showModal({
  840. title:that.$t('GoeasyExchange.luyincw'),
  841. content:that.$t('GoeasyExchange.qingshcs')
  842. });
  843. }
  844. },
  845. onRecordEnd() {
  846. try {
  847. recorderManager.stop();
  848. } catch (e) {
  849. console.log(e);
  850. }
  851. },
  852. showImageFullScreen(e) {
  853. let imagesUrl = [e.currentTarget.dataset.url];
  854. uni.previewImage({
  855. urls: imagesUrl
  856. });
  857. },
  858. playVideo(e) {
  859. this.videoPlayer.visible = true;
  860. this.videoPlayer.url = e.currentTarget.dataset.url;
  861. this.$nextTick(() => {
  862. this.videoPlayer.context.requestFullScreen({
  863. direction: 0
  864. });
  865. this.videoPlayer.context.play();
  866. });
  867. },
  868. playAudio (audioMessage) {
  869. let playingMessage = this.audioPlayer.playingMessage;
  870. if (playingMessage) {
  871. this.audioPlayer.innerAudioContext.stop();
  872. // 如果点击的消息正在播放,就认为是停止播放操作
  873. if (playingMessage === audioMessage) {
  874. return;
  875. }
  876. }
  877. this.audioPlayer.playingMessage = audioMessage;
  878. this.audioPlayer.innerAudioContext.src = audioMessage.payload.url;
  879. this.audioPlayer.innerAudioContext.play();
  880. },
  881. onVideoFullScreenChange(e) {
  882. //当退出全屏播放时,隐藏播放器
  883. if (this.videoPlayer.visible && !e.detail.fullScreen) {
  884. this.videoPlayer.visible = false;
  885. this.videoPlayer.context.stop();
  886. }
  887. },
  888. messageInputFocusin() {
  889. this.otherTypesMessagePanelVisible = false;
  890. this.emoji.visible = false;
  891. },
  892. switchEmojiKeyboard() {
  893. this.emoji.visible = !this.emoji.visible;
  894. this.otherTypesMessagePanelVisible = false;
  895. },
  896. showOtherTypesMessagePanel() {
  897. this.otherTypesMessagePanelVisible = !this.otherTypesMessagePanelVisible;
  898. this.emoji.visible = false;
  899. },
  900. chooseEmoji(emojiKey) {
  901. this.text += emojiKey;
  902. },
  903. hideOrderMessageList() {
  904. this.orderList.visible = false;
  905. },
  906. scrollToBottom() {
  907. this.$nextTick(() => {
  908. uni.pageScrollTo({
  909. scrollTop: 2000000,
  910. duration: 0
  911. });
  912. });
  913. },
  914. markPrivateMessageAsRead() {
  915. this.goEasy.im.markMessageAsRead({
  916. id: this.to.id,
  917. type: this.to.type,
  918. onSuccess: function () {
  919. console.log('标记私聊已读成功');
  920. },
  921. onFailed: function (error) {
  922. console.log("标记私聊已读失败", error);
  923. }
  924. });
  925. },
  926. goEasypushmsg(){
  927. var userinfo = uni.getStorageSync('userInfo');
  928. var payload={
  929. roomId:this.currentUser.id,
  930. userId:userinfo.userId,
  931. nickName:this.currentUser.name,
  932. avatar:this.currentUser.avatar,
  933. ptype:2
  934. }
  935. var that = this;
  936. var pubsub = this.goEasy.pubsub;
  937. pubsub.publish({
  938. channel:this.friend.id, //请确认与接收端一致
  939. message:JSON.stringify(payload), //app内onMessage收到的消息内容
  940. notification: { //定义通知栏推送
  941. title: 'IM message', //通知栏提醒标题,仅显示于通知栏
  942. body:'message', //通知栏提醒内容,仅显示于通知栏
  943. },
  944. onSuccess: function () {
  945. that.issend=true;
  946. console.log("Publish successfully.")
  947. },
  948. onFailed: function (error) {
  949. console.log("Failed to publish message, code:" + error.code + ' error:' + error.content);
  950. }
  951. });
  952. },
  953. }
  954. }
  955. </script>
  956. <style lang="scss">
  957. @import url('@/static/style/chatInterface.css');
  958. </style>