feat: 统一列表模糊搜索行为
- 统一管理端和用户中心搜索参数及多字段包含匹配 - 修复聊天搜索竞态并优化部门重名路径展示 - 补充部门展开、模型空白词和聊天查询回归测试
This commit is contained in:
@@ -79,9 +79,7 @@ watchListRouteState(route, agentListRouteSchema, (state) => {
|
||||
pageSize: state.pageSize,
|
||||
queryParams: {
|
||||
categoryId: state.categoryId || undefined,
|
||||
description: state.keyword || undefined,
|
||||
isQueryOr: true,
|
||||
name: state.keyword || undefined,
|
||||
keyword: state.keyword || undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -187,9 +185,7 @@ function handleSearch(keyword: string) {
|
||||
searchKeyword.value = keyword;
|
||||
pageDataRef.value?.setQuery({
|
||||
categoryId: selectedCategoryId.value || undefined,
|
||||
isQueryOr: true,
|
||||
name: keyword || undefined,
|
||||
description: keyword || undefined,
|
||||
keyword: keyword || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -268,9 +264,7 @@ function changeCategory(category: any) {
|
||||
selectedCategoryId.value = categoryId;
|
||||
pageDataRef.value?.setQuery({
|
||||
categoryId: categoryId || undefined,
|
||||
isQueryOr: true,
|
||||
name: searchKeyword.value || undefined,
|
||||
description: searchKeyword.value || undefined,
|
||||
keyword: searchKeyword.value || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -290,9 +284,7 @@ async function loadCategories() {
|
||||
selectedCategoryId.value = '';
|
||||
initialCategoryChangePending = false;
|
||||
pageDataRef.value?.setQuery?.({
|
||||
isQueryOr: true,
|
||||
name: searchKeyword.value || undefined,
|
||||
description: searchKeyword.value || undefined,
|
||||
keyword: searchKeyword.value || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -452,9 +444,7 @@ async function handleDeleteAction(row: AgentInfo) {
|
||||
:initial-page-number="initialListState.pageNumber"
|
||||
:initial-query-params="{
|
||||
categoryId: initialListState.categoryId || undefined,
|
||||
isQueryOr: true,
|
||||
name: initialListState.keyword || undefined,
|
||||
description: initialListState.keyword || undefined,
|
||||
keyword: initialListState.keyword || undefined,
|
||||
}"
|
||||
@state-change="handlePageStateChange"
|
||||
>
|
||||
|
||||
@@ -100,8 +100,7 @@ watchListRouteState(route, botListRouteSchema, (state) => {
|
||||
pageSize: state.pageSize,
|
||||
queryParams: {
|
||||
categoryId: state.categoryId || undefined,
|
||||
isQueryOr: true,
|
||||
title: state.keyword || undefined,
|
||||
keyword: state.keyword || undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -310,8 +309,7 @@ const handleSearch = (params: string) => {
|
||||
searchKeyword.value = params;
|
||||
pageDataRef.value.setQuery({
|
||||
categoryId: selectedCategoryId.value || undefined,
|
||||
title: params || undefined,
|
||||
isQueryOr: true,
|
||||
keyword: params || undefined,
|
||||
});
|
||||
};
|
||||
const handleButtonClick = () => {
|
||||
@@ -395,8 +393,7 @@ function changeCategory(category: any) {
|
||||
selectedCategoryId.value = categoryId;
|
||||
pageDataRef.value.setQuery({
|
||||
categoryId: categoryId || undefined,
|
||||
title: searchKeyword.value || undefined,
|
||||
isQueryOr: true,
|
||||
keyword: searchKeyword.value || undefined,
|
||||
});
|
||||
}
|
||||
function showControlDialog(item: any) {
|
||||
@@ -471,8 +468,7 @@ const getSideList = async () => {
|
||||
selectedCategoryId.value = '';
|
||||
initialCategoryChangePending = false;
|
||||
pageDataRef.value?.setQuery?.({
|
||||
title: searchKeyword.value || undefined,
|
||||
isQueryOr: true,
|
||||
keyword: searchKeyword.value || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -538,8 +534,7 @@ function handlePageStateChange(state: {
|
||||
:initial-page-number="initialListState.pageNumber"
|
||||
:initial-query-params="{
|
||||
categoryId: initialListState.categoryId || undefined,
|
||||
title: initialListState.keyword || undefined,
|
||||
isQueryOr: true,
|
||||
keyword: initialListState.keyword || undefined,
|
||||
}"
|
||||
@state-change="handlePageStateChange"
|
||||
>
|
||||
|
||||
@@ -101,6 +101,7 @@ const loadingAssistants = ref(false);
|
||||
const loadingKnowledgeOptions = ref(false);
|
||||
const sending = ref(false);
|
||||
const sessionSearchKeyword = ref('');
|
||||
const appliedSessionSearchKeyword = ref('');
|
||||
const currentAssistantId = ref<string>();
|
||||
const currentAssistantDetail = ref<any>();
|
||||
const currentSession = ref<any>();
|
||||
@@ -122,6 +123,7 @@ const sessionContextMenuVisible = ref(false);
|
||||
const sessionContextMenuTarget = ref<null | SessionItem>(null);
|
||||
const sessionContextMenuX = ref(0);
|
||||
const sessionContextMenuY = ref(0);
|
||||
let latestSessionRequestId = 0;
|
||||
|
||||
const variantSwitchController = createChatVariantSwitchController<
|
||||
ChatTimeHistoryRecord,
|
||||
@@ -242,17 +244,7 @@ const extraKnowledgeOptionList = computed(() => {
|
||||
return knowledgeOptions.value.filter((item) => !excluded.has(String(item.value)));
|
||||
});
|
||||
const filteredSessions = computed(() => {
|
||||
const keyword = sessionSearchKeyword.value.trim().toLowerCase();
|
||||
if (!keyword) {
|
||||
return sessions.value;
|
||||
}
|
||||
return sessions.value.filter((session) => {
|
||||
return [
|
||||
resolveSessionLabel(session),
|
||||
session.lastMessagePreview,
|
||||
session.assistantName,
|
||||
].some((value) => String(value || '').toLowerCase().includes(keyword));
|
||||
});
|
||||
return sessions.value;
|
||||
});
|
||||
const groupedSessions = computed(() => {
|
||||
const today: SessionItem[] = [];
|
||||
@@ -432,14 +424,21 @@ async function fetchSessions(options: { append?: boolean } = {}) {
|
||||
loadingMoreSessions.value = true;
|
||||
} else {
|
||||
loadingSessions.value = true;
|
||||
loadingMoreSessions.value = false;
|
||||
}
|
||||
const requestId = ++latestSessionRequestId;
|
||||
const nextPageNumber = append ? sessionPage.value.pageNumber + 1 : 1;
|
||||
const requestKeyword = appliedSessionSearchKeyword.value;
|
||||
const [, res] = await tryit(api.get)('/api/v1/chatWorkspace/sessions', {
|
||||
params: {
|
||||
keyword: requestKeyword || undefined,
|
||||
pageNumber: nextPageNumber,
|
||||
pageSize: sessionPage.value.pageSize,
|
||||
},
|
||||
});
|
||||
if (requestId !== latestSessionRequestId) {
|
||||
return;
|
||||
}
|
||||
if (append) {
|
||||
loadingMoreSessions.value = false;
|
||||
} else {
|
||||
@@ -458,6 +457,13 @@ async function fetchSessions(options: { append?: boolean } = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSessionSearch() {
|
||||
const keyword = sessionSearchKeyword.value.trim();
|
||||
sessionSearchKeyword.value = keyword;
|
||||
appliedSessionSearchKeyword.value = keyword;
|
||||
void fetchSessions();
|
||||
}
|
||||
|
||||
function mergeFreshSessionRecords(records: SessionItem[]) {
|
||||
const freshIds = new Set(records.map((item) => String(item.sessionId)));
|
||||
const optimisticIds = new Set(optimisticSessionIds.value);
|
||||
@@ -1581,6 +1587,8 @@ async function deleteSession(targetSession?: SessionItem) {
|
||||
clearable
|
||||
:prefix-icon="Search"
|
||||
placeholder="搜索会话"
|
||||
@clear="handleSessionSearch"
|
||||
@keyup.enter="handleSessionSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1661,7 +1669,7 @@ async function deleteSession(targetSession?: SessionItem) {
|
||||
class="chat-workspace__empty"
|
||||
/>
|
||||
<ElEmpty
|
||||
v-else-if="sessionSearchKeyword"
|
||||
v-else-if="appliedSessionSearchKeyword"
|
||||
description="未找到匹配会话"
|
||||
class="chat-workspace__empty"
|
||||
/>
|
||||
|
||||
@@ -405,9 +405,11 @@ function closeDetail() {
|
||||
<ElInput
|
||||
v-if="isSuperAdmin"
|
||||
v-model="query.userAccount"
|
||||
clearable
|
||||
class="chat-history-page__filter-control is-input"
|
||||
placeholder="搜索聊天用户"
|
||||
placeholder="搜索用户账号"
|
||||
:prefix-icon="Search"
|
||||
@clear="handleSearch"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -91,8 +91,7 @@ watchListRouteState(route, documentCollectionListRouteSchema, (state) => {
|
||||
pageSize: state.pageSize,
|
||||
queryParams: {
|
||||
categoryId: state.categoryId || undefined,
|
||||
isQueryOr: true,
|
||||
title: state.keyword || undefined,
|
||||
keyword: state.keyword || undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -590,8 +589,7 @@ const handleSearch = (params: any) => {
|
||||
searchKeyword.value = String(params || '');
|
||||
pageDataRef.value.setQuery({
|
||||
categoryId: selectedCategoryId.value || undefined,
|
||||
title: searchKeyword.value || undefined,
|
||||
isQueryOr: true,
|
||||
keyword: searchKeyword.value || undefined,
|
||||
});
|
||||
};
|
||||
const reloadKnowledgeList = () => {
|
||||
@@ -631,8 +629,7 @@ const getCategoryList = async () => {
|
||||
selectedCategoryId.value = '';
|
||||
initialCategoryChangePending = false;
|
||||
pageDataRef.value?.setQuery?.({
|
||||
title: searchKeyword.value || undefined,
|
||||
isQueryOr: true,
|
||||
keyword: searchKeyword.value || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -763,8 +760,7 @@ function changeCategory(category: any) {
|
||||
selectedCategoryId.value = categoryId;
|
||||
pageDataRef.value.setQuery({
|
||||
categoryId: categoryId || undefined,
|
||||
title: searchKeyword.value || undefined,
|
||||
isQueryOr: true,
|
||||
keyword: searchKeyword.value || undefined,
|
||||
});
|
||||
}
|
||||
function handlePageStateChange(state: {
|
||||
@@ -822,8 +818,7 @@ function handlePageStateChange(state: {
|
||||
:initial-page-number="initialListState.pageNumber"
|
||||
:initial-query-params="{
|
||||
categoryId: initialListState.categoryId || undefined,
|
||||
title: initialListState.keyword || undefined,
|
||||
isQueryOr: true,
|
||||
keyword: initialListState.keyword || undefined,
|
||||
}"
|
||||
@state-change="handlePageStateChange"
|
||||
>
|
||||
|
||||
@@ -100,7 +100,7 @@ defineExpose({
|
||||
},
|
||||
search(searchText: string) {
|
||||
pageDataRef.value?.setQuery?.({
|
||||
title: searchText,
|
||||
keyword: searchText.trim() || undefined,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -91,7 +91,7 @@ const treeData = computed(() => [
|
||||
const refreshList = () => {
|
||||
const query: Record<string, any> = {};
|
||||
if (searchKeyword.value.trim()) {
|
||||
query.question = searchKeyword.value.trim();
|
||||
query.keyword = searchKeyword.value.trim();
|
||||
}
|
||||
if (selectedCategoryId.value !== 'all') {
|
||||
query.categoryId = selectedCategoryId.value;
|
||||
|
||||
@@ -117,7 +117,7 @@ const headerButtons = [
|
||||
},
|
||||
];
|
||||
const handleSearch = (params: string) => {
|
||||
pageDataRef.value.setQuery({ packageName: params, isQueryOr: true });
|
||||
pageDataRef.value.setQuery({ keyword: params || undefined });
|
||||
};
|
||||
const handleHeaderButtonClick = (button: any) => {
|
||||
if (button.key === 'create') {
|
||||
|
||||
@@ -6,26 +6,23 @@ import {
|
||||
} from './plugin-query';
|
||||
|
||||
describe('plugin query params', () => {
|
||||
it('uses the persisted name field and an explicit like operator', () => {
|
||||
it('uses the unified keyword for plugin name and description search', () => {
|
||||
expect(buildPluginPageQueryParams('7', ' 1213 ')).toEqual({
|
||||
category: '7',
|
||||
name: '1213',
|
||||
name__op: 'like',
|
||||
keyword: '1213',
|
||||
});
|
||||
});
|
||||
|
||||
it('retains category while clearing the keyword', () => {
|
||||
expect(buildPluginPageQueryParams('7', ' ')).toEqual({
|
||||
category: '7',
|
||||
name: undefined,
|
||||
name__op: undefined,
|
||||
keyword: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('uses contains matching for plugin tool names', () => {
|
||||
it('uses the unified keyword for plugin tool name and description search', () => {
|
||||
expect(buildPluginToolPageQueryParams(' HTTP ')).toEqual({
|
||||
name: 'HTTP',
|
||||
name__op: 'like',
|
||||
keyword: 'HTTP',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,19 +3,17 @@ function normalizeKeyword(keyword?: string): string {
|
||||
}
|
||||
|
||||
function buildPluginPageQueryParams(categoryId: string, keyword?: string) {
|
||||
const name = normalizeKeyword(keyword);
|
||||
const normalizedKeyword = normalizeKeyword(keyword);
|
||||
return {
|
||||
category: categoryId || '0',
|
||||
name: name || undefined,
|
||||
name__op: name ? 'like' : undefined,
|
||||
keyword: normalizedKeyword || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPluginToolPageQueryParams(keyword?: string) {
|
||||
const name = normalizeKeyword(keyword);
|
||||
const normalizedKeyword = normalizeKeyword(keyword);
|
||||
return {
|
||||
name: name || undefined,
|
||||
name__op: name ? 'like' : undefined,
|
||||
keyword: normalizedKeyword || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ const formRef = ref<FormInstance>();
|
||||
const pageDataRef = ref();
|
||||
const saveDialog = ref();
|
||||
const previewDialog = ref();
|
||||
const selectedCategoryId = ref('');
|
||||
const formInline = ref({
|
||||
resourceName: '',
|
||||
resourceType: '',
|
||||
@@ -68,13 +69,19 @@ function initDict() {
|
||||
function search(formEl: FormInstance | undefined) {
|
||||
formEl?.validate((valid) => {
|
||||
if (valid) {
|
||||
pageDataRef.value.setQuery(formInline.value);
|
||||
pageDataRef.value.setQuery({
|
||||
categoryId: selectedCategoryId.value || undefined,
|
||||
keyword: formInline.value.resourceName.trim() || undefined,
|
||||
resourceType: formInline.value.resourceType || undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function reset(formEl: FormInstance | undefined) {
|
||||
formEl?.resetFields();
|
||||
pageDataRef.value.setQuery({});
|
||||
pageDataRef.value.setQuery({
|
||||
categoryId: selectedCategoryId.value || undefined,
|
||||
});
|
||||
}
|
||||
function showDialog(row: any) {
|
||||
saveDialog.value.openDialog({ ...row });
|
||||
@@ -177,7 +184,12 @@ const sideFormRules = computed(() => {
|
||||
const sideSaveLoading = ref(false);
|
||||
|
||||
function changeCategory(category: any) {
|
||||
pageDataRef.value.setQuery({ categoryId: category.id });
|
||||
selectedCategoryId.value = String(category.id || '');
|
||||
pageDataRef.value.setQuery({
|
||||
categoryId: selectedCategoryId.value || undefined,
|
||||
keyword: formInline.value.resourceName.trim() || undefined,
|
||||
resourceType: formInline.value.resourceType || undefined,
|
||||
});
|
||||
}
|
||||
function showControlDialog(item: any) {
|
||||
sideFormRef.value?.resetFields();
|
||||
|
||||
@@ -310,7 +310,7 @@ const currentPageSize = ref(initialListState.pageSize);
|
||||
const suppressInitialCategoryChange = ref(Boolean(initialListState.categoryId));
|
||||
const initialQueryParams = {
|
||||
categoryId: initialListState.categoryId || undefined,
|
||||
title: initialListState.keyword || undefined,
|
||||
keyword: initialListState.keyword || undefined,
|
||||
};
|
||||
watchListRouteState(route, workflowListRouteSchema, (state) => {
|
||||
if (String(state.categoryId) !== String(selectedCategoryId.value)) {
|
||||
@@ -325,7 +325,7 @@ watchListRouteState(route, workflowListRouteSchema, (state) => {
|
||||
pageSize: state.pageSize,
|
||||
queryParams: {
|
||||
categoryId: state.categoryId || undefined,
|
||||
title: state.keyword || undefined,
|
||||
keyword: state.keyword || undefined,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -401,7 +401,7 @@ async function updateVisibilityScope(
|
||||
function applyFilters() {
|
||||
pageDataRef.value?.setQuery({
|
||||
categoryId: selectedCategoryId.value || undefined,
|
||||
title: searchKeyword.value || undefined,
|
||||
keyword: searchKeyword.value || undefined,
|
||||
});
|
||||
}
|
||||
function currentListState(): WorkflowListRouteState {
|
||||
|
||||
@@ -66,7 +66,7 @@ watchListRouteState($route, workflowExecListRouteSchema, (state) => {
|
||||
const restored = pageDataRef.value?.restoreState?.({
|
||||
pageNumber: state.pageNumber,
|
||||
pageSize: state.pageSize,
|
||||
queryParams: { execKey: state.execKey || undefined },
|
||||
queryParams: { keyword: state.execKey || undefined },
|
||||
});
|
||||
if (workflowChanged && !restored) pageDataRef.value?.reload?.();
|
||||
});
|
||||
@@ -79,7 +79,7 @@ function search(formEl: FormInstance | undefined) {
|
||||
if (valid) {
|
||||
appliedExecKey.value = formInline.value.execKey;
|
||||
pageDataRef.value.setQuery({
|
||||
execKey: appliedExecKey.value || undefined,
|
||||
keyword: appliedExecKey.value || undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -222,7 +222,7 @@ function getTagType(row: any) {
|
||||
:page-size="initialListState.pageSize"
|
||||
:initial-page-number="initialListState.pageNumber"
|
||||
:initial-query-params="{
|
||||
execKey: initialListState.execKey || undefined,
|
||||
keyword: initialListState.execKey || undefined,
|
||||
}"
|
||||
:extra-query-params="{
|
||||
workflowId: $route.query.workflowId,
|
||||
|
||||
@@ -37,7 +37,9 @@ function initDict() {
|
||||
function search(formEl: FormInstance | undefined) {
|
||||
formEl?.validate((valid) => {
|
||||
if (valid) {
|
||||
pageDataRef.value.setQuery(formInline.value);
|
||||
pageDataRef.value.setQuery({
|
||||
keyword: formInline.value.nodeName.trim() || undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user