index.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918
  1. <template>
  2. <view class="">
  3. <view class="forbidden_footer" v-if="getPlaceholder.length > 0">
  4. <image style="margin-right: 8rpx" src="https://bjyjb-1362704775.cos.ap-chongqing.myqcloud.com/shop/image/forbidden_footer.png" />
  5. <text>{{ getPlaceholder }}</text>
  6. </view>
  7. <view v-show="getPlaceholder.length <= 0" :style="{ 'pointer-events': getPlaceholder ? 'none' : 'auto' }">
  8. <view class="chat_footer">
  9. <image v-if="showRecord"
  10. @click="updateRecordBtn"
  11. style="width: 26px; height: 26px;"
  12. src="https://bjyjb-1362704775.cos.ap-chongqing.myqcloud.com/shop/image/chating_footer_audio_recording.png" />
  13. <image v-else
  14. @click="updateRecordBtn"
  15. style="width: 26px; height: 26px;"
  16. src="https://bjyjb-1362704775.cos.ap-chongqing.myqcloud.com/shop/image/chating_footer_audio.png" />
  17. <view class="input_content">
  18. <CustomEditor
  19. :placeholder="getPlaceholder"
  20. v-show="!showRecord"
  21. class="custom_editor"
  22. ref="customEditor"
  23. @ready="editorReady"
  24. @focus="editorFocus"
  25. @blur="editorBlur"
  26. @input="editorInput"
  27. @tryAt="showAtPanel"/>
  28. <view v-if="storeQuoteMessage && storeQuoteMessage!=undefined" class="quote_message">
  29. <view class="content">
  30. <u-parse :content="getQuotedContent" />
  31. </view>
  32. <image @click="cancelQuote" style="width: 16px; height: 16px" src="https://bjyjb-1362704775.cos.ap-chongqing.myqcloud.com/shop/image/chating_footer_quote_close.png" alt="" />
  33. </view>
  34. <button class="record_btn" @longpress="longpressRecord" @touchmove="touchMoveSpeech" @touchend="endRecord" v-show="showRecord" customStyle="height:30px;">
  35. {{ recording ? '松手发送' : '按住说话' }}
  36. </button>
  37. </view>
  38. <view class="footer_action_area">
  39. <image @click="updateEmojiBar" class="emoji_action" src="https://bjyjb-1362704775.cos.ap-chongqing.myqcloud.com/shop/image/chating_footer_emoji.png" alt="" srcset="" />
  40. <image v-show="!hasContent" @click.prevent="updateActionBar" src="https://bjyjb-1362704775.cos.ap-chongqing.myqcloud.com/shop/image/chating_footer_add.png" alt="" srcset="" />
  41. <button class="btnSend" v-show="hasContent && !emojiBarVisible" @touchend.prevent="sendTextMessage">发送</button>
  42. <button class="btnSend" v-show="hasContent && emojiBarVisible" @touchend.prevent="sendTextMessage">发送</button>
  43. </view>
  44. </view>
  45. <chating-action-bar @sendMessage="sendMessage" @prepareMediaMessage="prepareMediaMessage" v-show="actionBarVisible" />
  46. <chating-emoji-bar @insertEmoji="emojiClick" @backspace="backspace" v-show="emojiBarVisible" />
  47. <u-action-sheet
  48. :safeAreaInsetBottom="true"
  49. round="12"
  50. :actions="actionSheetMenu"
  51. @select="selectClick"
  52. :closeOnClickOverlay="true"
  53. :closeOnClickAction="true"
  54. :show="showActionSheet"
  55. @close="showActionSheet = false"></u-action-sheet>
  56. <record-overlay @recordFinish="recordFinish" ref="recordOverlayRef" :visible="recording" />
  57. </view>
  58. </view>
  59. </template>
  60. <script>
  61. import { mapGetters, mapActions } from 'vuex';
  62. import { formatInputHtml, getPurePath, html2Text } from '../../../../../util/common';
  63. import { parseMessageByType, offlinePushInfo, parseAt } from '../../../../../util/imCommon';
  64. import { ChatingFooterActionTypes, UpdateMessageTypes, GroupMemberListTypes, PageEvents } from '../../../../../constant';
  65. import IMSDK, { GroupMemberRole, GroupStatus, IMMethods, MessageStatus, MessageType, SessionType } from 'openim-uniapp-polyfill';
  66. import UParse from '../../../../../components/gaoyia-parse/parse.vue';
  67. import CustomEditor from './CustomEditor.vue';
  68. import ChatingActionBar from './ChatingActionBar.vue';
  69. import ChatingEmojiBar from './ChatingEmojiBar.vue';
  70. import RecordOverlay from './RecordOverlay.vue';
  71. import { gotoAppPermissionSetting, requestAndroidPermission, judgeIosPermission } from '../../../../../util/permission';
  72. const needClearTypes = [
  73. // MessageType.AdvanceTextMessage,
  74. MessageType.TextMessage,
  75. MessageType.AtTextMessage,
  76. MessageType.QuoteMessage
  77. ];
  78. const albumChoose = [
  79. {
  80. name: '图片',
  81. type: ChatingFooterActionTypes.Album,
  82. idx: 0
  83. },
  84. {
  85. name: '视频',
  86. type: ChatingFooterActionTypes.Album,
  87. idx: 1
  88. }
  89. ];
  90. const cameraChoose = [
  91. {
  92. name: '拍照',
  93. type: ChatingFooterActionTypes.Camera,
  94. idx: 0
  95. },
  96. {
  97. name: '录制',
  98. type: ChatingFooterActionTypes.Camera,
  99. idx: 1
  100. }
  101. ];
  102. export default {
  103. components: {
  104. CustomEditor,
  105. ChatingActionBar,
  106. ChatingEmojiBar,
  107. UParse,
  108. RecordOverlay
  109. },
  110. props: {
  111. footerOutsideFlag: Number
  112. },
  113. data() {
  114. return {
  115. customEditorCtx: null,
  116. inputHtml: '',
  117. showRecord: false,
  118. actionBarVisible: false,
  119. emojiBarVisible: false,
  120. isInputFocus: false,
  121. actionSheetMenu: [],
  122. showActionSheet: false,
  123. recording: false,
  124. recordTop: uni.getWindowInfo().windowHeight - 130,
  125. conversationID: null,
  126. isJoinGroup: true
  127. };
  128. },
  129. computed: {
  130. ...mapGetters(['storeQuoteMessage', 'storeCurrentConversation', 'storeCurrentGroup', 'storeCurrentMemberInGroup', 'storeBlackList', 'storeRevokeMap','timStore']),
  131. getQuotedContent() {
  132. if (!this.storeQuoteMessage) {
  133. return '';
  134. }
  135. return `回复${parseMessageByType(this.storeQuoteMessage)}`;
  136. },
  137. hasContent() {
  138. return html2Text(this.inputHtml) !== '';
  139. },
  140. isNomal() {
  141. return this.storeCurrentMemberInGroup.roleLevel === GroupMemberRole.Nomal;
  142. },
  143. getPlaceholder() {
  144. const isSingle = this.storeCurrentConversation.conversationType === SessionType.Single;
  145. if (!isSingle && this.storeCurrentGroup.status === GroupStatus.Muted) {
  146. return !this.isNomal ? '' : '群主或管理员已开启全体禁言';
  147. }
  148. if (!isSingle && !this.isJoinGroup) {
  149. return '您已不在该群组';
  150. }
  151. if (!isSingle && this.storeCurrentMemberInGroup.muteEndTime > Date.now()) {
  152. return `您已被管理员或群主禁言!`;
  153. }
  154. if (isSingle && this.storeBlackList.find((black) => black.userID === this.storeCurrentConversation.userID)) {
  155. return '对方已被拉入黑名单';
  156. }
  157. return '';
  158. },
  159. imType() {
  160. return this.timStore?.imType ?? '' ;
  161. },
  162. orderId() {
  163. return this.timStore?.orderId ?? '' ;
  164. },
  165. orderType() {
  166. return this.timStore?.orderType ?? '' ;
  167. },
  168. },
  169. watch: {
  170. footerOutsideFlag(newVal) {
  171. this.onClickActionBarOutside();
  172. this.onClickEmojiBarOutside();
  173. }
  174. },
  175. mounted() {
  176. this.setIMListener();
  177. this.setKeyboardListener();
  178. if (this.storeCurrentConversation.groupID) {
  179. IMSDK.asyncApi(IMMethods.IsJoinGroup, IMSDK.uuid(), this.storeCurrentConversation.groupID).then((res) => {
  180. this.isJoinGroup = res.data;
  181. });
  182. }
  183. this.conversationID = this.storeCurrentConversation.conversationID;
  184. console.log("--qxj storeCurrentConversation");
  185. console.log(this.storeCurrentConversation);
  186. this.parseDraftText(this.storeCurrentConversation.draftText);
  187. // IMSDK.asyncApi(IMMethods.SetConversationDraft, IMSDK.uuid(), {
  188. // conversationID: this.conversationID,
  189. // draftText: ''
  190. // });
  191. uni.$on(PageEvents.ReEditMessage, this.setReEditMessage);
  192. },
  193. beforeDestroy() {
  194. this.disposeIMListener();
  195. this.disposeKeyboardListener();
  196. if (this.inputHtml && this.inputHtml.trim()) {
  197. IMSDK.asyncApi(IMMethods.SetConversationDraft, IMSDK.uuid(), {
  198. conversationID: this.conversationID,
  199. draftText: html2Text(this.inputHtml)
  200. });
  201. }
  202. },
  203. methods: {
  204. ...mapActions('message', ['pushNewMessage', 'updateOneMessage', 'updateQuoteMessageRevoke']),
  205. setReEditMessage(id) {
  206. this.customEditorCtx.clear();
  207. setTimeout(() => {
  208. this.$refs.customEditor.insertEmoji(this.storeRevokeMap[id].text);
  209. this.$store.commit('message/SET_QUOTE_MESSAGE', this.storeRevokeMap[id].quoteMessage);
  210. }, 50);
  211. },
  212. parseDraftText(draftText) {
  213. let currentText = '';
  214. for (let i = 0; i < draftText.length; i++) {
  215. const currentChar = draftText[i];
  216. if (currentChar === '<') {
  217. if (currentText) {
  218. const text = currentText;
  219. setTimeout(() => {
  220. this.$refs.customEditor.insertEmoji(text);
  221. }, 100);
  222. currentText = '';
  223. }
  224. const imgEndIndex = draftText.indexOf('>', i);
  225. if (imgEndIndex !== -1) {
  226. const imgTag = draftText.substring(i, imgEndIndex + 1);
  227. if (imgTag.includes('class="at_el"')) {
  228. const customDataReg = /data-custom=".+"/;
  229. const atInfoArr = imgTag.match(customDataReg)[0].slice(13, -1).split('&amp;');
  230. this.$refs.customEditor.createCanvasData(atInfoArr[0].slice(7), atInfoArr[1].slice(15));
  231. }
  232. i = imgEndIndex;
  233. }
  234. } else {
  235. currentText += currentChar;
  236. }
  237. }
  238. if (currentText) {
  239. setTimeout(() => {
  240. this.$refs.customEditor.insertEmoji(currentText);
  241. }, 700);
  242. }
  243. },
  244. longpressRecord() {
  245. this.recording = true;
  246. this.$nextTick(() => this.getRecordAreaData());
  247. },
  248. touchMoveSpeech(e) {
  249. uni.$u.throttle(this.$refs.recordOverlayRef.touchMoveSpeech(e, this.recordTop), 250);
  250. },
  251. endRecord() {
  252. this.recording = false;
  253. this.$refs.recordOverlayRef.checkStopType();
  254. },
  255. async recordFinish(path, duration) {
  256. console.log("qxj recordFinish duration:"+duration+" path:"+path);
  257. this.createAndSendSoundMessage(path,IMSDK.uuid(),duration);
  258. // const message = await IMSDK.asyncApi(IMMethods.CreateSoundMessageFromFullPath, IMSDK.uuid(), {
  259. // soundPath: getPurePath(path),
  260. // duration
  261. // });
  262. //this.sendMessage(message);
  263. },
  264. getRecordAreaData() {
  265. this.getEl('.record_area').then((data) => {
  266. if (data) {
  267. this.recordTop = data.top;
  268. }
  269. });
  270. },
  271. getEl(el) {
  272. return new Promise((resolve) => {
  273. const query = uni.createSelectorQuery().in(this);
  274. query.select(el).boundingClientRect((data) => {
  275. resolve(data);
  276. }).exec();
  277. });
  278. },
  279. async createTextMessage() {
  280. let message = '';
  281. const { text, atUsersInfo } = formatInputHtml(this.inputHtml);
  282. if (atUsersInfo.length === 0) {
  283. if (this.storeQuoteMessage) {
  284. let uuid=IMSDK.uuid();
  285. let params={
  286. text,
  287. message: JSON.stringify(this.storeQuoteMessage)
  288. };
  289. message = await IMSDK.asyncApi(IMMethods.CreateQuoteMessage, uuid, params);
  290. } else {
  291. message = await IMSDK.asyncApi(IMMethods.CreateTextMessage, IMSDK.uuid(), text);
  292. }
  293. }
  294. else {
  295. const quoteMessage = this.storeQuoteMessage ? this.storeQuoteMessage : '';
  296. message = await IMSDK.asyncApi(IMMethods.CreateTextAtMessage, IMSDK.uuid(), {
  297. text,
  298. atUserIDList: atUsersInfo.map((item) => item.atUserID),
  299. atUsersInfo,
  300. message: quoteMessage
  301. });
  302. }
  303. return message;
  304. },
  305. async sendTextMessage() {
  306. // if(this.emojiBarVisible){
  307. // uni.hideKeyboard()
  308. // }
  309. console.log("qxj sendTextMessage");
  310. if (!this.hasContent) return;
  311. IMSDK.asyncApi('changeInputStates', IMSDK.uuid(), {
  312. conversationID: this.storeCurrentConversation.conversationID,
  313. focus: false
  314. });
  315. const message = await this.createTextMessage();
  316. this.sendMessage(message);
  317. },
  318. sendMessage(message) {
  319. var customerData={
  320. type:this.timStore?.type,
  321. imType:this.timStore?.imType,
  322. orderId:this.timStore?.orderId,
  323. followId:this.timStore?.followId,
  324. orderType:this.timStore?.orderType
  325. }
  326. let userId=this.storeCurrentConversation.userID;
  327. if(userId!=undefined && (userId!="" || userId.length>0)){
  328. if(userId.indexOf('D')!==-1){
  329. message.ex=JSON.stringify(customerData);
  330. }
  331. }
  332. this.pushNewMessage(message);
  333. if (needClearTypes.includes(message.contentType)) {
  334. this.customEditorCtx.clear();
  335. }
  336. this.$emit('scrollToBottom');
  337. let Method=IMMethods.SendMessage;
  338. if(message.contentType==MessageType.PictureMessage || message.contentType==MessageType.VideoMessage || message.contentType==MessageType.VoiceMessage || message.contentType==MessageType.FileMessage){
  339. Method=IMMethods.SendMessageNotOss;
  340. }
  341. console.log("qxj sendMessage:"+JSON.stringify(message));
  342. IMSDK.asyncApi(Method, IMSDK.uuid(), {
  343. recvID: this.storeCurrentConversation.userID,
  344. groupID: this.storeCurrentConversation.groupID,
  345. message,
  346. offlinePushInfo
  347. }).then(({ data }) => {
  348. this.updateOneMessage({
  349. message: data,
  350. isSuccess: true
  351. });
  352. }).catch(({ data, errCode, errMsg }) => {
  353. this.updateOneMessage({
  354. message: data,
  355. type: UpdateMessageTypes.KeyWords,
  356. keyWords: [
  357. {
  358. key: 'status',
  359. value: MessageStatus.Failed
  360. },
  361. {
  362. key: 'errCode',
  363. value: errCode
  364. }
  365. ]
  366. });
  367. });
  368. if (this.storeQuoteMessage) {
  369. this.$store.commit('message/SET_QUOTE_MESSAGE', false);
  370. }
  371. },
  372. // action
  373. cancelQuote() {
  374. this.$store.commit('message/SET_QUOTE_MESSAGE', false);
  375. },
  376. onClickActionBarOutside() {
  377. if (this.actionBarVisible) {
  378. this.actionBarVisible = false;
  379. }
  380. },
  381. onClickEmojiBarOutside() {
  382. if (this.emojiBarVisible) {
  383. this.emojiBarVisible = false;
  384. }
  385. },
  386. updateActionBar() {
  387. this.$emit('scrollToBottom');
  388. if (this.recording) {
  389. return;
  390. }
  391. if (this.showRecord) {
  392. this.showRecord = false;
  393. }
  394. this.onClickEmojiBarOutside();
  395. this.actionBarVisible = !this.actionBarVisible;
  396. },
  397. updateEmojiBar() {
  398. this.$emit('scrollToBottom');
  399. if (this.recording) {
  400. return;
  401. }
  402. if (this.showRecord) {
  403. this.showRecord = false;
  404. }
  405. this.onClickActionBarOutside();
  406. this.emojiBarVisible = !this.emojiBarVisible;
  407. },
  408. editorReady(e) {
  409. this.customEditorCtx = e.context;
  410. this.customEditorCtx.clear();
  411. },
  412. editorFocus() {
  413. this.isInputFocus = true;
  414. this.$emit('scrollToBottom');
  415. this.keyboardChangeHander({
  416. height: 0
  417. });
  418. },
  419. editorBlur() {
  420. this.isInputFocus = false;
  421. },
  422. editorInput(e) {
  423. this.inputHtml = e.detail.html;
  424. uni.$u.throttle(this.updateTyping, 2000);
  425. },
  426. showAtPanel() {
  427. if (!this.$store.getters.storeCurrentConversation.groupID) return;
  428. uni.$u.route('/pages_im/pages/conversation/groupMemberList/index', {
  429. type: GroupMemberListTypes.ChooseAt,
  430. groupID: this.$store.getters.storeCurrentConversation.groupID
  431. });
  432. },
  433. backspace() {
  434. this.customEditorCtx.undo();
  435. },
  436. updateRecordBtn() {
  437. if (this.recording) {
  438. return;
  439. }
  440. if (!this.showRecord) {
  441. this.checkRecordPermission();
  442. }
  443. this.showRecord = !this.showRecord;
  444. this.onClickActionBarOutside();
  445. this.onClickEmojiBarOutside();
  446. },
  447. async checkRecordPermission() {
  448. if (uni.$u.os() === 'ios') {
  449. const iosFlag = judgeIosPermission('record');
  450. if (iosFlag === 0) {
  451. uni.$u.toast('您已禁止访问麦克风,请前往开启');
  452. setTimeout(() => gotoAppPermissionSetting(), 250);
  453. }
  454. if (iosFlag === -1) {
  455. let recorderManager = uni.getRecorderManager();
  456. recorderManager.onStop(() => (recorderManager = null));
  457. recorderManager.start();
  458. setTimeout(() => recorderManager.stop());
  459. }
  460. }
  461. if (uni.$u.os() === 'android') {
  462. const androidFlag = await requestAndroidPermission('android.permission.RECORD_AUDIO');
  463. if (androidFlag === -1) {
  464. uni.$u.toast('您已禁止访问麦克风,请前往开启');
  465. setTimeout(() => gotoAppPermissionSetting(), 250);
  466. }
  467. }
  468. },
  469. prepareMediaMessage(data) {
  470. // if (data.type === ChatingFooterActionTypes.Album) {
  471. // this.actionSheetMenu = [...albumChoose];
  472. // } else {
  473. // this.actionSheetMenu = [...cameraChoose];
  474. // }
  475. // this.showActionSheet = false;
  476. this.selectClick(data);
  477. },
  478. updateTyping() {
  479. if (this.storeCurrentConversation.conversationType === SessionType.Single) {
  480. IMSDK.asyncApi('changeInputStates', IMSDK.uuid(), {
  481. conversationID: this.storeCurrentConversation.conversationID,
  482. focus: true
  483. });
  484. }
  485. },
  486. // from comp
  487. emojiClick(emoji) {
  488. console.log('emoji==',emoji);
  489. this.$refs.customEditor.insertEmoji(emoji);
  490. },
  491. batchCreateImageMesage(paths) {
  492. paths.forEach(async (path) => {
  493. this.createAndSendImageMessage(path,IMSDK.uuid());
  494. });
  495. },
  496. createAndSendImageMessage(tempFilePath,uuid){
  497. let that=this;
  498. uni.getImageInfo({
  499. src: tempFilePath,
  500. success: (infoRes) => {
  501. uni.uploadFile({
  502. url:"https://api.cdwjyyh.com/app/common/uploadOSSIM",
  503. //url: uni.getStorageSync('requestPath')+'/app/common/uploadOSSIM',
  504. filePath: tempFilePath,
  505. name: 'file',
  506. formData: {
  507. 'user': 'test' // 上传附带参数
  508. },
  509. success:async (uploadFileRes) => {
  510. let imUrl =JSON.parse(uploadFileRes.data).url;
  511. const picBaseInfo = {
  512. uuid: uuid,
  513. type: 'png',
  514. size: infoRes.width,
  515. width: infoRes.width,
  516. height: infoRes.height,
  517. url: imUrl,
  518. };
  519. let params={
  520. sourcePicture: picBaseInfo,
  521. bigPicture: picBaseInfo,
  522. snapshotPicture: picBaseInfo,
  523. sourcePath: ""
  524. };
  525. let message = await IMSDK.asyncApi(IMMethods.CreateImageMessageByURL,uuid,params);
  526. this.sendMessage(message);
  527. }
  528. });
  529. },
  530. fail: (err) => {
  531. console.error('获取图片信息失败', err);
  532. uni.showToast({
  533. title: '图片信息获取失败',
  534. icon: 'none'
  535. });
  536. }
  537. });
  538. },
  539. createAndSendVideoMessage(tempFilePath,uuid){
  540. let that=this;
  541. uni.uploadFile({
  542. url: 'https://api.cdwjyyh.com/app/common/uploadOSSIM', //仅为示例,非真实的接口地址
  543. //url: uni.getStorageSync('requestPath')+'/app/common/uploadOSSIM',
  544. filePath: tempFilePath,
  545. name: 'file',
  546. formData: {
  547. },
  548. success:async (res) => {
  549. console.log("qxj createAndSendVideoMessage");
  550. console.log(res.data);
  551. let data=JSON.parse(res.data);
  552. if(data.code==200){
  553. let url =data.url;
  554. let videoInfo=data.videoInfo;
  555. let params={
  556. duration: videoInfo.duration,
  557. videoSize:videoInfo.size,
  558. videoType: 'mp4',
  559. videoUUID: uuid,
  560. videoUrl: url,
  561. snapshotUrl: data.thumbnailUrl,
  562. snapshotUUID: IMSDK.uuid(),
  563. snapshotSize: videoInfo.size,
  564. snapshotWidth: videoInfo.width,
  565. snapshotHeight: videoInfo.height,
  566. snapShotType: 'png'
  567. };
  568. let videoMsg = await IMSDK.asyncApi(IMMethods.CreateVideoMessageByURL,uuid,params);
  569. this.sendMessage(videoMsg);
  570. }
  571. }
  572. });
  573. },
  574. createAndSendSoundMessage(tempFilePath,uuid,duration){
  575. let that=this;
  576. uni.uploadFile({
  577. url: 'https://api.cdwjyyh.com/app/common/uploadOSSIM', //仅为示例,非真实的接口地址
  578. filePath: tempFilePath,
  579. name: 'file',
  580. formData: {
  581. "uer":"im"
  582. },
  583. success:async (res) => {
  584. console.log("qxj createAndSendSoundMessage");
  585. console.log(res.data);
  586. let data=JSON.parse(res.data);
  587. if(data.code==200){
  588. let url =data.url;
  589. //let videoInfo=data.videoInfo;
  590. let params={
  591. duration: duration,
  592. sourceUrl:url,
  593. dataSize:1024,
  594. soundType: 'mp3',
  595. uuid: uuid,
  596. };
  597. let soundMsg = await IMSDK.asyncApi(IMMethods.CreateSoundMessageByURL,uuid,params);
  598. this.sendMessage(soundMsg);
  599. }
  600. }
  601. });
  602. },
  603. selectClick(data) {
  604. console.log("qxj selectClick :"+111);
  605. if (data.index === 0) {
  606. if (data.type === ChatingFooterActionTypes.Album) {
  607. this.chooseOrShotImage(['album']).then((paths) => this.batchCreateImageMesage(paths));
  608. } else {
  609. this.chooseOrShotImage(['camera']).then((paths) => this.batchCreateImageMesage(paths));
  610. }
  611. }
  612. else {
  613. console.log("qxj selectClick :"+111);
  614. const whenGetFile = (data) => {
  615. this.createAndSendVideoMessage(data.path,IMSDK.uuid());
  616. return;
  617. IMSDK.getVideoCover(purePath).then(async ({ path }) => {
  618. const message = await IMSDK.asyncApi(IMMethods.CreateVideoMessageFromFullPath, IMSDK.uuid(), {
  619. videoPath: purePath,
  620. videoType: data.videoType,
  621. duration: Number(data.duration.toFixed()),
  622. snapshotPath: getPurePath(path)
  623. });
  624. this.sendMessage(message);
  625. });
  626. };
  627. if (data.type === ChatingFooterActionTypes.Album) {
  628. this.chooseOrShotVideo(['album']).then(whenGetFile);
  629. } else {
  630. this.chooseOrShotVideo(['camera']).then(whenGetFile);
  631. }
  632. }
  633. },
  634. chooseOrShotImage(sourceType) {
  635. return new Promise((resolve, reject) => {
  636. uni.chooseImage({
  637. count: 9,
  638. sizeType: ['compressed'],
  639. sourceType,
  640. success: function ({ tempFilePaths }) {
  641. console.log("qxj tempFilePaths:"+tempFilePaths);
  642. resolve(tempFilePaths);
  643. },
  644. fail: function (err) {
  645. console.log(err);
  646. reject(err);
  647. }
  648. });
  649. });
  650. },
  651. chooseOrShotVideo(sourceType) {
  652. return new Promise((resolve, reject) => {
  653. uni.chooseVideo({
  654. compressed: true,
  655. sourceType,
  656. extension: ['mp4'],
  657. success: function ({ tempFilePath, duration }) {
  658. const idx = tempFilePath.lastIndexOf('.');
  659. const videoType = tempFilePath.slice(idx + 1);
  660. if (tempFilePath.includes('_doc/')) {
  661. tempFilePath = `file://${plus.io.convertLocalFileSystemURL(tempFilePath)}`;
  662. }
  663. console.log(tempFilePath);
  664. resolve({
  665. path: tempFilePath,
  666. videoType,
  667. duration
  668. });
  669. },
  670. fail: function (err) {
  671. console.log(err);
  672. reject(err);
  673. }
  674. });
  675. });
  676. },
  677. insertAt(userID, nickname) {
  678. const { atUsersInfo } = formatInputHtml(this.inputHtml);
  679. if (atUsersInfo.find((item) => item.atUserID === userID)) {
  680. return;
  681. }
  682. this.$refs.customEditor.createCanvasData(userID, nickname);
  683. },
  684. //im listener
  685. sendProgressHandler({ data: { progress, message } }) {
  686. this.updateOneMessage({
  687. message,
  688. type: UpdateMessageTypes.KeyWords,
  689. keyWords: {
  690. key: 'uploadProgress',
  691. value: progress
  692. }
  693. });
  694. },
  695. joinedGroupChangeHandler({ data }) {
  696. if (data.groupID === this.storeCurrentConversation.groupID) {
  697. IMSDK.asyncApi(IMMethods.IsJoinGroup, IMSDK.uuid(), data.groupID).then((res) => {
  698. this.isJoinGroup = res.data;
  699. });
  700. }
  701. },
  702. setIMListener() {
  703. //IMSDK.subscribe(IMSDK.IMEvents.SendMessageProgress, this.sendProgressHandler);
  704. uni.$on(IMSDK.IMEvents.OnJoinedGroupDeleted, this.joinedGroupChangeHandler);
  705. uni.$on(IMSDK.IMEvents.OnJoinedGroupAdded, this.joinedGroupChangeHandler);
  706. },
  707. disposeIMListener() {
  708. //IMSDK.unsubscribe(IMSDK.IMEvents.SendMessageProgress, this.sendProgressHandler);
  709. uni.$off(IMSDK.IMEvents.OnJoinedGroupDeleted, this.joinedGroupChangeHandler);
  710. uni.$off(IMSDK.IMEvents.OnJoinedGroupAdded, this.joinedGroupChangeHandler);
  711. },
  712. // keyboard
  713. keyboardChangeHander({ height }) {
  714. if (height > 0) {
  715. if (this.emojiBarVisible) {
  716. this.emojiBarVisible = false;
  717. }
  718. if (this.actionBarVisible) {
  719. this.actionBarVisible = false;
  720. }
  721. }
  722. },
  723. setKeyboardListener() {
  724. uni.onKeyboardHeightChange(this.keyboardChangeHander);
  725. },
  726. disposeKeyboardListener() {
  727. uni.offKeyboardHeightChange(this.keyboardChangeHander);
  728. },
  729. getVideoSnshot(item) {
  730. return new Promise((reslove, reject) => {
  731. var video = document.createElement("VIDEO");
  732. video.style.display = 'none';
  733. video.setAttribute('crossOrigin', 'anonymous');
  734. video.setAttribute("autoplay", "autoplay");
  735. video.setAttribute("muted", "muted");
  736. video.innerHTML = "<source src=" + item + ' type="audio/mp4">';
  737. var canvas = document.createElement("canvas");
  738. var ctx = canvas.getContext("2d");
  739. video.addEventListener("canplay", function() {
  740. var anw = document.createAttribute("width");
  741. anw.nodeValue = video.videoWidth;
  742. var anh = document.createAttribute("height");
  743. anh.nodeValue = video.videoHeight;
  744. canvas.setAttributeNode(anw);
  745. canvas.setAttributeNode(anh);
  746. ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
  747. var base64 = canvas.toDataURL("image/png");
  748. video.pause();
  749. reslove(base64);
  750. });
  751. });
  752. },
  753. }
  754. };
  755. </script>
  756. <script module="snap" lang="renderjs">
  757. export default {
  758. methods: {
  759. getSnapFlagUpdate(newValue, oldValue, ownerVm, vm) {
  760. if (newValue === null) {
  761. return;
  762. }
  763. const {path} = newValue
  764. this.getVideoSnshot(path).then(base64 => {
  765. ownerVm.callMethod('receiveSnapBase64', {
  766. ...newValue,
  767. base64
  768. })
  769. })
  770. },
  771. getVideoSnshot(item) {
  772. return new Promise((reslove, reject) => {
  773. var video = document.createElement("VIDEO");
  774. video.style.display = 'none';
  775. video.setAttribute('crossOrigin', 'anonymous');
  776. video.setAttribute("autoplay", "autoplay");
  777. video.setAttribute("muted", "muted");
  778. video.innerHTML = "<source src=" + item + ' type="audio/mp4">';
  779. var canvas = document.createElement("canvas");
  780. var ctx = canvas.getContext("2d");
  781. video.addEventListener("canplay", function() {
  782. var anw = document.createAttribute("width");
  783. anw.nodeValue = video.videoWidth;
  784. var anh = document.createAttribute("height");
  785. anh.nodeValue = video.videoHeight;
  786. canvas.setAttributeNode(anw);
  787. canvas.setAttributeNode(anh);
  788. ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
  789. var base64 = canvas.toDataURL("image/png");
  790. video.pause();
  791. reslove(base64);
  792. });
  793. });
  794. },
  795. },
  796. }
  797. </script>
  798. <style lang="scss" scoped>
  799. .custom_editor {
  800. img {
  801. vertical-align: sub;
  802. }
  803. }
  804. .forbidden_footer {
  805. width: 100%;
  806. height: 112rpx;
  807. color: #8e9ab0;
  808. display: flex;
  809. flex-direction: row;
  810. justify-content: center;
  811. align-items: center;
  812. background: #f0f2f6;
  813. }
  814. .chat_footer {
  815. display: flex;
  816. align-items: center;
  817. // background-color: #e9f4ff;
  818. background: #f0f2f6;
  819. // height: 50px;
  820. // max-height: 120px;
  821. padding: 24rpx 20rpx;
  822. font-size: 36rpx;
  823. .input_content {
  824. flex: 1;
  825. // min-height: 30px;
  826. // max-height: 120px;
  827. margin: 0 24rpx;
  828. border-radius: 8rpx;
  829. position: relative;
  830. .record_btn {
  831. // background-color: #3c9cff;
  832. background: #fff;
  833. color: black;
  834. height: 30px;
  835. line-height: 30px;
  836. font-size: 36rpx;
  837. }
  838. }
  839. .quote_message {
  840. @include vCenterBox();
  841. justify-content: space-between;
  842. margin-top: 12rpx;
  843. padding: 8rpx;
  844. // padding-top: 20rpx;
  845. border-radius: 6rpx;
  846. background-color: #fff;
  847. color: #666;
  848. .content {
  849. ::v-deep uni-view {
  850. @include ellipsisWithLine(2);
  851. }
  852. }
  853. }
  854. .footer_action_area {
  855. display: flex;
  856. align-items: center;
  857. .emoji_action {
  858. margin-right: 24rpx;
  859. }
  860. .btnSend{
  861. font-size: 36rpx;
  862. color: #fff;
  863. border-radius:6px;
  864. background-color:#1ab84d ;
  865. padding:20rpx 26rpx;
  866. }
  867. image {
  868. width: 26px;
  869. height: 26px;
  870. }
  871. }
  872. .send_btn {
  873. height: 30px;
  874. line-height: 30px;
  875. background-color: #4a9cfc;
  876. padding: 0 8px;
  877. border-radius: 4px;
  878. color: #fff;
  879. }
  880. }
  881. </style>