index.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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 :class="['tui-search-global-header', !isPC && 'tui-search-h5-global-header']">
  21. <SearchInput class="search-input" :searchType="searchType" />
  22. <!-- <SearchMore
  23. v-if="isPC || !searchingStatus"
  24. class="search-more"
  25. :searchType="searchType"
  26. /> -->
  27. </div>
  28. <SearchContainer
  29. v-if="searchingStatus"
  30. class="search-container"
  31. popupPosition="bottom"
  32. :searchType="searchType"
  33. >
  34. <template #result>
  35. <SearchResult class="search-result" :searchType="searchType" />
  36. </template>
  37. </SearchContainer>
  38. </div>
  39. <div
  40. v-else-if="
  41. (searchType === 'conversation' && isShowInConversationSearch) || isUniFrameWork
  42. "
  43. :class="['tui-search-conversation', !isPC && 'tui-search-h5-conversation']"
  44. >
  45. <SearchContainer
  46. class="search-container"
  47. popupPosition="aside"
  48. :searchType="searchType ? searchType : 'conversation'"
  49. @closeInConversationSearch="closeInConversationSearch"
  50. >
  51. <template #input>
  52. <SearchInput :searchType="searchType ? searchType : 'conversation'" />
  53. </template>
  54. <template #result>
  55. <SearchResult
  56. class="search-result"
  57. :searchType="searchType ? searchType : 'conversation'"
  58. />
  59. </template>
  60. </SearchContainer>
  61. </div>
  62. </div>
  63. </template>
  64. <script lang="ts" setup>
  65. import { ref, onMounted, computed, withDefaults, onUnmounted } from "../../adapter-vue";
  66. import { TUIStore, StoreName } from "@tencentcloud/chat-uikit-engine";
  67. import { TUIGlobal, outsideClick } from "@tencentcloud/universal-api";
  68. import SearchInput from "./search-input/index.vue";
  69. import SearchContainer from "./search-container/index.vue";
  70. import SearchResult from "./search-result/index.vue";
  71. import SearchMore from "./search-more/index.vue";
  72. import { searchMessageTypeDefault } from "./search-type-list";
  73. import { searchMessageTimeDefault } from "./search-time-list";
  74. import { isPC, isUniFrameWork } from "../../utils/env";
  75. import { ISearchingStatus, SEARCH_TYPE } from "./type";
  76. const props = withDefaults(
  77. defineProps<{
  78. searchType?: SEARCH_TYPE;
  79. }>(),
  80. {
  81. searchType: () => {
  82. return isUniFrameWork ? "conversation" : "global";
  83. },
  84. }
  85. );
  86. const globalSearchRef = ref<HTMLElement | null>();
  87. // 当前会话
  88. const currentConversationID = ref<string>("");
  89. // 控制搜索状态
  90. const searchingStatus = ref<boolean>(false);
  91. // 是否展示指定会话内搜索: 与 TUIChat 交互,由 TUIChat MessageInputToolBar 中 "查看历史消息ICON" 控制
  92. const isShowInConversationSearch = ref<boolean>(isUniFrameWork);
  93. // 是否全屏搜索 - 移动端正在搜索时全屏搜索
  94. const isFullScreen = computed(
  95. () =>
  96. !isPC &&
  97. ((props.searchType === "global" && searchingStatus.value) ||
  98. (props.searchType === "conversation" && isShowInConversationSearch.value))
  99. );
  100. const initSearchValue = (searchType: SEARCH_TYPE) => {
  101. TUIStore.update(StoreName.SEARCH, "currentSearchInputValue", {
  102. value: "",
  103. searchType: searchType,
  104. });
  105. TUIStore.update(StoreName.SEARCH, "currentSearchMessageType", {
  106. value: searchMessageTypeDefault[searchType],
  107. searchType: searchType,
  108. });
  109. TUIStore.update(StoreName.SEARCH, "currentSearchMessageTime", {
  110. value: searchMessageTimeDefault,
  111. searchType: searchType,
  112. });
  113. };
  114. function onCurrentConversationIDUpdate(conversationID: string) {
  115. if (!isUniFrameWork && currentConversationID.value !== conversationID) {
  116. // pc端 单页面 切换会话,关闭搜索
  117. closeInConversationSearch();
  118. }
  119. currentConversationID.value = conversationID;
  120. }
  121. function onCurrentSearchingStatusChange(value: ISearchingStatus) {
  122. if (value?.searchType === props.searchType) {
  123. searchingStatus.value = value?.isSearching;
  124. // global search ui bind on click outside close
  125. if (value?.searchType === "global" && globalSearchRef.value) {
  126. if (isPC && value.isSearching) {
  127. outsideClick.listen({
  128. domRefs: globalSearchRef.value,
  129. handler: closeGlobalSearch,
  130. });
  131. }
  132. }
  133. if (value?.searchType === "global" && isUniFrameWork) {
  134. // hide tab bar in uni-app when global searching
  135. value.isSearching
  136. ? TUIGlobal?.hideTabBar()?.catch(() => {
  137. /* ignore */
  138. })
  139. : TUIGlobal?.showTabBar()?.catch(() => {
  140. /* ignore */
  141. });
  142. }
  143. }
  144. }
  145. function onIsShowInConversationSearchChange(value: boolean) {
  146. isShowInConversationSearch.value = value ? true : false;
  147. isShowInConversationSearch.value && initSearchValue(props.searchType);
  148. }
  149. onMounted(() => {
  150. // init with default value
  151. ["global", "conversation"].forEach((type: string) => {
  152. initSearchValue(type as SEARCH_TYPE);
  153. });
  154. // watch store change
  155. TUIStore.watch(StoreName.CONV, {
  156. currentConversationID: onCurrentConversationIDUpdate,
  157. });
  158. TUIStore.watch(StoreName.SEARCH, {
  159. currentSearchingStatus: onCurrentSearchingStatusChange,
  160. isShowInConversationSearch: onIsShowInConversationSearchChange,
  161. });
  162. });
  163. onUnmounted(() => {
  164. // unwatch store change
  165. TUIStore.unwatch(StoreName.CONV, {
  166. currentConversationID: onCurrentConversationIDUpdate,
  167. });
  168. TUIStore.unwatch(StoreName.SEARCH, {
  169. currentSearchingStatus: onCurrentSearchingStatusChange,
  170. isShowInConversationSearch: onIsShowInConversationSearchChange,
  171. });
  172. });
  173. function closeGlobalSearch() {
  174. TUIStore.update(StoreName.SEARCH, "currentSearchingStatus", {
  175. isSearching: false,
  176. searchType: props.searchType,
  177. });
  178. }
  179. function closeInConversationSearch() {
  180. TUIStore.update(StoreName.SEARCH, "isShowInConversationSearch", false);
  181. }
  182. </script>
  183. <style lang="scss" scoped src="./style/index.scss"></style>