From c72a2e699bbf493175d409eb24fa6a8c1344a95b Mon Sep 17 00:00:00 2001
From: "alan.cl" <1165243776@qq.com>
Date: Mon, 9 Mar 2026 16:10:16 +0800
Subject: [PATCH] feat(web): move view-all icon to sidebar header + add search
to conversations page
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Replace bottom '查看全部' text link in sidebar with MoreOutlined icon + Tooltip
in the '所有任务' section header (flex justify-between layout)
- Add debounced search input (SearchOutlined, 300ms) to /conversations page
- Implement client-side title filtering via filteredList useMemo
- Show distinct empty states: '暂无历史记录' vs '没有匹配的对话'
---
web/components/layout/side-bar.tsx | 12 +-
web/pages/conversations/index.tsx | 191 +++++++++++++++++++++++++++++
2 files changed, 201 insertions(+), 2 deletions(-)
create mode 100644 web/pages/conversations/index.tsx
diff --git a/web/components/layout/side-bar.tsx b/web/components/layout/side-bar.tsx
index f0f42c1fe..67a34fb03 100644
--- a/web/components/layout/side-bar.tsx
+++ b/web/components/layout/side-bar.tsx
@@ -16,6 +16,7 @@ import Icon, {
MenuFoldOutlined,
MenuUnfoldOutlined,
MessageOutlined,
+ MoreOutlined,
PlusOutlined,
SettingOutlined,
ThunderboltOutlined,
@@ -341,8 +342,15 @@ function SideBar() {
{/* All Tasks Section */}
-
- {t('all_tasks')}
+
+
+ {t('all_tasks')}
+
+
+
+
+
+
diff --git a/web/pages/conversations/index.tsx b/web/pages/conversations/index.tsx
new file mode 100644
index 000000000..efca21166
--- /dev/null
+++ b/web/pages/conversations/index.tsx
@@ -0,0 +1,191 @@
+import { apiInterceptors, delDialogue, getDialogueListPaged } from '@/client/api';
+import { IChatDialogueSchema } from '@/types/chat';
+import { DeleteOutlined, MessageOutlined, SearchOutlined } from '@ant-design/icons';
+import { useRequest } from 'ahooks';
+import { Empty, Input, Pagination, Popconfirm, Spin, Tooltip, message } from 'antd';
+import debounce from 'lodash/debounce';
+import moment from 'moment';
+import 'moment/locale/zh-cn';
+import { useRouter } from 'next/router';
+import { useCallback, useMemo, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+
+const PAGE_SIZE = 20;
+
+function ConversationsPage() {
+ const router = useRouter();
+ const { t } = useTranslation();
+ const [list, setList] = useState
([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+
+ const handleSearch = useCallback(
+ debounce((value: string) => {
+ setSearchKeyword(value);
+ }, 300),
+ [],
+ );
+
+ const filteredList = useMemo(() => {
+ if (!searchKeyword.trim()) return list;
+ const keyword = searchKeyword.toLowerCase();
+ return list.filter(conv => {
+ const title = typeof conv.user_input === 'string' ? conv.user_input.toLowerCase() : '';
+ return title.includes(keyword);
+ });
+ }, [list, searchKeyword]);
+
+ const totalRef = useRef<{
+ current_page: number;
+ total_count: number;
+ total_pages: number;
+ }>();
+
+ const { loading, run: fetchList } = useRequest(
+ async (page = 1) =>
+ await apiInterceptors(
+ getDialogueListPaged({ chat_mode: 'chat_react_agent' }, page, PAGE_SIZE),
+ ),
+ {
+ defaultParams: [1],
+ onSuccess: data => {
+ const [, res] = data;
+ setList(res?.items || []);
+ totalRef.current = {
+ current_page: res?.page || 1,
+ total_count: res?.total_count || 0,
+ total_pages: res?.total_pages || 0,
+ };
+ },
+ },
+ );
+
+ const handleDelete = useCallback(
+ async (e: React.MouseEvent, convUid: string) => {
+ e.stopPropagation();
+ e.preventDefault();
+ const [err] = await apiInterceptors(delDialogue(convUid));
+ if (!err) {
+ message.success('已删除');
+ const current = totalRef.current;
+ if (current) {
+ const remaining = current.total_count - 1;
+ const maxPage = Math.max(1, Math.ceil(remaining / PAGE_SIZE));
+ fetchList(Math.min(current.current_page, maxPage));
+ }
+ }
+ },
+ [fetchList],
+ );
+
+ const formatTime = (dateStr?: string) => {
+ if (!dateStr) return '';
+ return moment(dateStr).fromNow();
+ };
+
+ const getTitle = (conv: IChatDialogueSchema) => {
+ if (typeof conv.user_input === 'string' && conv.user_input.trim()) {
+ return conv.user_input;
+ }
+ return t('new_task') || '新对话';
+ };
+
+ return (
+
+
+
+ {t('all_tasks') || '所有任务'}
+
+
+ }
+ placeholder='搜索对话...'
+ onChange={e => handleSearch(e.target.value)}
+ onClear={() => setSearchKeyword('')}
+ allowClear
+ className='w-[230px] h-[36px] border-1 border-white backdrop-filter backdrop-blur-lg bg-white bg-opacity-30 dark:border-[#6f7f95] dark:bg-[#6f7f95] dark:bg-opacity-60'
+ />
+
+ {totalRef.current ? `共 ${totalRef.current.total_count} 条` : ''}
+
+
+
+
+
+
+ {!loading && list.length === 0 ? (
+
+
+
+ ) : !loading && filteredList.length === 0 ? (
+
+
+
+ ) : (
+
+ {filteredList.map(conv => (
+
router.push(`/?id=${conv.conv_uid}`)}
+ className='group flex items-center gap-3 px-4 py-3 rounded-xl cursor-pointer transition-colors hover:bg-white dark:hover:bg-gray-800 hover:shadow-sm'
+ >
+
+
+
+
+
+
+ {getTitle(conv)}
+
+ {conv.gmt_created && (
+
{formatTime(conv.gmt_created)}
+ )}
+
+
+
handleDelete(e as React.MouseEvent, conv.conv_uid)}
+ onCancel={e => {
+ e?.stopPropagation();
+ e?.preventDefault();
+ }}
+ okText='删除'
+ cancelText='取消'
+ okButtonProps={{ danger: true }}
+ >
+
+ {
+ e.stopPropagation();
+ e.preventDefault();
+ }}
+ className='flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity'
+ >
+
+
+
+
+
+ ))}
+
+ )}
+
+
+
+ {(totalRef.current?.total_count ?? 0) > PAGE_SIZE && (
+
+
`共 ${total} 条`}
+ onChange={page => fetchList(page)}
+ />
+
+ )}
+
+ );
+}
+
+export default ConversationsPage;