index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. <template>
  2. <view class="contact_choose_container">
  3. <!-- <u-modal
  4. :show="showConfirmModal"
  5. showCancelButton
  6. asyncClose
  7. @confirm="modalConfirm"
  8. @cancel="() => (showConfirmModal = false)"
  9. :content="`确定要发送${cardInfo.nickname}的名片吗?`"
  10. /> -->
  11. <custom-nav-bar title="联系人" />
  12. <view class="search_bar_wrap">
  13. <u-search shape="square" placeholder="搜索" :showAction="false" v-model="keyword" />
  14. </view>
  15. <view class="tab_container">
  16. <template v-if="activeTab === 0">
  17. <template v-for="item in tabs.slice(0, storeSelfInfo.members.length ? 3 : 2)">
  18. <setting-item v-if="!(!showGroup && item.idx === 2)" @click="tabChange(item.idx)" :key="item.idx" :title="item.title" :border="false" />
  19. </template>
  20. <view class="tab_pane" v-if="showConversation">
  21. <view class="title">
  22. <text style="margin-left: 36rpx">最近会话</text>
  23. </view>
  24. <u-list class="member_list">
  25. <u-list-item v-for="item in renderConversations" :key="item.conversationID">
  26. <user-item
  27. @updateCheck="updateCheckedUserOrGroup"
  28. :checked="checkedGroupIDList.includes(item.groupID) || checkedUserIDList.includes(item.userID)"
  29. :disabled="disabledGroupIDList.includes(item.groupID) || disabledUserIDList.includes(item.userID)"
  30. :item="item"
  31. :checkVisible="showCheck"
  32. />
  33. </u-list-item>
  34. </u-list>
  35. </view>
  36. <view class="tab_pane" v-else></view>
  37. </template>
  38. <template v-else>
  39. <view class="tab_pane" v-show="activeTab === 1">
  40. <choose-index-list
  41. @itemClick="userClick"
  42. @updateCheck="updateCheckedUser"
  43. :indexList="getChooseData.indexList"
  44. :itemArr="getChooseData.dataList"
  45. :checkedIDList="checkedUserIDList"
  46. :disabledIDList="disabledUserIDList"
  47. :showCheck="showCheck"
  48. />
  49. </view>
  50. <view class="tab_pane" v-show="activeTab === 2">
  51. <u-list class="member_list">
  52. <u-list-item v-for="item in renderGroupList" :key="item.groupID">
  53. <user-item
  54. @updateCheck="updateCheckedGroup"
  55. :checked="checkedGroupIDList.includes(item.groupID)"
  56. :disabled="disabledGroupIDList.includes(item.groupID)"
  57. :item="item"
  58. :checkVisible="showCheck"
  59. />
  60. </u-list-item>
  61. </u-list>
  62. </view>
  63. </template>
  64. </view>
  65. <choose-index-footer :comfirmLoading="comfirmLoading" v-if="showCheck" @removeItem="updateCheckedUserOrGroup" @confirm="confirm" :choosedData="getCheckedInfo" />
  66. </view>
  67. </template>
  68. <script>
  69. import { mapGetters } from 'vuex';
  70. import { ContactChooseTypes, PageEvents, UpdateMessageTypes, CustomType, FileMessageTypes } from '../../../constant';
  71. import { formatChooseData, toastWithCallback } from '../../../util/common';
  72. import { offlinePushInfo } from '../../../util/imCommon';
  73. import IMSDK, { GroupStatus, IMMethods, SessionType, MessageStatus } from 'openim-uniapp-polyfill';
  74. import CustomNavBar from '../../../components/CustomNavBar/index.vue';
  75. import UserItem from '../../../components/UserItem/index.vue';
  76. import ChooseIndexList from '../../../components/ChooseIndexList/index.vue';
  77. import ChooseIndexFooter from '../../../components/ChooseIndexFooter/index.vue';
  78. import SettingItem from '../../../components/SettingItem/index.vue';
  79. const showGroupTypes = [
  80. ContactChooseTypes.Mute,
  81. ContactChooseTypes.ForWard,
  82. ContactChooseTypes.BatchForWard,
  83. ContactChooseTypes.MergeForWard,
  84. ContactChooseTypes.GetListWithGroup,
  85. ContactChooseTypes.InviteMeeting,
  86. ContactChooseTypes.SendImage
  87. ];
  88. const showConversationTypes = [
  89. ContactChooseTypes.ForWard,
  90. ContactChooseTypes.BatchForWard,
  91. ContactChooseTypes.MergeForWard,
  92. ContactChooseTypes.ShareCard,
  93. ContactChooseTypes.InviteMeeting,
  94. ContactChooseTypes.SendImage
  95. ];
  96. export default {
  97. components: {
  98. CustomNavBar,
  99. UserItem,
  100. ChooseIndexList,
  101. ChooseIndexFooter,
  102. SettingItem
  103. },
  104. data() {
  105. return {
  106. keyword: '',
  107. type: ContactChooseTypes.Card,
  108. activeTab: 0,
  109. showConfirmModal: false,
  110. groupID: '',
  111. cardInfo: {},
  112. mergeInfo: {},
  113. forwardMessage: '',
  114. disabledUserIDList: [this.$store.getters.storeCurrentUserID],
  115. disabledGroupIDList: [],
  116. checkedUserInfoList: [],
  117. checkedGroupInfoList: [],
  118. originConversations: [],
  119. comfirmLoading: false,
  120. tabs: [
  121. {
  122. idx: 1,
  123. title: '我的好友'
  124. },
  125. {
  126. idx: 2,
  127. title: '我的群聊'
  128. },
  129. {
  130. idx: 3,
  131. title: '组织架构'
  132. }
  133. ]
  134. };
  135. },
  136. computed: {
  137. ...mapGetters([
  138. 'storeSelfInfo',
  139. 'storeFriendList',
  140. 'storeBlackList',
  141. 'storeGroupList',
  142. 'storeCurrentConversation',
  143. 'storeCurrentUserID',
  144. 'storeConversationList',
  145. 'storeCacheMap',
  146. 'timStore'
  147. ]),
  148. showCheck() {
  149. return this.type !== ContactChooseTypes.Card;
  150. },
  151. showGroup() {
  152. return showGroupTypes.includes(this.type);
  153. },
  154. showConversation() {
  155. return showConversationTypes.includes(this.type);
  156. },
  157. getChooseData() {
  158. if (this.keyword) {
  159. return {
  160. indexList: ['#'],
  161. dataList: [this.storeFriendList.filter((friend) => friend.nickname.includes(this.keyword) || friend.remark.includes(this.keyword))]
  162. };
  163. }
  164. return formatChooseData(this.storeFriendList);
  165. },
  166. renderGroupList() {
  167. return this.keyword ? this.storeGroupList.filter((group) => group.groupName.includes(this.keyword)) : this.storeGroupList;
  168. },
  169. renderConversations() {
  170. return this.keyword ? this.originConversations.filter((cve) => cve.showName.includes(this.keyword)) : this.originConversations;
  171. },
  172. checkedUserIDList() {
  173. return this.checkedUserInfoList.map((user) => user.userID);
  174. },
  175. checkedGroupIDList() {
  176. return this.checkedGroupInfoList.map((group) => group.groupID);
  177. },
  178. getCheckedInfo() {
  179. return [...this.checkedUserInfoList, ...this.checkedGroupInfoList];
  180. }
  181. },
  182. onLoad(options) {
  183. const { groupID, type, mergeInfo, cardInfo, forwardMessage, checkedUserInfoList, checkedGroupInfoList } = options;
  184. this.type = type;
  185. this.groupID = groupID;
  186. this.cardInfo = cardInfo ? JSON.parse(cardInfo) : {};
  187. this.mergeInfo = decodeURIComponent(mergeInfo);
  188. this.forwardMessage = decodeURIComponent(forwardMessage) || '';
  189. this.checkedUserInfoList = checkedUserInfoList ? JSON.parse(checkedUserInfoList) : [];
  190. this.checkedGroupInfoList = checkedGroupInfoList ? JSON.parse(checkedGroupInfoList) : [];
  191. this.originConversations = this.storeConversationList.filter((conversation) => conversation.conversationType !== SessionType.Notification);
  192. if (this.type === ContactChooseTypes.Invite) {
  193. this.checkJoinedGroupUser();
  194. }
  195. if (this.showConversation) {
  196. this.checkBlackUser();
  197. }
  198. if (showGroupTypes.includes(this.type)) {
  199. this.checkDisabledGroup();
  200. }
  201. },
  202. methods: {
  203. checkJoinedGroupUser() {
  204. const friendIDList = this.storeFriendList.map((friend) => friend.userID);
  205. IMSDK.asyncApi('getUsersInGroup', IMSDK.uuid(), {
  206. groupID: this.groupID,
  207. userIDList: friendIDList
  208. }).then(({ data }) => {
  209. this.disabledUserIDList = [...data, ...this.disabledUserIDList];
  210. });
  211. },
  212. checkBlackUser() {
  213. const blackIDList = this.storeBlackList.map((black) => black.userID);
  214. this.disabledUserIDList = [...this.disabledUserIDList, ...blackIDList];
  215. },
  216. checkDisabledGroup() {
  217. this.disabledGroupIDList = this.storeGroupList.filter((group) => group.status !== GroupStatus.Nomal).map((group) => group.groupID);
  218. },
  219. tabChange(idx) {
  220. this.keyword = '';
  221. if (idx === 3) {
  222. uni.$u.route('/pages/contact/orgnization/index', {
  223. title: this.$store.getters.storeOrganizationData.current.name,
  224. departmentID: '',
  225. checkedUserInfoList: JSON.stringify(this.checkedUserInfoList),
  226. checkedGroupInfoList: JSON.stringify(this.checkedGroupInfoList),
  227. disabledUserIDList: JSON.stringify(this.disabledUserIDList),
  228. groupID: this.groupID,
  229. chooseType: this.type
  230. });
  231. return;
  232. }
  233. this.activeTab = idx;
  234. },
  235. updateCheckedUserOrGroup(item) {
  236. if (item.userID) {
  237. this.updateCheckedUser(item);
  238. } else {
  239. this.updateCheckedGroup(item);
  240. }
  241. },
  242. updateCheckedUser(item) {
  243. const idx = this.checkedUserInfoList.findIndex((user) => user.userID === item.userID);
  244. if (idx > -1) {
  245. const tmpArr = [...this.checkedUserInfoList];
  246. tmpArr.splice(idx, 1);
  247. this.checkedUserInfoList = [...tmpArr];
  248. } else {
  249. this.checkedUserInfoList = [...this.checkedUserInfoList, item];
  250. }
  251. },
  252. updateCheckedGroup(item) {
  253. console.log('updateCheckedGroup', item);
  254. const idx = this.checkedGroupInfoList.findIndex((group) => group.groupID === item.groupID);
  255. if (idx > -1) {
  256. const tmpArr = [...this.checkedGroupInfoList];
  257. tmpArr.splice(idx, 1);
  258. this.checkedGroupInfoList = [...tmpArr];
  259. } else {
  260. this.checkedGroupInfoList = [...this.checkedGroupInfoList, item];
  261. }
  262. },
  263. userClick(item) {
  264. this.cardInfo = {
  265. ...item
  266. };
  267. this.showConfirmModal = true;
  268. },
  269. confirm() {
  270. if (this.activeTab) {
  271. this.activeTab = 0;
  272. return;
  273. }
  274. this.comfirmLoading = true;
  275. if (this.type === ContactChooseTypes.GetList || this.type === ContactChooseTypes.GetListWithGroup) {
  276. let pages = getCurrentPages();
  277. let prevPage = pages[pages.length - 2];
  278. let data = this.getCheckedInfo;
  279. if (this.type === ContactChooseTypes.GetListWithGroup) {
  280. data = [this.checkedUserInfoList, this.checkedGroupInfoList];
  281. }
  282. prevPage.$vm.getCheckedUsers(data);
  283. this.comfirmLoading = false;
  284. uni.navigateBack({
  285. delta: 1
  286. });
  287. return;
  288. }
  289. if (this.type === ContactChooseTypes.Invite) {
  290. IMSDK.asyncApi(IMSDK.IMMethods.InviteUserToGroup, IMSDK.uuid(), {
  291. groupID: this.groupID,
  292. reason: '',
  293. userIDList: this.getCheckedInfo.map((user) => user.userID)
  294. }).then(() => {
  295. toastWithCallback('操作成功', () => uni.navigateBack());
  296. this.comfirmLoading = false;
  297. })
  298. .catch(() => toastWithCallback('操作失败'));
  299. return;
  300. }
  301. this.getCheckedInfo.map(async (item) => {
  302. let message = {};
  303. let forwardMessage = {};
  304. if (this.type === ContactChooseTypes.SendImage) {
  305. message = JSON.parse(this.forwardMessage);
  306. }
  307. if (this.type === ContactChooseTypes.ForWard) {
  308. forwardMessage = JSON.parse(this.forwardMessage);
  309. message = await IMSDK.asyncApi(IMMethods.CreateForwardMessage, IMSDK.uuid(), forwardMessage);
  310. }
  311. if (this.type === ContactChooseTypes.InviteMeeting) {
  312. message = await IMSDK.asyncApi(IMMethods.CreateCustomMessage, IMSDK.uuid(), {
  313. data: JSON.stringify({
  314. customType: CustomType.MeetingInvitation,
  315. data: JSON.parse(this.forwardMessage)
  316. }),
  317. extension: '',
  318. description: 'meeting'
  319. });
  320. }
  321. if (this.type === ContactChooseTypes.ShareCard) {
  322. message = await IMSDK.asyncApi(IMMethods.CreateCardMessage, IMSDK.uuid(), this.cardInfo);
  323. }
  324. if (this.type === ContactChooseTypes.MergeForWard) {
  325. const { messageList, title, summaryList } = JSON.parse(this.mergeInfo);
  326. message = await IMSDK.asyncApi(IMMethods.CreateMergerMessage, IMSDK.uuid(), {
  327. messageList,
  328. title,
  329. summaryList
  330. });
  331. }
  332. if (this.type === ContactChooseTypes.BatchForWard) {
  333. const { messageList } = JSON.parse(this.mergeInfo);
  334. let arr = [];
  335. const promiseArray = messageList.map(async (item) => {
  336. const message = await IMSDK.asyncApi(IMMethods.CreateForwardMessage, IMSDK.uuid(), item);
  337. arr.push(message);
  338. });
  339. Promise.all(promiseArray)
  340. .then(() => {
  341. // console.log(arr);
  342. arr.map((message, idx) => {
  343. let Method=IMMethods.SendMessage;
  344. if(message.contentType==MessageType.PictureMessage || message.contentType==MessageType.VideoMessage || message.contentType==MessageType.VoiceMessage || message.contentType==MessageType.FileMessage){
  345. Method=IMMethods.SendMessageNotOss;
  346. }
  347. IMSDK.asyncApi(Method, IMSDK.uuid(), {
  348. recvID: item.userID,
  349. groupID: item.groupID,
  350. message,
  351. offlinePushInfo
  352. }).then(({ data }) => {
  353. if (FileMessageTypes.includes(data.contentType)) {
  354. const path = this.storeCacheMap[messageList[idx].clientMsgID].path;
  355. if (path) {
  356. this.$store.dispatch('user/addLocalPathToCache', {
  357. key: data.clientMsgID,
  358. path
  359. });
  360. }
  361. }
  362. if (!isInConversation) return;
  363. this.$store.dispatch('message/updateOneMessage', {
  364. message: data,
  365. isSuccess: true
  366. });
  367. })
  368. .catch(({ data, errCode, errMsg }) => {
  369. if (!isInConversation) return;
  370. this.$store.dispatch('message/updateOneMessage', {
  371. message: data,
  372. type: UpdateMessageTypes.KeyWords,
  373. keyWords: [
  374. {
  375. key: 'status',
  376. value: MessageStatus.Failed
  377. },
  378. {
  379. key: 'errCode',
  380. value: errCode
  381. }
  382. ]
  383. });
  384. });
  385. });
  386. })
  387. .catch((error) => {
  388. console.error('BatchForWard Error:', error);
  389. });
  390. return null;
  391. }
  392. const isInConversation = this.inCurrentConversation(item);
  393. if (isInConversation) {
  394. this.$store.dispatch('message/pushNewMessage', message);
  395. }
  396. let Method=IMMethods.SendMessage;
  397. if(message.contentType==MessageType.PictureMessage || message.contentType==MessageType.VideoMessage || message.contentType==MessageType.VoiceMessage || message.contentType==MessageType.FileMessage){
  398. Method=IMMethods.SendMessageNotOss;
  399. }
  400. IMSDK.asyncApi(Method, IMSDK.uuid(), {
  401. recvID: item.userID,
  402. groupID: item.groupID,
  403. message,
  404. offlinePushInfo
  405. }).then(({ data }) => {
  406. if (this.type === ContactChooseTypes.ForWard && FileMessageTypes.includes(data.contentType)) {
  407. const path = this.storeCacheMap[forwardMessage.clientMsgID].path;
  408. if (path) {
  409. this.$store.dispatch('user/addLocalPathToCache', {
  410. key: data.clientMsgID,
  411. path
  412. });
  413. }
  414. }
  415. if (!isInConversation) return;
  416. this.$store.dispatch('message/updateOneMessage', {
  417. message: data,
  418. isSuccess: true
  419. });
  420. }).catch(({ data, errCode, errMsg }) => {
  421. if (!isInConversation) return;
  422. this.$store.dispatch('message/updateOneMessage', {
  423. message: data,
  424. type: UpdateMessageTypes.KeyWords,
  425. keyWords: [
  426. {
  427. key: 'status',
  428. value: MessageStatus.Failed
  429. },
  430. {
  431. key: 'errCode',
  432. value: errCode
  433. }
  434. ]
  435. });
  436. });
  437. });
  438. uni.$emit(PageEvents.MutipleCheckUpdate, { flag: false });
  439. toastWithCallback('转发成功', () => uni.navigateBack());
  440. this.comfirmLoading = false;
  441. },
  442. orgConfirm(userList, groupList) {
  443. const userArr = [...userList];
  444. this.checkedGroupInfoList = [...groupList];
  445. this.checkedUserInfoList = userArr;
  446. console.log('orgConfirm', this.checkedUserInfoList);
  447. },
  448. async modalConfirm() {
  449. const message = await IMSDK.asyncApi(IMMethods.CreateCardMessage, IMSDK.uuid(), this.cardInfo);
  450. this.$store.dispatch('message/pushNewMessage', message);
  451. IMSDK.asyncApi(IMMethods.SendMessage, IMSDK.uuid(), {
  452. recvID: this.storeCurrentConversation.userID,
  453. groupID: this.storeCurrentConversation.groupID,
  454. message,
  455. offlinePushInfo
  456. })
  457. .then(({ data }) => {
  458. this.$store.dispatch('message/updateOneMessage', {
  459. message: data,
  460. isSuccess: true
  461. });
  462. })
  463. .catch(({ data, errCode, errMsg }) => {
  464. this.$store.dispatch('message/updateOneMessage', {
  465. message: data,
  466. type: UpdateMessageTypes.KeyWords,
  467. keyWords: [
  468. {
  469. key: 'status',
  470. value: MessageStatus.Failed
  471. },
  472. {
  473. key: 'errCode',
  474. value: errCode
  475. }
  476. ]
  477. });
  478. });
  479. // this.$nextTick(() => uni.$emit(PageEvents.ScrollToBottom, parsedMessage.clientMsgID))
  480. toastWithCallback('发送成功', () => {
  481. let pages = getCurrentPages();
  482. let prevPage = pages[pages.length - 2];
  483. prevPage.$vm.scrollToBottom();
  484. this.activeTab = 0;
  485. uni.navigateBack({
  486. delta: 1
  487. });
  488. });
  489. this.showConfirmModal = false;
  490. },
  491. inCurrentConversation(item) {
  492. if (item.userID) {
  493. return item.userID === this.storeCurrentConversation.userID;
  494. }
  495. return item.groupID === this.storeCurrentConversation.groupID;
  496. }
  497. },
  498. onBackPress() {
  499. if (this.activeTab) {
  500. this.activeTab = 0;
  501. return true;
  502. }
  503. return false;
  504. }
  505. };
  506. </script>
  507. <style lang="scss" scoped>
  508. ::v-deep .u-popup {
  509. flex: none;
  510. }
  511. .contact_choose_container {
  512. height: 100vh;
  513. display: flex;
  514. flex-direction: column;
  515. background: #fff;
  516. .search_bar_wrap {
  517. height: 34px;
  518. padding: 12px 22px;
  519. }
  520. .tab_container {
  521. @include colBox(false);
  522. flex: 1;
  523. overflow: hidden;
  524. margin-top: 20px;
  525. .setting_item {
  526. padding: 32rpx 36rpx;
  527. }
  528. .title {
  529. height: 60rpx;
  530. display: flex;
  531. justify-content: start;
  532. align-items: center;
  533. // padding: 16rpx 8rpx;
  534. background: #f8f9fa;
  535. color: #8e9ab0;
  536. font-size: 24rpx;
  537. }
  538. .title {
  539. padding: 16rpx 8rpx;
  540. background: #f8f9fa;
  541. color: #8e9ab0;
  542. font-size: 24rpx;
  543. }
  544. .tabs_bar {
  545. @include vCenterBox();
  546. justify-content: space-evenly;
  547. .tab_item {
  548. @include colBox(false);
  549. align-items: center;
  550. image {
  551. width: 50px;
  552. height: 50px;
  553. }
  554. }
  555. }
  556. .tab_pane {
  557. display: flex;
  558. flex-direction: column;
  559. flex: 1;
  560. overflow: hidden;
  561. .member_list {
  562. flex: 1;
  563. height: 80% !important;
  564. ::v-deep uni-scroll-view {
  565. max-height: 100% !important;
  566. }
  567. }
  568. .user_list {
  569. height: 100% !important;
  570. }
  571. }
  572. .member_anchor {
  573. background-color: #f8f8f8 !important;
  574. border: none !important;
  575. }
  576. .orgnization_row {
  577. border-top: 24px solid #f9f9fb;
  578. box-sizing: border-box;
  579. margin-top: 0;
  580. }
  581. }
  582. }
  583. </style>