Files
TERES_web_frontend/src/pages/agent/list.tsx

160 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useCallback, useMemo, useState, useEffect } from 'react';
import {
Box,
Typography,
TextField,
InputAdornment,
Paper,
Button,
Pagination,
Stack,
} from '@mui/material';
import { Search as SearchIcon, Refresh as RefreshIcon, Add as AddIcon } from '@mui/icons-material';
import { useAgentList, useAgentOperations } from '@/hooks/agent-hooks';
import AgentGridView from '@/components/agent/AgentGridView';
import CreateAgentDialog from '@/pages/agent/components/CreateAgentDialog';
import EditAgentDialog from '@/pages/agent/components/EditAgentDialog';
import { useTranslation } from 'react-i18next';
import { useDialog } from '@/hooks/useDialog';
import { useNavigate } from 'react-router-dom';
function AgentListPage() {
const [searchValue, setSearchValue] = useState('');
const [createOpen, setCreateOpen] = useState(false);
const [editOpen, setEditOpen] = useState(false);
const [editTarget, setEditTarget] = useState<any>(null);
const {
agents,
total,
loading,
currentPage,
pageSize,
setCurrentPage,
setKeywords,
refresh,
} = useAgentList({ page: 1, page_size: 10 });
const { t } = useTranslation();
const navigate = useNavigate();
const dialog = useDialog();
const ops = useAgentOperations();
const totalPages = useMemo(() => {
return Math.ceil((agents?.length || 0) / pageSize) || 1;
}, [agents, pageSize]);
const currentPageData = useMemo(() => {
const startIndex = (currentPage - 1) * pageSize;
const endIndex = startIndex + pageSize;
return (agents || []).slice(startIndex, endIndex);
}, [agents, currentPage, pageSize]);
const handleSearch = useCallback((value: string) => {
// 仅更新输入值,实际搜索在 500ms 防抖后触发
setSearchValue(value);
}, []);
// 500ms 防抖:在用户停止输入 500ms 后触发搜索
useEffect(() => {
const handler = setTimeout(() => {
setKeywords(searchValue);
setCurrentPage(1);
}, 500);
return () => clearTimeout(handler);
}, [searchValue, setKeywords, setCurrentPage]);
return (
<Box sx={{ p: 3 }}>
{/* 页面标题 */}
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Typography variant="h4" fontWeight={600}>
{t('agent.agentList')}
</Typography>
<Button
variant="contained"
startIcon={<AddIcon />}
onClick={() => setCreateOpen(true)}
sx={{ borderRadius: 2 }}
>
{t('agent.createAgent')}
</Button>
</Box>
<Paper sx={{ p: 2, mb: 2 }}>
<Box display="flex" gap={2} alignItems="center">
<TextField
value={searchValue}
onChange={(e) => handleSearch(e.target.value)}
placeholder="搜索名称或描述"
size="small"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
)
}}
/>
<Button variant="outlined" startIcon={<RefreshIcon />} onClick={refresh}></Button>
</Box>
</Paper>
<AgentGridView
agents={currentPageData}
loading={loading}
searchTerm={searchValue}
onCreateAgent={() => setCreateOpen(true)}
onEdit={(agent) => { setEditTarget(agent); setEditOpen(true); }}
onView={(agent) => {
navigate(`/agent/${agent.id}`);
}}
onDelete={async (agent) => {
const confirmed = await dialog.confirm({
title: t('dialog.confirmDelete'),
content: t('dialog.confirmDeleteMessage'),
confirmText: t('dialog.delete'),
cancelText: t('dialog.cancel'),
});
if (!confirmed) return;
const res = await ops.deleteAgents([agent.id]);
if (res.success) refresh();
}}
/>
{totalPages >= 1 && (
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 4 }}>
<Stack spacing={2}>
<Pagination
count={totalPages}
page={currentPage}
onChange={(_, page) => setCurrentPage(page)}
color="primary"
size="large"
showFirstButton
showLastButton
/>
<Typography variant="body2" color="text.secondary" textAlign="center">
{total} {currentPage} / {totalPages}
</Typography>
</Stack>
</Box>
)}
<CreateAgentDialog
open={createOpen}
onClose={() => setCreateOpen(false)}
onCreated={() => {
setCreateOpen(false);
refresh();
}}
/>
<EditAgentDialog
open={editOpen}
agent={editTarget}
onClose={() => { setEditOpen(false); setEditTarget(null); }}
onSaved={() => { setEditOpen(false); setEditTarget(null); refresh(); }}
/>
</Box>
);
}
export default AgentListPage;