index.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. <template>
  2. <SearchResultLoading
  3. v-if="isLoading"
  4. :class="['search-result-loading', !isPC && 'search-result-loading-h5']"
  5. />
  6. <SearchResultDefault
  7. v-else-if="isSearchDefaultShow"
  8. :class="['search-result-default', !isPC && 'search-result-default-h5']"
  9. />
  10. <div
  11. v-else
  12. :class="[
  13. 'tui-search-result',
  14. !isPC && 'tui-search-result-h5',
  15. isPC && isResultDetailShow && 'tui-search-result-with-border',
  16. ]"
  17. >
  18. <div
  19. v-if="props.searchType !== 'conversation' && (isPC || !isResultDetailShow)"
  20. class="tui-search-result-main"
  21. >
  22. <div class="tui-search-result-list">
  23. <div
  24. v-for="result in searchResult"
  25. :key="result.key"
  26. class="tui-search-result-list-item"
  27. >
  28. <div v-if="props.searchType === 'global'" class="header">
  29. {{ TUITranslateService.t(`TUISearch.${result.label}`) }}
  30. </div>
  31. <div class="list">
  32. <div
  33. v-for="item in result.list"
  34. :key="item.conversation.conversationID"
  35. :class="[generateListItemClass(item)]"
  36. >
  37. <SearchResultItem
  38. v-if="
  39. result.key === 'contact' || result.key === 'group' || item.conversation
  40. "
  41. :listItem="item"
  42. :type="result.key"
  43. displayType="info"
  44. :keywordList="keywordList"
  45. @showResultDetail="showResultDetail"
  46. @navigateToChatPosition="navigateToChatPosition"
  47. />
  48. </div>
  49. </div>
  50. <div
  51. v-if="currentSearchTabKey === 'all' || result.cursor"
  52. class="more"
  53. @click="getMoreResult(result)"
  54. >
  55. <Icon class="more-icon" :file="searchIcon" width="12px" height="12px" />
  56. <div class="more-text">
  57. <span>{{ TUITranslateService.t("TUISearch.查看更多") }}</span>
  58. <span>{{ TUITranslateService.t(`TUISearch.${result.label}`) }}</span>
  59. </div>
  60. </div>
  61. </div>
  62. </div>
  63. </div>
  64. <div
  65. v-if="isResultDetailShow || props.searchType === 'conversation'"
  66. :class="[
  67. 'tui-search-result-detail',
  68. props.searchType === 'conversation' && 'tui-search-result-in-conversation',
  69. ]"
  70. >
  71. <SearchResultLoading
  72. v-if="isSearchDetailLoading"
  73. :class="['search-result-loading', !isPC && 'search-result-loading-h5']"
  74. />
  75. <div
  76. v-if="
  77. !isSearchDetailLoading &&
  78. isResultDetailShow &&
  79. props.searchType !== 'conversation'
  80. "
  81. class="tui-search-message-header"
  82. >
  83. <div class="header-content">
  84. <div class="header-content-count normal">
  85. <span>{{ searchConversationMessageTotalCount }}</span>
  86. <span>{{ TUITranslateService.t("TUISearch.条与") }}</span>
  87. </div>
  88. <div class="header-content-keyword">
  89. <span v-for="(keyword, index) in keywordList" :key="index">
  90. <span class="normal">"</span>
  91. <span class="highlight">{{ keyword }}</span>
  92. <span class="normal">"</span>
  93. </span>
  94. </div>
  95. <div class="header-content-type normal">
  96. <span>{{ TUITranslateService.t("TUISearch.相关的") }}</span>
  97. <span>{{
  98. TUITranslateService.t(
  99. `TUISearch.${
  100. currentSearchTabKey === "allMessage"
  101. ? "结果"
  102. : searchMessageTypeList[currentSearchTabKey].label
  103. }`
  104. )
  105. }}</span>
  106. </div>
  107. </div>
  108. <div
  109. class="header-enter"
  110. @click="enterConversation({ conversationID: currentSearchConversationID })"
  111. >
  112. <span>{{ TUITranslateService.t("TUISearch.进入聊天") }}</span>
  113. <Icon class="enter-icon" :file="enterIcon" width="14px" height="14px" />
  114. </div>
  115. </div>
  116. <div
  117. v-if="
  118. !isSearchDetailLoading &&
  119. searchConversationMessageList &&
  120. searchConversationMessageList[0]
  121. "
  122. class="tui-search-message-list"
  123. >
  124. <template
  125. v-if="
  126. props.searchType === 'global' ||
  127. (currentSearchTabKey !== 'imageMessage' &&
  128. currentSearchTabKey !== 'fileMessage')
  129. "
  130. >
  131. <div
  132. v-for="item in searchConversationMessageList"
  133. :key="generateVueRenderKey(item.ID)"
  134. :class="['list-item']"
  135. >
  136. <SearchResultItem
  137. :listItem="item"
  138. :listItemContent="item.getMessageContent()"
  139. :type="currentSearchTabKey"
  140. :displayType="generateResultItemDisplayType()"
  141. :keywordList="keywordList"
  142. @showResultDetail="showResultDetail"
  143. @navigateToChatPosition="navigateToChatPosition"
  144. />
  145. </div>
  146. </template>
  147. <!-- 会话内搜索-文件/图片视频类消息,需要按照时间合集展示 -->
  148. <template v-else>
  149. <div
  150. v-for="group in searchConversationResultGroupByDate"
  151. :key="generateVueRenderKey(group.date)"
  152. :class="[
  153. 'list-group',
  154. currentSearchTabKey === 'fileMessage'
  155. ? 'list-group-file'
  156. : 'list-group-image',
  157. ]"
  158. >
  159. <div :class="['list-group-date']">
  160. {{ group.date }}
  161. </div>
  162. <div
  163. v-for="item in group.list"
  164. :key="generateVueRenderKey(item.ID)"
  165. :class="['list-group-item']"
  166. >
  167. <SearchResultItem
  168. :listItem="item"
  169. :listItemContent="item.getMessageContent()"
  170. :type="currentSearchTabKey"
  171. :displayType="generateResultItemDisplayType()"
  172. :keywordList="keywordList"
  173. @showResultDetail="showResultDetail"
  174. @navigateToChatPosition="navigateToChatPosition"
  175. />
  176. </div>
  177. </div>
  178. </template>
  179. <div
  180. v-if="searchConversationResult && searchConversationResult.cursor"
  181. class="more"
  182. @click="getMoreResultInConversation"
  183. >
  184. <Icon class="more-icon" :file="searchIcon" width="12px" height="12px" />
  185. <div class="more-text">
  186. {{ TUITranslateService.t("TUISearch.查看更多历史记录") }}
  187. </div>
  188. </div>
  189. </div>
  190. </div>
  191. </div>
  192. </template>
  193. <script setup lang="ts">
  194. import { ref, watch, computed, onMounted, onUnmounted } from "../../../adapter-vue";
  195. import {
  196. TUITranslateService,
  197. TUIConversationService,
  198. TUIStore,
  199. StoreName,
  200. IMessageModel,
  201. SearchCloudMessagesParams,
  202. } from "@tencentcloud/chat-uikit-engine";
  203. import { TUIGlobal } from "@tencentcloud/universal-api";
  204. import SearchResultItem from "./search-result-item/index.vue";
  205. import SearchResultDefault from "./search-result-default/index.vue";
  206. import SearchResultLoading from "./search-result-loading/index.vue";
  207. import { searchMessageTypeList, searchMessageTypeDefault } from "../search-type-list";
  208. import Icon from "../../common/Icon.vue";
  209. import searchIcon from "../../../assets/icon/search.svg";
  210. import enterIcon from "../../../assets/icon/right-icon.svg";
  211. import {
  212. searchCloudMessages,
  213. enterConversation,
  214. generateSearchResultYMD,
  215. debounce,
  216. } from "../utils";
  217. import { enableSampleTaskStatus } from "../../../utils/enableSampleTaskStatus";
  218. import { isPC, isUniFrameWork } from "../../../utils/env";
  219. import {
  220. SEARCH_TYPE,
  221. ISearchInputValue,
  222. ISearchMessageType,
  223. ISearchMessageTime,
  224. } from "../type";
  225. import { ISearchCloudMessageResult, ISearchResultListItem } from "../../../interface";
  226. const props = defineProps({
  227. searchType: {
  228. type: String,
  229. default: "global", // "global":全局搜索, "conversation":会话内搜索
  230. validator(value: string) {
  231. return ["global", "conversation"].includes(value);
  232. },
  233. },
  234. });
  235. // search params
  236. const keywordList = ref<Array<string>>([]);
  237. const messageTypeList = ref<string | Array<string>>(
  238. searchMessageTypeDefault[props.searchType as SEARCH_TYPE]?.value as Array<string>
  239. );
  240. const timePosition = ref<number>(0);
  241. const timePeriod = ref<number>(0);
  242. // 通过空格分割整串输入后,按照“与”关系搜索
  243. // 比如: 输入"111 222", 搜索既有 111 又有 222 的消息, 同时也包含到严格搜索“111 222”的消息情况
  244. const keywordListMatchType = ref<string>("and");
  245. // current search tab key
  246. const currentSearchTabKey = ref<string>(
  247. searchMessageTypeDefault[props.searchType as SEARCH_TYPE]?.key
  248. );
  249. // search results all(所有会话搜索结果)
  250. const searchResult = ref<{
  251. [propsName: string]: {
  252. key: string;
  253. label: string;
  254. list: Array<ISearchResultListItem>;
  255. cursor: string | null;
  256. };
  257. }>({});
  258. const searchAllMessageList = ref<Array<ISearchResultListItem>>([]);
  259. const searchAllMessageTotalCount = ref<number>(0);
  260. // search results detail(具体某个会话的搜索结果)
  261. const currentSearchConversationID = ref<string>("");
  262. const searchConversationResult = ref<ISearchCloudMessageResult>();
  263. const searchConversationMessageList = ref<Array<IMessageModel>>([]);
  264. const searchConversationMessageTotalCount = ref<number>();
  265. // 文件消息/图片视频消息的搜索结果,按时间线分组
  266. const searchConversationResultGroupByDate = ref<
  267. Array<{ date: string; list: Array<IMessageModel> }>
  268. >([]);
  269. // ui display control
  270. const isResultDetailShow = ref<boolean>(false);
  271. const isLoading = ref<boolean>(false);
  272. const isSearchDetailLoading = ref<boolean>(false);
  273. const isSearchDefaultShow = computed((): boolean => {
  274. if (isLoading.value) {
  275. return false;
  276. }
  277. if (props.searchType === "global") {
  278. // 未搜索 / 有结果
  279. if (!keywordList?.value?.length || Object?.keys(searchResult.value)?.length) {
  280. return false;
  281. } else {
  282. return true;
  283. }
  284. } else {
  285. if (searchConversationMessageList?.value?.length) {
  286. return false;
  287. } else {
  288. return true;
  289. }
  290. }
  291. });
  292. function onCurrentConversationIDUpdate(id: string) {
  293. props.searchType === "conversation" && (currentSearchConversationID.value = id);
  294. }
  295. function onCurrentSearchInputValueUpdate(obj: ISearchInputValue) {
  296. if (obj?.searchType === props?.searchType) {
  297. // 根据空格模糊搜索结果
  298. keywordList.value = obj?.value ? obj.value.trim().split(/\s+/) : [];
  299. }
  300. }
  301. function onCurrentSearchMessageTypeUpdate(typeObject: ISearchMessageType) {
  302. if (typeObject?.searchType === props?.searchType) {
  303. currentSearchTabKey.value =
  304. typeObject?.value?.key ||
  305. searchMessageTypeDefault[props.searchType as SEARCH_TYPE]?.key;
  306. messageTypeList.value =
  307. typeObject?.value?.value ||
  308. searchMessageTypeDefault[props.searchType as SEARCH_TYPE]?.value;
  309. }
  310. }
  311. function onCurrentSearchMessageTimeUpdate(timeObject: ISearchMessageTime) {
  312. if (timeObject?.searchType === props?.searchType) {
  313. timePosition.value = timeObject?.value?.value?.timePosition;
  314. timePeriod.value = timeObject?.value?.value?.timePeriod;
  315. }
  316. }
  317. onMounted(() => {
  318. TUIStore.watch(StoreName.CONV, {
  319. currentConversationID: onCurrentConversationIDUpdate,
  320. });
  321. TUIStore.watch(StoreName.SEARCH, {
  322. currentSearchInputValue: onCurrentSearchInputValueUpdate,
  323. currentSearchMessageType: onCurrentSearchMessageTypeUpdate,
  324. currentSearchMessageTime: onCurrentSearchMessageTimeUpdate,
  325. });
  326. });
  327. onUnmounted(() => {
  328. TUIStore.unwatch(StoreName.CONV, {
  329. currentConversationID: onCurrentConversationIDUpdate,
  330. });
  331. TUIStore.unwatch(StoreName.SEARCH, {
  332. currentSearchInputValue: onCurrentSearchInputValueUpdate,
  333. currentSearchMessageType: onCurrentSearchMessageTypeUpdate,
  334. currentSearchMessageTime: onCurrentSearchMessageTimeUpdate,
  335. });
  336. });
  337. const setMessageSearchResultList = (option?: {
  338. conversationID?: string | undefined;
  339. cursor?: string | undefined;
  340. }) => {
  341. searchCloudMessages({
  342. keywordList: keywordList?.value?.length ? keywordList.value : undefined,
  343. messageTypeList:
  344. typeof messageTypeList.value === "string"
  345. ? [messageTypeList.value]
  346. : messageTypeList.value,
  347. timePosition: timePosition.value,
  348. timePeriod: timePeriod.value,
  349. conversationID: option?.conversationID || undefined,
  350. cursor: option?.cursor || undefined,
  351. keywordListMatchType: keywordListMatchType.value,
  352. } as SearchCloudMessagesParams).then((res: { data: ISearchCloudMessageResult }) => {
  353. enableSampleTaskStatus("searchCloudMessage");
  354. if (!option?.conversationID) {
  355. // 全部会话搜索结果
  356. option?.cursor
  357. ? (searchAllMessageList.value = [
  358. ...searchAllMessageList.value,
  359. ...res.data.searchResultList,
  360. ])
  361. : (searchAllMessageList.value = res?.data?.searchResultList);
  362. searchAllMessageTotalCount.value = res?.data?.totalCount;
  363. const key =
  364. currentSearchTabKey.value === "all" ? "allMessage" : currentSearchTabKey.value;
  365. if (
  366. searchAllMessageList?.value?.length &&
  367. currentSearchTabKey.value !== "contact" &&
  368. currentSearchTabKey.value !== "group"
  369. ) {
  370. searchResult.value = Object.assign({}, searchResult.value, {
  371. [key]: {
  372. key,
  373. label: searchMessageTypeList[key].label,
  374. list:
  375. currentSearchTabKey.value === "all"
  376. ? searchAllMessageList?.value?.slice(0, 3)
  377. : searchAllMessageList?.value,
  378. cursor: res?.data?.cursor || null,
  379. },
  380. });
  381. } else {
  382. delete searchResult?.value[key];
  383. }
  384. } else {
  385. // 指定会话搜索结果
  386. searchConversationResult.value = res?.data;
  387. option?.cursor
  388. ? (searchConversationMessageList.value = [
  389. ...searchConversationMessageList.value,
  390. ...(res?.data?.searchResultList[0]?.messageList as Array<IMessageModel>),
  391. ])
  392. : (searchConversationMessageList.value =
  393. res?.data?.searchResultList[0]?.messageList);
  394. // todo: 此处为 后台 totalCount 不准确的规避方案,待后台修复后请改为 res?.data?.totalCount
  395. searchConversationMessageTotalCount.value = res?.data?.totalCount;
  396. // 计算时间戳展示位置(仅会话内搜索 文件/图片 类型需要)
  397. if (
  398. props?.searchType === "conversation" &&
  399. (currentSearchTabKey.value === "fileMessage" ||
  400. currentSearchTabKey.value === "imageMessage")
  401. ) {
  402. searchConversationResultGroupByDate.value = groupResultListByDate(
  403. searchConversationMessageList.value
  404. );
  405. } else {
  406. searchConversationResultGroupByDate.value = [];
  407. }
  408. }
  409. isLoading.value = false;
  410. isSearchDetailLoading.value = false;
  411. });
  412. };
  413. const setMessageSearchResultListDebounce = debounce(setMessageSearchResultList, 500);
  414. const resetSearchResult = () => {
  415. searchResult.value = {};
  416. searchConversationResult.value = {} as ISearchCloudMessageResult;
  417. searchConversationMessageList.value = [];
  418. searchConversationResultGroupByDate.value = [];
  419. };
  420. watch(
  421. () => [
  422. keywordList.value,
  423. currentSearchTabKey.value,
  424. timePosition.value,
  425. timePeriod.value,
  426. ],
  427. (newValue, oldValue) => {
  428. if (newValue === oldValue) {
  429. return;
  430. }
  431. // 全局搜索必须有关键词,会话内搜索可以没有关键词
  432. if (!keywordList?.value?.length && props?.searchType === "global") {
  433. resetSearchResult();
  434. return;
  435. }
  436. isLoading.value = true;
  437. if (props.searchType === "conversation") {
  438. // 会话内搜索
  439. resetSearchResult();
  440. setMessageSearchResultList({
  441. conversationID: currentSearchConversationID.value,
  442. });
  443. } else {
  444. // 全局搜索
  445. if (oldValue && oldValue[1] === "all" && newValue && newValue[1] === "allMessage") {
  446. // 从【全部结果-聊天记录】分类中 点击某条结果,直接跳转到【聊天记录】tab,并且打开相关会话内搜索结果 情况, isResultDetailShow 保持为true
  447. // 不重新搜索
  448. searchResult?.value["allMessage"]?.list &&
  449. (searchResult.value["allMessage"].list = searchAllMessageList?.value);
  450. // 清除非聊天记录类型的其他搜索结果
  451. Object?.keys(searchResult?.value)?.forEach((key: string) => {
  452. if (key !== "allMessage") {
  453. delete searchResult?.value[key];
  454. }
  455. });
  456. isLoading.value = false;
  457. return;
  458. } else {
  459. // 其余情况,恢复 isResultDetailShow.value 为 false,并且重新搜索
  460. isResultDetailShow.value = false;
  461. resetSearchResult();
  462. }
  463. setMessageSearchResultListDebounce();
  464. }
  465. },
  466. { immediate: true }
  467. );
  468. const getMoreResult = (result: {
  469. key: string;
  470. label: string;
  471. list: Array<ISearchResultListItem>;
  472. cursor: string | null;
  473. }) => {
  474. if (currentSearchTabKey.value === "all") {
  475. // 此时查看更多:切换到相应结果对应的result,展示其类型全量搜索结果
  476. TUIStore.update(StoreName.SEARCH, "currentSearchMessageType", {
  477. value: searchMessageTypeList[result.key],
  478. searchType: props.searchType,
  479. });
  480. } else {
  481. // 在某一单类结果下查看更多:根据 cursor 作为搜索起始位置,拉取下一部分结果
  482. setMessageSearchResultList({ cursor: result?.cursor || undefined });
  483. }
  484. };
  485. const getMoreResultInConversation = () => {
  486. setMessageSearchResultList({
  487. cursor: searchConversationResult?.value?.cursor,
  488. conversationID: currentSearchConversationID?.value,
  489. });
  490. };
  491. const showResultDetail = (
  492. isShow: boolean,
  493. targetType?: string,
  494. targetResult?: IMessageModel | ISearchResultListItem
  495. ) => {
  496. isResultDetailShow.value = isShow;
  497. if (targetType) {
  498. TUIStore.update(StoreName.SEARCH, "currentSearchMessageType", {
  499. value: searchMessageTypeList[targetType],
  500. searchType: props.searchType,
  501. });
  502. }
  503. currentSearchConversationID.value =
  504. (targetResult as ISearchResultListItem)?.conversation?.conversationID || "";
  505. searchConversationMessageTotalCount.value = (targetResult as ISearchResultListItem)?.messageCount;
  506. if (targetResult) {
  507. isSearchDetailLoading.value = true;
  508. setMessageSearchResultListDebounce({
  509. conversationID: currentSearchConversationID.value,
  510. });
  511. }
  512. };
  513. const generateListItemClass = (item: ISearchResultListItem): Array<string> => {
  514. return currentSearchConversationID.value === item?.conversation?.conversationID
  515. ? ["list-item", "list-item-selected"]
  516. : ["list-item"];
  517. };
  518. const generateResultItemDisplayType = (): string => {
  519. if (
  520. props.searchType === "conversation" &&
  521. currentSearchTabKey.value === "fileMessage"
  522. ) {
  523. return "file";
  524. } else if (
  525. props.searchType === "conversation" &&
  526. currentSearchTabKey.value === "imageMessage"
  527. ) {
  528. return "image";
  529. } else if (isPC) {
  530. return "bubble";
  531. } else {
  532. return "info";
  533. }
  534. };
  535. const groupResultListByDate = (
  536. messageList: Array<IMessageModel>
  537. ): Array<{ date: string; list: Array<IMessageModel> }> => {
  538. const result: Array<{ date: string; list: Array<IMessageModel> }> = [];
  539. if (!messageList?.length) {
  540. return result;
  541. } else if (messageList?.length === 1) {
  542. result.push({
  543. date: generateSearchResultYMD(messageList[0]?.time),
  544. list: messageList,
  545. });
  546. return result;
  547. }
  548. let prevYMD = "";
  549. let currYMD = "";
  550. for (let i = 0; i < messageList?.length; i++) {
  551. currYMD = generateSearchResultYMD(messageList[i]?.time);
  552. if (prevYMD !== currYMD) {
  553. result.push({ date: currYMD, list: [messageList[i]] });
  554. } else {
  555. result[result?.length - 1]?.list?.push(messageList[i]);
  556. }
  557. prevYMD = currYMD;
  558. }
  559. return result;
  560. };
  561. const loginType = ref(uni.getStorageSync("loginType"));
  562. const navigateToChatPosition = (message: IMessageModel) => {
  563. let companyUserId =
  564. message.conversationID.indexOf("A") > -1
  565. ? message.conversationID.substring(4)
  566. : message.conversationID.substring(3);
  567. if (props.searchType === "global") {
  568. // 全局搜索,关闭 search container
  569. TUIStore.update(StoreName.SEARCH, "currentSearchingStatus", {
  570. isSearching: false,
  571. searchType: props.searchType,
  572. });
  573. // 切换会话
  574. TUIConversationService.switchConversation(message?.conversationID).then(() => {
  575. TUIStore.update(StoreName.CHAT, "messageSource", message);
  576. if (loginType.value == 1) {
  577. isUniFrameWork &&
  578. TUIGlobal?.navigateTo({
  579. url:
  580. "/TUIKit/components/TUIChat/index?companyUserId=" +
  581. uni.getStorageSync("userId") +
  582. "&recruitUserId=" +
  583. companyUserId,
  584. });
  585. } else {
  586. isUniFrameWork &&
  587. TUIGlobal?.navigateTo({
  588. url:
  589. "/TUIKit/components/TUIChat/index?companyUserId=" +
  590. companyUserId +
  591. "&recruitUserId=" +
  592. uni.getStorageSync("userId"),
  593. });
  594. }
  595. });
  596. } else if (props.searchType === "conversation") {
  597. // 会话内搜索,关闭 search container
  598. TUIStore.update(StoreName.SEARCH, "isShowInConversationSearch", false);
  599. TUIStore.update(StoreName.CHAT, "messageSource", message);
  600. isUniFrameWork && TUIGlobal?.navigateBack();
  601. }
  602. };
  603. const generateVueRenderKey = (value: string): string => {
  604. return `${currentSearchTabKey}-${value}`;
  605. };
  606. </script>
  607. <style lang="scss" scoped src="./style/index.scss"></style>