index.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <template>
  2. <div
  3. v-if="
  4. searchType === 'global' ||
  5. ((searchType === 'conversation' || (!searchType && isUniFrameWork)) &&
  6. isShowInConversationSearch)
  7. "
  8. :class="[
  9. 'tui-search',
  10. !isPC && 'tui-search-h5',
  11. `tui-search-main-${searchType ? searchType : 'conversation'}`,
  12. isFullScreen && 'tui-search-h5-full-screen',
  13. ]"
  14. >
  15. <div
  16. v-if="searchType === 'global'"
  17. ref="globalSearchRef"
  18. :class="['tui-search-global', !isPC && 'tui-search-h5-global']"
  19. >
  20. <div
  21. :class="[
  22. 'tui-search-global-header',
  23. !isPC && 'tui-search-h5-global-header',
  24. ]"
  25. >
  26. <SearchInput
  27. class="search-input"
  28. :searchType="searchType"
  29. />
  30. <SearchMore
  31. v-if="isPC || !searchingStatus"
  32. class="search-more"
  33. :searchType="searchType"
  34. />
  35. </div>
  36. <SearchContainer
  37. v-if="searchingStatus"
  38. class="search-container"
  39. popupPosition="bottom"
  40. :searchType="searchType"
  41. >
  42. <template #result>
  43. <SearchResult
  44. class="search-result"
  45. :searchType="searchType"
  46. />
  47. </template>
  48. </SearchContainer>
  49. </div>
  50. <div
  51. v-else-if="
  52. (searchType === 'conversation' && isShowInConversationSearch) ||
  53. isUniFrameWork
  54. "
  55. :class="[
  56. 'tui-search-conversation',
  57. !isPC && 'tui-search-h5-conversation',
  58. ]"
  59. >
  60. <SearchContainer
  61. class="search-container"
  62. popupPosition="aside"
  63. :searchType="searchType ? searchType : 'conversation'"
  64. @closeInConversationSearch="closeInConversationSearch"
  65. >
  66. <template #input>
  67. <SearchInput
  68. :searchType="searchType ? searchType : 'conversation'"
  69. />
  70. </template>
  71. <template #result>
  72. <SearchResult
  73. class="search-result"
  74. :searchType="searchType ? searchType : 'conversation'"
  75. />
  76. </template>
  77. </SearchContainer>
  78. </div>
  79. </div>
  80. </template>
  81. <script lang="ts" setup>
  82. import {
  83. ref,
  84. onMounted,
  85. computed,
  86. withDefaults,
  87. onUnmounted,
  88. } from '../../adapter-vue';
  89. import { TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine';
  90. import { TUIGlobal, outsideClick } from '@tencentcloud/universal-api';
  91. import SearchInput from './search-input/index.vue';
  92. import SearchContainer from './search-container/index.vue';
  93. import SearchResult from './search-result/index.vue';
  94. import SearchMore from './search-more/index.vue';
  95. import { searchMessageTypeDefault } from './search-type-list';
  96. import { searchMessageTimeDefault } from './search-time-list';
  97. import { isPC, isUniFrameWork } from '../../utils/env';
  98. import { ISearchingStatus, SEARCH_TYPE } from './type';
  99. const props = withDefaults(
  100. defineProps<{
  101. searchType?: SEARCH_TYPE;
  102. }>(),
  103. {
  104. searchType: () => {
  105. return isUniFrameWork ? 'conversation' : 'global';
  106. },
  107. },
  108. );
  109. const globalSearchRef = ref<HTMLElement | null>();
  110. const currentConversationID = ref<string>('');
  111. const searchingStatus = ref<boolean>(false);
  112. // Whether to display the search in the chat
  113. const isShowInConversationSearch = ref<boolean>(isUniFrameWork);
  114. // Whether to search in full screen - Search in full screen when the mobile terminal is searching
  115. const isFullScreen = computed(
  116. () =>
  117. !isPC
  118. && ((props.searchType === 'global' && searchingStatus.value)
  119. || (props.searchType === 'conversation' && isShowInConversationSearch.value)),
  120. );
  121. const initSearchValue = (searchType: SEARCH_TYPE) => {
  122. TUIStore.update(StoreName.SEARCH, 'currentSearchInputValue', {
  123. value: '',
  124. searchType: searchType,
  125. });
  126. TUIStore.update(StoreName.SEARCH, 'currentSearchMessageType', {
  127. value: searchMessageTypeDefault[searchType],
  128. searchType: searchType,
  129. });
  130. TUIStore.update(StoreName.SEARCH, 'currentSearchMessageTime', {
  131. value: searchMessageTimeDefault,
  132. searchType: searchType,
  133. });
  134. };
  135. function onCurrentConversationIDUpdate(conversationID: string) {
  136. if (!isUniFrameWork && currentConversationID.value !== conversationID) {
  137. // PC side single page switch session, close search
  138. closeInConversationSearch();
  139. }
  140. currentConversationID.value = conversationID;
  141. }
  142. function onCurrentSearchingStatusChange(value: ISearchingStatus) {
  143. if (value?.searchType === props.searchType) {
  144. searchingStatus.value = value?.isSearching;
  145. // global search ui bind on click outside close
  146. if (value?.searchType === 'global' && globalSearchRef.value) {
  147. if (isPC && value.isSearching) {
  148. outsideClick.listen({
  149. domRefs: globalSearchRef.value,
  150. handler: closeGlobalSearch,
  151. });
  152. }
  153. }
  154. if (value?.searchType === 'global' && isUniFrameWork) {
  155. // hide tab bar in uni-app when global searching
  156. value.isSearching ? TUIGlobal?.hideTabBar()?.catch(() => { /* ignore */ }) : TUIGlobal?.showTabBar()?.catch(() => { /* ignore */ });
  157. }
  158. }
  159. }
  160. function onIsShowInConversationSearchChange(value: boolean) {
  161. isShowInConversationSearch.value = value ? true : false;
  162. isShowInConversationSearch.value && initSearchValue(props.searchType);
  163. }
  164. onMounted(() => {
  165. // init with default value
  166. ['global', 'conversation'].forEach((type: string) => {
  167. initSearchValue(type as SEARCH_TYPE);
  168. });
  169. // watch store change
  170. TUIStore.watch(StoreName.CONV, {
  171. currentConversationID: onCurrentConversationIDUpdate,
  172. });
  173. TUIStore.watch(StoreName.SEARCH, {
  174. currentSearchingStatus: onCurrentSearchingStatusChange,
  175. isShowInConversationSearch: onIsShowInConversationSearchChange,
  176. });
  177. });
  178. onUnmounted(() => {
  179. // unwatch store change
  180. TUIStore.unwatch(StoreName.CONV, {
  181. currentConversationID: onCurrentConversationIDUpdate,
  182. });
  183. TUIStore.unwatch(StoreName.SEARCH, {
  184. currentSearchingStatus: onCurrentSearchingStatusChange,
  185. isShowInConversationSearch: onIsShowInConversationSearchChange,
  186. });
  187. });
  188. function closeGlobalSearch() {
  189. TUIStore.update(StoreName.SEARCH, 'currentSearchingStatus', {
  190. isSearching: false,
  191. searchType: props.searchType,
  192. });
  193. }
  194. function closeInConversationSearch() {
  195. TUIStore.update(StoreName.SEARCH, 'isShowInConversationSearch', false);
  196. }
  197. </script>
  198. <style lang="scss" scoped src="./style/index.scss"></style>