GoeasyExchange.vue 35 KB

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