import { MetaFunction, type LoaderFunctionArgs, type ActionFunctionArgs } from "@remix-run/node"; import { useSearchParams, useNavigate, useLoaderData, useFetcher, useRouteLoaderData } from "@remix-run/react"; import { useState, useEffect } from "react"; import indexStyles from "~/styles/pages/prompts_index.css?url"; import { Card } from "~/components/ui/Card"; import { Button } from "~/components/ui/Button"; import { FilterPanel, FilterSelect, SearchFilter } from "~/components/ui/FilterPanel"; import { Table } from "~/components/ui/Table"; import { Pagination } from "~/components/ui/Pagination"; import { getPromptTemplates, deletePromptTemplate, type PromptTemplateUI } from "~/api/prompts/prompts"; import { toastService, messageService } from "~/components/ui"; // 样式链接 export function links() { return [{ rel: "stylesheet", href: indexStyles }]; } // 页面元数据 export const meta: MetaFunction = () => { return [ { title: "提示词模板管理 - 中国烟草AI合同及卷宗审核系统" }, { name: "description", content: "管理提示词模板,包括创建、编辑和删除提示词模板" }, ]; }; // 定义加载器返回数据类型 interface LoaderData { templates: PromptTemplateUI[]; total: number; pageSize: number; currentPage: number; error?: string; } // 定义 Action 返回数据类型 interface ActionData { success: boolean; error?: string; } // 数据加载器 export async function loader({ request }: LoaderFunctionArgs) { try { // 获取用户会话信息 const { getUserSession } = await import("~/api/login/auth.server"); const { frontendJWT } = await getUserSession(request); const url = new URL(request.url); const name = url.searchParams.get('name') || undefined; const type = url.searchParams.get('type') || undefined; const status = url.searchParams.get('status') || undefined; const page = parseInt(url.searchParams.get('page') || '1', 10); const pageSize = parseInt(url.searchParams.get('pageSize') || '10', 10); // console.log('加载提示词模板参数:', { name, type: typeParam, status, page, pageSize }); // 从 API 获取数据 const result = await getPromptTemplates({ name, type, status, page, pageSize }, frontendJWT); if (result.error) { console.error('获取提示词模板失败:', result.error); return Response.json( { templates: [], total: 0, pageSize, currentPage: page, error: result.error }, { status: result.status || 500 } ); } // console.log(`成功加载${result.data?.templates.length || 0}条提示词模板数据`); return Response.json({ templates: result.data?.templates || [], total: result.data?.total || 0, pageSize, currentPage: page }); } catch (error) { console.error("加载提示词模板失败:", error); return Response.json( { templates: [], total: 0, pageSize: 10, currentPage: 1, error: "加载提示词模板失败" }, { status: 500 } ); } } // Action函数 - 处理删除请求 export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const id = formData.get("id") as string; const intent = formData.get("intent") as string; // 获取用户会话信息 const { getUserSession } = await import("~/api/login/auth.server"); const { frontendJWT } = await getUserSession(request); if (intent === "delete" && id) { try { const result = await deletePromptTemplate(id, frontendJWT); if (result.error) { return Response.json({ success: false, error: result.error }, { status: result.status || 500 }); } return Response.json({ success: true }); } catch (error) { return Response.json( { success: false, error: error instanceof Error ? error.message : "删除提示词模板失败" }, { status: 500 } ); } } return Response.json({ success: false, error: "无效的操作" }, { status: 400 }); } // 页面组件 export default function PromptsIndex() { const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); const { templates, total, currentPage, pageSize, error } = useLoaderData(); const [isLoading, setIsLoading] = useState(false); const fetcher = useFetcher(); // 获取用户角色并判断权限 const rootData = useRouteLoaderData("root") as { userRole: string }; const userRole = rootData?.userRole || 'common'; const hasEditPermission = userRole.toLowerCase().includes('provin'); // 调试信息 useEffect(() => { console.log('📋 [Prompts] 用户角色:', userRole); console.log('📋 [Prompts] 是否有编辑权限:', hasEditPermission); }, [userRole, hasEditPermission]); // 处理搜索名称 const handleNameSearch = (value: string) => { const newParams = new URLSearchParams(searchParams); if (value) { newParams.set('name', value); } else { newParams.delete('name'); } newParams.set('page', '1'); setSearchParams(newParams); }; // 处理类型筛选变更 const handleTypeChange = (e: React.ChangeEvent) => { const { value } = e.target; const newParams = new URLSearchParams(searchParams); if (value) { newParams.set('type', value); } else { newParams.delete('type'); } newParams.set('page', '1'); setSearchParams(newParams); }; // 处理状态筛选变更 const handleStatusChange = (e: React.ChangeEvent) => { const { value } = e.target; const newParams = new URLSearchParams(searchParams); if (value) { newParams.set('status', value); } else { newParams.delete('status'); } newParams.set('page', '1'); setSearchParams(newParams); }; // 处理重置筛选 const handleReset = () => { setSearchParams(new URLSearchParams()); }; // 处理搜索按钮点击 // const handleSearch = () => { // // 搜索已经由 URL 参数变化触发,这里不需要额外操作 // console.log('搜索参数:', Object.fromEntries(searchParams.entries())); // }; // 查看模板详情 const handleViewTemplate = (id: string) => { navigate(`/prompts/new?id=${id}&mode=view`); }; // 复制模板 const handleCloneTemplate = (id: string) => { if (confirm('确定要复制该模板创建新模板吗?')) { navigate(`/prompts/new?id=${id}&mode=clone`); } }; // 编辑模板 const handleEditTemplate = (id: string) => { navigate(`/prompts/new?id=${id}&mode=edit`); }; // 删除模板 const handleDeleteTemplate = (id: string) => { messageService.show({ title: "确认删除", message: "确定要删除该模板吗?删除后无法恢复。", type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, onConfirm: () => { const formData = new FormData(); formData.append('id', id); formData.append('intent', 'delete'); fetcher.submit(formData, { method: 'post' }); } }); }; // 监听 fetcher 状态变化 useEffect(() => { if (fetcher.state === 'idle' && fetcher.data) { if (fetcher.data.success) { toastService.success('删除成功!'); // Remix 会自动重新验证 loader 数据,无需手动刷新页面 } else if (fetcher.data.error) { toastService.error(`删除失败: ${fetcher.data.error}`); } } }, [fetcher.state, fetcher.data]); // 处理分页 const handlePageChange = (page: number) => { const newParams = new URLSearchParams(searchParams); newParams.set('page', page.toString()); setSearchParams(newParams); }; // 处理每页条数变更 const handlePageSizeChange = (size: number) => { const newParams = new URLSearchParams(searchParams); newParams.set('pageSize', size.toString()); newParams.set('page', '1'); // 更改每页条数时,重置到第一页 setSearchParams(newParams); }; // 定义表格列配置 const columns = [ { title: "模板名称", key: "template_name", width: "25%", render: (_: unknown, record: PromptTemplateUI) => (
{record.template_name}
) }, { title: "类型", key: "template_type", width: "100px", render: (_: unknown, record: PromptTemplateUI) => { let typeText = ''; let typeClass = ''; switch (record.template_type) { case 'LLM_Extraction': typeText = '抽取'; typeClass = 'type-extraction'; break; case 'VLM_Extraction': typeText = '抽取'; typeClass = 'type-extraction'; break; case 'Evaluation': typeText = '评估'; typeClass = 'type-evaluation'; break; case 'Summary': typeText = '摘要'; typeClass = 'type-summary'; break; case 'Common': typeText = '通用'; typeClass = 'type-common'; break; } return {typeText}; } }, { title: "描述", key: "description", width: "30%", render: (_: unknown, record: PromptTemplateUI) => (
{record.description}
) }, { title: "版本", key: "version", width: "70px", render: (_: unknown, record: PromptTemplateUI) => record.version }, { title: "状态", key: "status", width: "110px", render: (_: unknown, record: PromptTemplateUI) => { let statusText = ''; let statusClass = ''; switch (record.status) { case 'active': statusText = '启用'; statusClass = 'status-active'; break; case 'inactive': statusText = '停用'; statusClass = 'status-inactive'; break; case 'system': statusText = '系统预设'; statusClass = 'status-system'; break; } return {statusText}; } }, { title: "创建者", key: "created_by", width: "120px", render: (_: unknown, record: PromptTemplateUI) => ( // 用户 {record.created_by} {record.created_by_username} ) }, { title: "操作", key: "operation", width: "160px", render: (_: unknown, record: PromptTemplateUI) => (
{record.status === 'system' ? ( <> {hasEditPermission && ( )} ) : ( <> {hasEditPermission && ( )} )}
) } ]; return (
{/* 页面头部 */}

提示词模板管理

{hasEditPermission && ( )}
{/* 搜索栏 - 使用FilterPanel */} {/* */} } noActionDivider={true} > {/* 错误信息 */} {error && (
{error}
)} {/* 数据表格 */} {/* 分页 */} ); }