GoeasyExchange.vue 35 KB

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