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;