index.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <div
  3. :class="[
  4. 'tui-search-container',
  5. !isPC && 'tui-search-container-h5',
  6. isPC && `container-${props.popupPosition}`,
  7. `container-${props.searchType}`,
  8. ]"
  9. >
  10. <div
  11. :class="[
  12. isPC && `tui-search-container-${props.popupPosition}`,
  13. !isPC && 'tui-search-container-h5-main',
  14. ]"
  15. >
  16. <div
  17. v-if="props.searchType === 'conversation' && !isUniFrameWork"
  18. class="tui-search-header"
  19. >
  20. <div class="tui-search-header-title">
  21. {{ TUITranslateService.t("TUISearch.搜索会话内容") }}
  22. </div>
  23. <div
  24. class="tui-search-header-close"
  25. @click="closeSearchContainer"
  26. >
  27. <Icon
  28. :file="closeDarkIcon"
  29. width="14px"
  30. height="14px"
  31. />
  32. </div>
  33. </div>
  34. <div class="tui-search-tabs">
  35. <div
  36. v-for="(tabItem, tabKey) in searchTypeList"
  37. :key="tabKey"
  38. :class="[
  39. 'tui-search-tabs-item',
  40. currentSearchMessageType.key === tabItem.key && 'tui-search-tabs-item-selected',
  41. ]"
  42. @click="selectSearchType(tabItem)"
  43. >
  44. {{ TUITranslateService.t(`TUISearch.${tabItem.label}`) }}
  45. </div>
  46. </div>
  47. <!-- TUISearch search input slot -->
  48. <slot name="input" />
  49. <div
  50. v-if="isTimeTabsShow"
  51. class="tui-search-time"
  52. >
  53. <div
  54. v-for="(tabItem, tabKey) in searchMessageTimeList"
  55. :key="tabKey"
  56. :class="[
  57. 'tui-search-time-item',
  58. currentSearchMessageTime.key === tabItem.key && 'tui-search-time-item-selected',
  59. ]"
  60. @click="selectSearchTime(tabItem)"
  61. >
  62. <div
  63. v-if="tabItem.key === 'all'"
  64. class="tui-search-time-item-picker"
  65. >
  66. <div
  67. v-if="!isDatePickerShow"
  68. class="tui-search-time-item-all"
  69. @click.stop="handleSelectAllTimeClicked"
  70. >
  71. {{
  72. TUITranslateService.t(`TUISearch.选择时间`) +
  73. ": " +
  74. TUITranslateService.t(`TUISearch.全部`)
  75. }}
  76. <Icon
  77. :file="downArrowIcon"
  78. width="14px"
  79. height="14px"
  80. />
  81. </div>
  82. <div @click.stop>
  83. <DatePicker
  84. v-if="isDatePickerShow"
  85. type="range"
  86. :rangeTableType="datePickerRangeDisplayType"
  87. @pick="pickTimePeriod"
  88. />
  89. </div>
  90. <div
  91. v-if="isDatePickerShow"
  92. class="tui-search-time-item-close"
  93. @click="clearTimePicker"
  94. >
  95. <Icon
  96. class="icon"
  97. :file="closeIcon"
  98. width="14px"
  99. height="14px"
  100. />
  101. </div>
  102. </div>
  103. <div v-else>
  104. {{ TUITranslateService.t(`TUISearch.${tabItem.label}`) }}
  105. </div>
  106. </div>
  107. </div>
  108. <!-- TUISearch search result slot -->
  109. <slot name="result" />
  110. </div>
  111. </div>
  112. </template>
  113. <script setup lang="ts">
  114. import { ref, computed, onMounted, onUnmounted } from '../../../adapter-vue';
  115. import {
  116. TUITranslateService,
  117. TUIStore,
  118. StoreName,
  119. } from '@tencentcloud/chat-uikit-engine';
  120. import { Dayjs } from 'dayjs';
  121. import {
  122. globalSearchTypeList,
  123. conversationSearchTypeList,
  124. searchMessageTypeDefault,
  125. } from '../search-type-list';
  126. import { searchMessageTimeList, searchMessageTimeDefault } from '../search-time-list';
  127. import Icon from '../../common/Icon.vue';
  128. import DatePicker from '../../common/DatePicker/index.vue';
  129. import downArrowIcon from '../../../assets/icon/down-icon.svg';
  130. import closeIcon from '../../../assets/icon/input-close.svg';
  131. import closeDarkIcon from '../../../assets/icon/close-dark.svg';
  132. import { isPC, isUniFrameWork } from '../../../utils/env';
  133. import { SEARCH_TYPE, ISearchMessageTime, ISearchMessageType, ISearchTimeTab, ISearchTypeTab } from '../type';
  134. const props = defineProps({
  135. popupPosition: {
  136. type: String, // "bottom" / "aside"
  137. default: 'bottom',
  138. },
  139. searchType: {
  140. type: String,
  141. default: 'global', // "global" / "conversation"
  142. validator(value: string) {
  143. return ['global', 'conversation'].includes(value);
  144. },
  145. },
  146. });
  147. const emits = defineEmits(['searchConfigChange', 'closeInConversationSearch']);
  148. const searchTypeList = computed(() =>
  149. props?.searchType === 'conversation' ? conversationSearchTypeList : globalSearchTypeList,
  150. );
  151. const currentSearchMessageType = ref(searchMessageTypeDefault[props?.searchType as SEARCH_TYPE]);
  152. const currentSearchMessageTime = ref(searchMessageTimeDefault);
  153. const isTimeTabsShow = computed(() => {
  154. return (
  155. currentSearchMessageType.value.key !== 'contact'
  156. && currentSearchMessageType.value.key !== 'group'
  157. );
  158. });
  159. const datePickerRangeDisplayType = computed((): string =>
  160. isPC && props.searchType === 'global' && !isUniFrameWork ? 'two' : 'one',
  161. );
  162. const isDatePickerShow = ref<boolean>(false);
  163. function onCurrentSearchMessageTypeChange(typeObject: ISearchMessageType) {
  164. if (typeObject?.searchType === props?.searchType) {
  165. currentSearchMessageType.value
  166. = typeObject?.value || searchMessageTypeDefault[props?.searchType as SEARCH_TYPE];
  167. }
  168. }
  169. function onCurrentSearchMessageTimeChange(timeObject: ISearchMessageTime) {
  170. if (timeObject?.searchType === props?.searchType) {
  171. currentSearchMessageTime.value = timeObject?.value || searchMessageTimeDefault;
  172. }
  173. }
  174. onMounted(() => {
  175. TUIStore.watch(StoreName.SEARCH, {
  176. currentSearchMessageType: onCurrentSearchMessageTypeChange,
  177. currentSearchMessageTime: onCurrentSearchMessageTimeChange,
  178. });
  179. });
  180. onUnmounted(() => {
  181. TUIStore.unwatch(StoreName.SEARCH, {
  182. currentSearchMessageType: onCurrentSearchMessageTypeChange,
  183. currentSearchMessageTime: onCurrentSearchMessageTimeChange,
  184. });
  185. });
  186. const selectSearchType = (item: ISearchTypeTab) => {
  187. TUIStore.update(StoreName.SEARCH, 'currentSearchMessageType', {
  188. value: item,
  189. searchType: props.searchType,
  190. });
  191. };
  192. const selectSearchTime = (item: ISearchTimeTab) => {
  193. if (isDatePickerShow.value && item.key === 'all') {
  194. isDatePickerShow.value = false;
  195. } else {
  196. isDatePickerShow.value = false;
  197. TUIStore.update(StoreName.SEARCH, 'currentSearchMessageTime', {
  198. value: item,
  199. searchType: props.searchType,
  200. });
  201. }
  202. };
  203. const handleSelectAllTimeClicked = () => {
  204. if (currentSearchMessageTime.value?.key !== 'all') {
  205. TUIStore.update(StoreName.SEARCH, 'currentSearchMessageTime', {
  206. value: searchMessageTimeDefault,
  207. searchType: props.searchType,
  208. });
  209. } else {
  210. isDatePickerShow.value = true;
  211. }
  212. };
  213. const pickTimePeriod = (time: typeof Dayjs) => {
  214. if (currentSearchMessageTime.value?.key === 'all') {
  215. const { startDate, endDate } = time;
  216. const timePosition = Number((endDate?.toDate()?.getTime() / 1000).toFixed(0));
  217. const timePeriod = timePosition - Number((startDate?.toDate()?.getTime() / 1000).toFixed(0));
  218. const newSearchMessageTime = {
  219. key: currentSearchMessageTime.value.key,
  220. label: currentSearchMessageTime.value.label,
  221. value: {
  222. timePosition,
  223. timePeriod,
  224. },
  225. };
  226. TUIStore.update(StoreName.SEARCH, 'currentSearchMessageTime', {
  227. value: newSearchMessageTime,
  228. searchType: props.searchType,
  229. });
  230. }
  231. };
  232. const clearTimePicker = () => {
  233. isDatePickerShow.value = false;
  234. if (currentSearchMessageTime.value?.key === 'all') {
  235. TUIStore.update(StoreName.SEARCH, 'currentSearchMessageTime', {
  236. value: searchMessageTimeDefault,
  237. searchType: props.searchType,
  238. });
  239. }
  240. };
  241. const closeSearchContainer = () => {
  242. emits('closeInConversationSearch');
  243. };
  244. </script>
  245. <style lang="scss" scoped src="./style/index.scss"></style>