index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <template>
  2. <div>
  3. <div>
  4. <div class="item" @click="person">
  5. <div>
  6. <img :src="lxr" class="img" />
  7. </div>
  8. <div class="title">新的联系人</div>
  9. </div>
  10. <div class="item" @click="chat">
  11. <div>
  12. <img :src="ql" class="img" />
  13. </div>
  14. <div class="title">我的群聊</div>
  15. </div>
  16. <div class="item" @click="Blacklist">
  17. <div>
  18. <img :src="hmd" class="img" />
  19. </div>
  20. <div class="title">黑名单</div>
  21. </div>
  22. <liu-indexed-list :dataList="dataList" @click="selectItem"></liu-indexed-list>
  23. </div>
  24. <liu-drag-button @clickBtn="clickBtn">
  25. <Icon :file="addSVG" :width="'14px'" :height="'14px'" />
  26. </liu-drag-button>
  27. <ul
  28. v-if="!contactSearchingStatus"
  29. :class="['tui-contact-list', !isPC && 'tui-contact-list-h5']"
  30. >
  31. <li
  32. v-for="(contactListObj, key) in contactListMap"
  33. :key="key"
  34. class="tui-contact-list-item"
  35. >
  36. <header
  37. class="tui-contact-list-item-header"
  38. @click="toggleCurrentContactList(key)"
  39. >
  40. <div class="tui-contact-list-item-header-left">
  41. <Icon
  42. :file="currentContactListKey === key ? downSVG : rightSVG"
  43. width="16px"
  44. height="16px"
  45. />
  46. <div>{{ TUITranslateService.t(`TUIContact.${contactListObj.title}`) }}</div>
  47. </div>
  48. <div class="tui-contact-list-item-header-right">
  49. <span
  50. v-if="contactListObj.unreadCount"
  51. class="tui-contact-list-item-header-right-unread"
  52. >
  53. {{ contactListObj.unreadCount }}
  54. </span>
  55. </div>
  56. </header>
  57. <ul
  58. :class="[
  59. 'tui-contact-list-item-main',
  60. currentContactListKey === key ? '' : 'hidden',
  61. ]"
  62. >
  63. <li
  64. v-for="contactListItem in contactListObj.list"
  65. :key="contactListItem.renderKey"
  66. class="tui-contact-list-item-main-item"
  67. :class="['selected']"
  68. @click="selectItem(contactListItem)"
  69. >
  70. <ContactListItem
  71. :item="contactListItem"
  72. :displayOnlineStatus="displayOnlineStatus && key === 'friendList'"
  73. />
  74. </li>
  75. </ul>
  76. </li>
  77. </ul>
  78. <ul v-else class="tui-contact-list">
  79. <li
  80. v-for="(item, key) in contactSearchResult"
  81. :key="key"
  82. class="tui-contact-list-item"
  83. >
  84. <div v-if="item.list[0]" class="tui-contact-search-list">
  85. <div class="tui-contact-search-list-title">
  86. {{ TUITranslateService.t(`TUIContact.${item.label}`) }}
  87. </div>
  88. <div
  89. v-for="(listItem, index) in item.list"
  90. :key="index"
  91. class="tui-contact-search-list-item"
  92. :class="['selected']"
  93. @click="selectItem(listItem)"
  94. >
  95. <ContactListItem :item="listItem" :displayOnlineStatus="false" />
  96. </div>
  97. </div>
  98. </li>
  99. <div v-if="isContactSearchNoResult" class="tui-contact-search-list-default">
  100. {{ TUITranslateService.t("TUIContact.无搜索结果") }}
  101. </div>
  102. </ul>
  103. </div>
  104. </template>
  105. <script setup lang="ts">
  106. import {
  107. TUITranslateService,
  108. TUIStore,
  109. StoreName,
  110. IGroupModel,
  111. TUIFriendService,
  112. Friend,
  113. FriendApplication,
  114. TUIUserService,
  115. } from "@tencentcloud/chat-uikit-engine";
  116. import TUICore, { TUIConstants } from "@tencentcloud/tui-core";
  117. import { ref, computed, onMounted, onUnmounted, provide } from "../../../adapter-vue";
  118. import Icon from "../../common/Icon.vue";
  119. import downSVG from "../../../assets/icon/down-icon.svg";
  120. import rightSVG from "../../../assets/icon/right-icon.svg";
  121. import addSVG from "../../../assets/icon/add.svg";
  122. import lxr from "../../../../static/img/lxr.png";
  123. import ql from "../../../../static/img/ql.png";
  124. import hmd from "../../../../static/img/hmd.png";
  125. import { TUIGlobal } from "@tencentcloud/universal-api";
  126. import {
  127. IContactList,
  128. IContactSearchResult,
  129. IBlackListUserItem,
  130. IUserStatus,
  131. IUserStatusMap,
  132. IContactInfoType,
  133. } from "../../../interface";
  134. import ContactListItem from "./contact-list-item/index.vue";
  135. import { isPC } from "../../../utils/env";
  136. const currentContactListKey = ref<keyof IContactList>("");
  137. const currentContactInfo = ref<IContactInfoType>({} as IContactInfoType);
  138. const dataList = ref([]);
  139. const contactListMap = ref<IContactList>({
  140. friendApplicationList: {
  141. key: "friendApplicationList",
  142. title: "新的联系人",
  143. list: [] as FriendApplication[],
  144. unreadCount: 0,
  145. },
  146. blackList: {
  147. key: "blackList",
  148. title: "黑名单",
  149. list: [] as IBlackListUserItem[],
  150. },
  151. groupList: {
  152. key: "groupList",
  153. title: "我的群聊",
  154. list: [] as IGroupModel[],
  155. },
  156. friendList: {
  157. key: "friendList",
  158. title: "我的好友",
  159. list: [] as Friend[],
  160. },
  161. });
  162. const contactSearchingStatus = ref<boolean>(false);
  163. const contactSearchResult = ref<IContactSearchResult>();
  164. const displayOnlineStatus = ref<boolean>(false);
  165. const userOnlineStatusMap = ref<IUserStatusMap>();
  166. const show = ref(false);
  167. const clickBtn = () => {
  168. // show.value = true;
  169. TUIGlobal?.navigateTo({
  170. url: "/pages/group/add",
  171. });
  172. };
  173. const isContactSearchNoResult = computed((): boolean => {
  174. return (
  175. !contactSearchResult?.value?.user?.list[0] &&
  176. !contactSearchResult?.value?.group?.list[0]
  177. );
  178. });
  179. const Blacklist = () => {
  180. TUIGlobal?.navigateTo({
  181. url: "/pages/group/black-list?list=" + JSON.stringify(contactListMap.value),
  182. });
  183. };
  184. const person = () => {
  185. TUIGlobal?.navigateTo({
  186. url: "/pages/group/person",
  187. });
  188. };
  189. const chat = () => {
  190. TUIGlobal?.navigateTo({
  191. url: "/pages/group/chat?list=" + JSON.stringify(contactListMap.value),
  192. });
  193. };
  194. onMounted(() => {
  195. TUIStore.watch(StoreName.APP, {
  196. enabledCustomerServicePlugin: onCustomerServiceCommercialPluginUpdated,
  197. });
  198. TUIStore.watch(StoreName.GRP, {
  199. groupList: onGroupListUpdated,
  200. });
  201. TUIStore.watch(StoreName.USER, {
  202. userBlacklist: onUserBlacklistUpdated,
  203. displayOnlineStatus: onDisplayOnlineStatusUpdated,
  204. userStatusList: onUserStatusListUpdated,
  205. });
  206. TUIStore.watch(StoreName.FRIEND, {
  207. friendList: onFriendListUpdated,
  208. friendApplicationList: onFriendApplicationListUpdated,
  209. friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated,
  210. });
  211. TUIStore.watch(StoreName.CUSTOM, {
  212. currentContactSearchingStatus: onCurrentContactSearchingStatusUpdated,
  213. currentContactSearchResult: onCurrentContactSearchResultUpdated,
  214. currentContactListKey: onCurrentContactListKeyUpdated,
  215. currentContactInfo: onCurrentContactInfoUpdated,
  216. });
  217. console.log(contactListMap.value);
  218. let list = [];
  219. contactListMap.value.friendList.list.forEach((e) => {
  220. list.push(e.profile);
  221. });
  222. dataList.value = list;
  223. console.log("dataList.value", contactListMap.value);
  224. });
  225. onUnmounted(() => {
  226. TUIStore.unwatch(StoreName.APP, {
  227. enabledCustomerServicePlugin: onCustomerServiceCommercialPluginUpdated,
  228. });
  229. TUIStore.unwatch(StoreName.GRP, {
  230. groupList: onGroupListUpdated,
  231. });
  232. TUIStore.unwatch(StoreName.USER, {
  233. userBlacklist: onUserBlacklistUpdated,
  234. displayOnlineStatus: onDisplayOnlineStatusUpdated,
  235. userStatusList: onUserStatusListUpdated,
  236. });
  237. TUIStore.unwatch(StoreName.FRIEND, {
  238. friendList: onFriendListUpdated,
  239. friendApplicationList: onFriendApplicationListUpdated,
  240. friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated,
  241. });
  242. TUIStore.unwatch(StoreName.CUSTOM, {
  243. currentContactSearchingStatus: onCurrentContactSearchingStatusUpdated,
  244. currentContactSearchResult: onCurrentContactSearchResultUpdated,
  245. currentContactListKey: onCurrentContactListKeyUpdated,
  246. currentContactInfo: onCurrentContactInfoUpdated,
  247. });
  248. });
  249. function toggleCurrentContactList(key: keyof IContactList) {
  250. if (currentContactListKey.value === key) {
  251. currentContactListKey.value = "";
  252. currentContactInfo.value = {} as IContactInfoType;
  253. TUIStore.update(StoreName.CUSTOM, "currentContactListKey", "");
  254. TUIStore.update(StoreName.CUSTOM, "currentContactInfo", {} as IContactInfoType);
  255. } else {
  256. currentContactListKey.value = key;
  257. TUIStore.update(StoreName.CUSTOM, "currentContactListKey", key);
  258. if (key === "friendApplicationList") {
  259. TUIFriendService.setFriendApplicationRead();
  260. }
  261. }
  262. }
  263. function selectItem(item: any) {
  264. currentContactInfo.value = item;
  265. // For a result in the search list, before viewing the contactInfo details,
  266. // it is necessary to update the data for the "already in the group list/already in the friend list" situation to obtain more detailed information
  267. if (contactSearchingStatus.value) {
  268. let targetListItem;
  269. if ((currentContactInfo.value as Friend)?.userID) {
  270. targetListItem = contactListMap.value?.friendList?.list?.find(
  271. (item: IContactInfoType) =>
  272. (item as Friend)?.userID === (currentContactInfo.value as Friend)?.userID
  273. );
  274. } else if ((currentContactInfo.value as IGroupModel)?.groupID) {
  275. targetListItem = contactListMap.value?.groupList?.list?.find(
  276. (item: IContactInfoType) =>
  277. (item as IGroupModel)?.groupID ===
  278. (currentContactInfo.value as IGroupModel)?.groupID
  279. );
  280. }
  281. if (targetListItem) {
  282. currentContactInfo.value = targetListItem;
  283. }
  284. }
  285. TUIStore.update(StoreName.CUSTOM, "currentContactInfo", currentContactInfo.value);
  286. }
  287. function onDisplayOnlineStatusUpdated(status: boolean) {
  288. displayOnlineStatus.value = status;
  289. }
  290. function onUserStatusListUpdated(list: Map<string, IUserStatus>) {
  291. if (list?.size > 0) {
  292. userOnlineStatusMap.value = Object.fromEntries(list?.entries());
  293. }
  294. }
  295. function onCustomerServiceCommercialPluginUpdated(isEnabled: boolean) {
  296. if (!isEnabled) {
  297. return;
  298. }
  299. // After the customer purchases the customer service plug-in,
  300. // the engine updates the enabledCustomerServicePlugin to true through the commercial capability bit.
  301. const contactListExtensionID = TUIConstants.TUIContact.EXTENSION.CONTACT_LIST.EXT_ID;
  302. const tuiContactExtensionList = TUICore.getExtensionList(contactListExtensionID);
  303. const customerData = tuiContactExtensionList.find((extension: any) => {
  304. const { name, accountList = [] } = extension.data || {};
  305. return name === "customer" && accountList.length > 0;
  306. });
  307. if (customerData) {
  308. const { data, text } = customerData;
  309. const { accountList } = (data || {}) as { accountList: string[] };
  310. TUIUserService.getUserProfile({ userIDList: accountList })
  311. .then((res) => {
  312. if (res.data.length > 0) {
  313. const customerList = {
  314. title: text,
  315. list: res.data.map((item: any, index: number) => {
  316. return {
  317. ...item,
  318. renderKey: generateRenderKey("customerList", item, index),
  319. infoKeyList: [],
  320. btnKeyList: ["enterC2CConversation"],
  321. };
  322. }),
  323. key: "customerList",
  324. };
  325. contactListMap.value = { ...contactListMap.value, customerList };
  326. }
  327. })
  328. .catch(() => {});
  329. }
  330. }
  331. function onGroupListUpdated(groupList: IGroupModel[]) {
  332. updateContactListMap("groupList", groupList);
  333. }
  334. function onUserBlacklistUpdated(userBlacklist: IBlackListUserItem[]) {
  335. updateContactListMap("blackList", userBlacklist);
  336. }
  337. function onFriendApplicationUnreadCountUpdated(friendApplicationUnreadCount: number) {
  338. contactListMap.value.friendApplicationList.unreadCount = friendApplicationUnreadCount;
  339. }
  340. function onFriendListUpdated(friendList: Friend[]) {
  341. updateContactListMap("friendList", friendList);
  342. }
  343. function onFriendApplicationListUpdated(friendApplicationList: FriendApplication[]) {
  344. updateContactListMap("friendApplicationList", friendApplicationList);
  345. }
  346. function updateContactListMap(key: keyof IContactList, list: IContactInfoType[]) {
  347. contactListMap.value[key].list = list;
  348. contactListMap.value[key].list.map(
  349. (item: IContactInfoType, index: number) =>
  350. (item.renderKey = generateRenderKey(key, item, index))
  351. );
  352. updateCurrentContactInfoFromList(contactListMap.value[key].list, key);
  353. }
  354. function updateCurrentContactInfoFromList(
  355. list: IContactInfoType[],
  356. type: keyof IContactList
  357. ) {
  358. if (
  359. !(currentContactInfo.value as Friend)?.userID &&
  360. !(currentContactInfo.value as IGroupModel)?.groupID
  361. ) {
  362. return;
  363. }
  364. if (type === currentContactListKey.value || contactSearchingStatus.value) {
  365. currentContactInfo.value =
  366. list?.find(
  367. (item: any) =>
  368. (item?.groupID &&
  369. item?.groupID === (currentContactInfo.value as IGroupModel)?.groupID) ||
  370. (item?.userID && item?.userID === (currentContactInfo.value as Friend)?.userID)
  371. ) || ({} as IContactInfoType);
  372. TUIStore.update(StoreName.CUSTOM, "currentContactInfo", currentContactInfo.value);
  373. }
  374. }
  375. function generateRenderKey(
  376. contactListMapKey: keyof IContactList,
  377. contactInfo: IContactInfoType,
  378. index: number
  379. ) {
  380. return `${contactListMapKey}-${
  381. (contactInfo as Friend).userID ||
  382. (contactInfo as IGroupModel).groupID ||
  383. "index" + index
  384. }`;
  385. }
  386. function onCurrentContactSearchResultUpdated(searchResult: IContactSearchResult) {
  387. contactSearchResult.value = searchResult;
  388. }
  389. function onCurrentContactSearchingStatusUpdated(searchingStatus: boolean) {
  390. contactSearchingStatus.value = searchingStatus;
  391. TUIStore.update(StoreName.CUSTOM, "currentContactInfo", {} as IContactInfoType);
  392. TUIStore.update(StoreName.CUSTOM, "currentContactListKey", "");
  393. }
  394. function onCurrentContactInfoUpdated(contactInfo: IContactInfoType) {
  395. currentContactInfo.value = contactInfo;
  396. }
  397. function onCurrentContactListKeyUpdated(contactListKey: string) {
  398. currentContactListKey.value = contactListKey;
  399. }
  400. provide("userOnlineStatusMap", userOnlineStatusMap);
  401. </script>
  402. <style lang="scss" scoped src="./style/index.scss"></style>