diff --git a/app/routes/rule-groups._index.tsx b/app/routes/rule-groups._index.tsx index 4a86369..3405e23 100644 --- a/app/routes/rule-groups._index.tsx +++ b/app/routes/rule-groups._index.tsx @@ -8,7 +8,14 @@ import { StatusDot } from "~/components/ui/StatusDot"; import { Table } from "~/components/ui/Table"; import { FilterPanel, FilterSelect, SearchFilter } from "~/components/ui/FilterPanel"; // import { Pagination } from "~/components/ui/Pagination"; -import { getRuleGroups, getChildGroups, type RuleGroup, deleteRuleGroup } from "~/api/evaluation_points/rule-groups"; +import { + getRuleGroups, + getChildGroups, + type RuleGroup, + deleteRuleGroup, + batchUpdateRuleGroupStatus, + batchDeleteRuleGroups +} from "~/api/evaluation_points/rule-groups"; import { toastService } from "~/components/ui"; export function links() { @@ -27,20 +34,51 @@ export async function loader({ request }: { request: Request }) { // 获取用户会话信息 const { getUserSession } = await import("~/api/login/auth.server"); const { frontendJWT } = await getUserSession(request); - - const response = await getRuleGroups(frontendJWT); + + // 🆕 解析URL查询参数(服务端筛选和分页) + const url = new URL(request.url); + const name = url.searchParams.get('name') || undefined; + const code = url.searchParams.get('code') || undefined; + const is_enabled = url.searchParams.get('is_enabled'); + const page = parseInt(url.searchParams.get('page') || '1'); + const pageSize = parseInt(url.searchParams.get('pageSize') || '50'); + + // 🆕 调用增强的 getRuleGroups API + const response = await getRuleGroups({ + name, + code, + is_enabled: is_enabled ? is_enabled === 'true' : undefined, + pid: null, // 仅获取一级分组 + page, + pageSize, + token: frontendJWT + }); + if (response.error) { throw new Error(response.error); } - return Response.json({ groups: response.data, frontendJWT }); + + return Response.json({ + groups: response.data || [], + totalCount: ('totalCount' in response) ? (response.totalCount || 0) : 0, + page, + pageSize, + frontendJWT + }); } catch (error) { console.error('加载评查点分组失败:', error); - return Response.json({ groups: [] }); + return Response.json({ + groups: [], + totalCount: 0, + page: 1, + pageSize: 50 + }); } } export default function RuleGroupsIndex() { - const { groups: initialGroups, frontendJWT } = useLoaderData(); + const loaderData = useLoaderData(); + const { groups: initialGroups, totalCount = 0, page = 1, pageSize = 50, frontendJWT } = loaderData; const rootData = useRouteLoaderData("root") as { userRole: string }; const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); @@ -49,6 +87,7 @@ export default function RuleGroupsIndex() { const [loading, setLoading] = useState>({}); const [filteredChildrenMap, setFilteredChildrenMap] = useState>({}); const [initialLoading, setInitialLoading] = useState(true); + const [selectedIds, setSelectedIds] = useState([]); // 🆕 批量选择状态 const userRole = rootData?.userRole || 'common'; const hasEditPermission = userRole.toLowerCase().includes('provin'); @@ -229,6 +268,69 @@ export default function RuleGroupsIndex() { } }; + // 🆕 批量启用/禁用 + const handleBatchEnable = async (enable: boolean) => { + if (selectedIds.length === 0) { + toastService.warning('请先选择要操作的分组'); + return; + } + + try { + const result = await batchUpdateRuleGroupStatus(selectedIds, enable, frontendJWT); + if (result.success) { + toastService.success(`成功${enable ? '启用' : '禁用'} ${result.updated_count} 个分组`); + // 刷新页面以重新加载数据 + window.location.reload(); + } else { + toastService.error(`批量操作失败:${result.failed_ids.length} 个分组操作失败`); + } + } catch (error) { + console.error('批量操作失败:', error); + toastService.error('批量操作失败,请稍后重试'); + } + }; + + // 🆕 批量删除 + const handleBatchDelete = async () => { + if (selectedIds.length === 0) { + toastService.warning('请先选择要删除的分组'); + return; + } + + if (!confirm(`确定要删除选中的 ${selectedIds.length} 个分组吗?此操作不可恢复。`)) { + return; + } + + try { + const result = await batchDeleteRuleGroups(selectedIds, frontendJWT); + toastService.success(`成功删除 ${result.deleted_count} 个分组`); + if (result.failed_ids.length > 0) { + toastService.warning(`有 ${result.failed_ids.length} 个分组删除失败`); + } + // 刷新页面以重新加载数据 + window.location.reload(); + } catch (error) { + console.error('批量删除失败:', error); + toastService.error('批量删除失败,请稍后重试'); + } + }; + + // 🆕 处理全选/取消全选 + const handleSelectAll = () => { + if (selectedIds.length === groups.length) { + setSelectedIds([]); + } else { + setSelectedIds(groups.map(g => g.id)); + } + }; + + // 🆕 处理单选 + const handleSelectRow = (id: string) => { + setSelectedIds(prev => + prev.includes(id) ? prev.filter(selectedId => selectedId !== id) : [...prev, id] + ); + }; + // 处理搜索名称 const handleNameSearch = (value: string) => { const newParams = new URLSearchParams(searchParams); @@ -450,6 +552,28 @@ export default function RuleGroupsIndex() { // 定义表格列配置 const columns = [ + // 🆕 复选框列 + ...(hasEditPermission ? [{ + title: ( + 0 && selectedIds.length === groups.length} + onChange={handleSelectAll} + style={{ cursor: 'pointer' }} + /> + ), + key: "selection", + width: "50px", + render: (_: unknown, record: RuleGroup) => ( + handleSelectRow(record.id)} + onClick={(e) => e.stopPropagation()} + style={{ cursor: 'pointer' }} + /> + ) + }] : []), { title: "分组名称", key: "name", @@ -579,6 +703,34 @@ export default function RuleGroupsIndex() { > 收起全部 + {hasEditPermission && selectedIds.length > 0 && ( + <> + + + + + )} {hasEditPermission && (