Search Store

最近更新时间:2026-07-01 16:34:31

我的收藏

概述

SearchStore 提供了基于云端搜索的搜索能力。如果自定义组件能力不能支持您的业务,可以使用 SearchStore 实现您的需求。

属性

属性名
类型
说明
userList
UserProfile[]
用户搜索结果列表。
userTotalCount
number
用户搜索结果总数。
hasMoreUsers
boolean
用户搜索结果是否还有下一页。
groupList
GroupSearchInfo[]
群组搜索结果列表。
groupTotalCount
number
群组搜索结果总数。
hasMoreGroups
boolean
群组搜索结果是否还有下一页。
groupMemberList
Record<string, GroupMember[]>
群成员搜索结果,按 groupID 分组。
groupMemberTotalCount
number
群成员搜索结果总数。
hasMoreGroupMembers
boolean
群成员搜索结果是否还有下一页。
messageResults
MessageSearchResultItem[]
消息搜索结果列表。每项按会话聚合,包含会话 ID、会话展示信息、命中消息数量和消息列表。
messageResultTotalCount
number
消息搜索结果总数。
hasMoreMessageResults
boolean
消息搜索结果是否还有下一页。

方法

方法名
类型
说明
search
(keywordList: string[], option?: SearchOption) => Promise<void>
发起搜索。默认会同时搜索用户、群组、群成员和消息;可通过 option.searchScope 限制搜索范围。调用时会重置当前结果和分页游标。
searchMore
(searchType: SearchType) => Promise<void>
加载指定搜索类型的下一页结果。searchType 可为 User、Group、GroupMember、Message。
destroy
() => void
销毁当前 SearchStore 实例,清理订阅和状态。通常由 React/Vue3 封装层自动管理。
SearchOption
字段名
类型
说明
keywordListMatchMode
KeywordListMatchMode
多关键词匹配模式:Or 或 And。默认 Or。
searchScope
SearchType[]
搜索范围。默认包含用户、群组、群成员、消息四类。
pageSize
number
每页数量,默认 20。
userFilter
UserSearchFilter
用户搜索过滤条件。
messageFilter
MessageSearchFilter
消息搜索过滤条件。
groupMemberFilter
GroupMemberSearchFilter
群成员搜索过滤条件。

使用示例

<script setup lang="ts">
import { ref } from 'vue';
import {
SearchType,
SearchStore,
} from '@tencentcloud/chat-uikit-vue3';

const {
hasMoreUsers,
search,
searchMore,
userList,
userTotalCount,
} = SearchStore.create();

const keyword = ref('');
const hasSearched = ref(false);
const isLoading = ref(false);

async function handleSearch() {
const nextKeyword = keyword.value.trim();
if (!nextKeyword) {
return;
}

isLoading.value = true;
hasSearched.value = true;
try {
await search([nextKeyword], {
pageSize: 20,
searchScope: [SearchType.User],
});
} finally {
isLoading.value = false;
}
}

async function handleLoadMore() {
isLoading.value = true;
try {
await searchMore(SearchType.User);
} finally {
isLoading.value = false;
}
}

function getAvatarText(value: string) {
return value.slice(0, 1).toUpperCase();
}
</script>

<template>
<div class="search-store-basic-demo">
<div class="search-store-basic-demo__header">
<h2>Search Store Basic</h2>
<p>Very simple user search powered by SearchStore.</p>
</div>

<div class="search-store-basic-demo__toolbar">
<input
v-model="keyword"
type="text"
placeholder="Enter user keyword"
@keyup.enter="handleSearch"
/>
<button
type="button"
:disabled="isLoading || !keyword.trim()"
@click="handleSearch"
>
{{ isLoading ? 'Searching...' : 'Search' }}
</button>
</div>

<div class="search-store-basic-demo__panel">
<h3>Users ({{ userTotalCount }})</h3>
<ul v-if="userList.length > 0" class="search-store-basic-demo__list">
<li
v-for="user in userList"
:key="user.userID"
class="search-store-basic-demo__item"
>
<span class="search-store-basic-demo__avatar">
{{ getAvatarText(user.nickname || user.userID) }}
</span>
<span>
<strong>{{ user.nickname || user.userID }}</strong>
<small>{{ user.userID }}</small>
</span>
</li>
</ul>
<div v-else class="search-store-basic-demo__empty">
{{ hasSearched ? 'No users found.' : 'Enter a keyword and search.' }}
</div>
</div>

<button
v-if="hasMoreUsers"
class="search-store-basic-demo__more"
type="button"
:disabled="isLoading"
@click="handleLoadMore"
>
Load More
</button>
</div>
</template>

<style scoped>
.search-store-basic-demo {
display: flex;
height: 100%;
flex-direction: column;
gap: 16px;
padding: 24px;
overflow: hidden;
}

.search-store-basic-demo__header h2 {
margin: 0 0 8px;
color: #111827;
font-size: 22px;
}

.search-store-basic-demo__header p {
margin: 0;
color: #6b7280;
font-size: 14px;
}

.search-store-basic-demo__toolbar {
display: flex;
gap: 8px;
}

.search-store-basic-demo__toolbar input {
width: 260px;
padding: 8px 10px;
border: 1px solid #d7dce5;
border-radius: 8px;
}

.search-store-basic-demo__toolbar button,
.search-store-basic-demo__more {
padding: 8px 12px;
border: 1px solid #667eea;
border-radius: 8px;
background: #667eea;
color: #fff;
cursor: pointer;
}

.search-store-basic-demo__toolbar button:disabled,
.search-store-basic-demo__more:disabled {
opacity: 0.6;
cursor: not-allowed;
}

.search-store-basic-demo__panel {
display: flex;
width: min(560px, 100%);
min-height: 0;
flex: 1;
flex-direction: column;
overflow: hidden;
border: 1px solid #edf0f5;
border-radius: 12px;
background: #fff;
}

.search-store-basic-demo__panel h3 {
margin: 0;
padding: 14px 16px;
border-bottom: 1px solid #edf0f5;
color: #111827;
font-size: 15px;
}

.search-store-basic-demo__list {
display: flex;
flex-direction: column;
gap: 8px;
margin: 0;
padding: 12px;
overflow: auto;
list-style: none;
}

.search-store-basic-demo__item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
border-radius: 10px;
background: #f9fafb;
}

.search-store-basic-demo__avatar {
display: inline-flex;
width: 32px;
height: 32px;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #eef2ff;
color: #4f46e5;
font-size: 13px;
font-weight: 700;
}

.search-store-basic-demo__item strong,
.search-store-basic-demo__item small {
display: block;
}

.search-store-basic-demo__item strong {
color: #111827;
font-size: 14px;
}

.search-store-basic-demo__item small {
margin-top: 2px;
color: #6b7280;
font-size: 12px;
}

.search-store-basic-demo__empty {
padding: 24px;
color: #6b7280;
text-align: center;
}

.search-store-basic-demo__more {
width: fit-content;
}

</style>