index.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <template>
  2. <div :class="['tui-contact-list-card', !isPC && 'tui-contact-list-card-h5']">
  3. <div class="tui-contact-list-card-left">
  4. <Avatar
  5. class="tui-contact-list-card-left-avatar"
  6. useSkeletonAnimation
  7. :url="generateAvatar(props.item)"
  8. />
  9. <div
  10. v-if="props.displayOnlineStatus && props.item"
  11. :class="{
  12. 'online-status': true,
  13. 'online-status-online': isOnline,
  14. 'online-status-offline': !isOnline,
  15. }"
  16. />
  17. </div>
  18. <div class="tui-contact-list-card-main">
  19. <div class="tui-contact-list-card-main-name">
  20. {{ generateName(props.item) }}
  21. </div>
  22. <div
  23. v-if="otherContentForSow"
  24. class="tui-contact-list-card-main-other"
  25. >
  26. {{ otherContentForSow }}
  27. </div>
  28. </div>
  29. <div class="tui-contact-list-card-right">
  30. <div
  31. v-if="groupTypeForShow"
  32. class="tui-contact-list-card-right-group-type"
  33. >
  34. {{ groupTypeForShow }}
  35. </div>
  36. <div
  37. v-if="showApplicationStatus"
  38. class="tui-contact-list-card-right-application"
  39. >
  40. <div
  41. v-if="showApplicationStatus.style === 'text'"
  42. class="tui-contact-list-card-right-application-text"
  43. >
  44. {{ TUITranslateService.t(`TUIContact.${showApplicationStatus.label}`) }}
  45. </div>
  46. <button
  47. v-else-if="showApplicationStatus.style === 'button'"
  48. class="tui-contact-list-card-right-application-button"
  49. @click.stop="showApplicationStatus.onClick"
  50. >
  51. {{ TUITranslateService.t(`TUIContact.${showApplicationStatus.label}`) }}
  52. </button>
  53. </div>
  54. </div>
  55. </div>
  56. </template>
  57. <script setup lang="ts">
  58. import { computed, withDefaults, inject, watch, ref, Ref } from '../../../../adapter-vue';
  59. import TUIChatEngine, {
  60. TUITranslateService,
  61. IGroupModel,
  62. FriendApplication,
  63. Friend,
  64. } from '@tencentcloud/chat-uikit-engine';
  65. import { IContactInfoType, IUserStatus } from '../../../../interface';
  66. import Avatar from '../../../common/Avatar/index.vue';
  67. import { generateAvatar, generateName, acceptFriendApplication } from '../../utils';
  68. import { isPC } from '../../../../utils/env';
  69. const props = withDefaults(
  70. defineProps<{
  71. item: IContactInfoType;
  72. displayOnlineStatus?: boolean;
  73. }>(),
  74. {
  75. item: () => ({} as IContactInfoType),
  76. displayOnlineStatus: false,
  77. },
  78. );
  79. const userOnlineStatusMap = inject<Ref<Map<string, IUserStatus>>>('userOnlineStatusMap');
  80. const isOnline = ref<boolean>(false);
  81. const groupType = {
  82. [TUIChatEngine.TYPES.GRP_WORK]: 'Work',
  83. [TUIChatEngine.TYPES.GRP_AVCHATROOM]: 'AVChatRoom',
  84. [TUIChatEngine.TYPES.GRP_PUBLIC]: 'Public',
  85. [TUIChatEngine.TYPES.GRP_MEETING]: 'Meeting',
  86. [TUIChatEngine.TYPES.GRP_COMMUNITY]: 'Community',
  87. };
  88. const otherContentForSow = computed((): string => {
  89. let content = '';
  90. if (
  91. (props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME
  92. || (props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME
  93. ) {
  94. content = (props.item as FriendApplication)?.wording || '';
  95. } else if ((props.item as IGroupModel)?.groupID) {
  96. content = `ID:${(props.item as IGroupModel)?.groupID}`;
  97. }
  98. return content;
  99. });
  100. const groupTypeForShow = computed((): string => {
  101. let type = '';
  102. if ((props.item as IGroupModel)?.groupID) {
  103. type = groupType[(props.item as IGroupModel)?.type];
  104. }
  105. return type;
  106. });
  107. const showApplicationStatus = computed(() => {
  108. if (
  109. (props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME
  110. ) {
  111. return {
  112. style: 'text',
  113. label: '等待验证',
  114. };
  115. } else if (
  116. (props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME
  117. ) {
  118. return {
  119. style: 'button',
  120. label: '同意',
  121. onClick: () => {
  122. acceptFriendApplication((props.item as FriendApplication)?.userID);
  123. },
  124. };
  125. }
  126. return false;
  127. });
  128. watch(
  129. () => userOnlineStatusMap?.value,
  130. () => {
  131. isOnline.value = getOnlineStatus();
  132. },
  133. {
  134. immediate: true,
  135. deep: true,
  136. },
  137. );
  138. function getOnlineStatus(): boolean {
  139. return !!(
  140. props.displayOnlineStatus
  141. && userOnlineStatusMap?.value
  142. && (props.item as Friend)?.userID
  143. && userOnlineStatusMap.value?.[(props.item as Friend).userID]?.statusType === TUIChatEngine.TYPES.USER_STATUS_ONLINE
  144. );
  145. }
  146. </script>
  147. <style lang="scss" scoped>
  148. .tui-contact-list-card {
  149. padding: 5px 0;
  150. display: flex;
  151. flex-direction: row;
  152. align-items: center;
  153. cursor: pointer;
  154. user-select: none;
  155. overflow: hidden;
  156. flex: 1;
  157. &-left {
  158. position: relative;
  159. width: 36px;
  160. height: 36px;
  161. &-avatar {
  162. width: 36px;
  163. height: 36px;
  164. border-radius: 5px;
  165. }
  166. .online-status {
  167. box-sizing: border-box;
  168. position: absolute;
  169. width: 10px;
  170. height: 10px;
  171. left: 30px;
  172. top: 30px;
  173. border: 2px solid #fff;
  174. box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
  175. border-radius: 50%;
  176. &-online {
  177. background: #29cc85;
  178. }
  179. &-offline {
  180. background: #a4a4a4;
  181. }
  182. }
  183. }
  184. &-main {
  185. flex: 1;
  186. padding: 0 10px;
  187. overflow: hidden;
  188. &-name,
  189. &-other {
  190. font-size: 14px;
  191. flex: 1;
  192. overflow: hidden;
  193. text-overflow: ellipsis;
  194. white-space: nowrap;
  195. }
  196. &-other {
  197. color: #999;
  198. }
  199. }
  200. &-right {
  201. width: fit-content;
  202. &-group-type {
  203. padding: 0 4px;
  204. line-height: 14px;
  205. font-size: 12px;
  206. border-radius: 1px;
  207. font-weight: 400;
  208. color: rgba(0, 0, 0, 0.3);
  209. border: 1px solid rgba(0, 0, 0, 0.3);
  210. }
  211. &-application {
  212. &-text {
  213. color: #999;
  214. font-size: 12px;
  215. }
  216. &-button {
  217. border: 1px solid #006eff;
  218. background: #006eff;
  219. color: #fff;
  220. padding: 3px 8px;
  221. border-radius: 4px;
  222. font-size: 12px;
  223. text-align: center;
  224. cursor: pointer;
  225. user-select: none;
  226. line-height: 150%;
  227. &::after {
  228. border: none;
  229. }
  230. }
  231. }
  232. }
  233. }
  234. .tui-contact-list-card-h5 {
  235. cursor: none !important;
  236. }
  237. </style>