完善提示词管理和配置管理的增删改查

This commit is contained in:
2025-04-09 21:48:28 +08:00
parent fda6515891
commit 4e43df00c0
14 changed files with 1100 additions and 484 deletions
+121 -140
View File
@@ -1,11 +1,13 @@
import { MetaFunction } from "@remix-run/node";
import { useSearchParams, useNavigate } from "@remix-run/react";
import { MetaFunction, json, type LoaderFunctionArgs } from "@remix-run/node";
import { useSearchParams, useNavigate, useLoaderData } from "@remix-run/react";
import { useState } 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";
// 定义提示词模板类型
export interface PromptTemplate {
@@ -33,123 +35,61 @@ export const meta: MetaFunction = () => {
];
};
// 模拟数据
const MOCK_TEMPLATES: PromptTemplate[] = [
{
id: "1",
template_name: "行政处罚-抽取通用模板",
template_type: "Extraction",
description: "本模板用于抽取行政处罚决定书编号等信息",
version: "v1.0",
status: "system",
created_by: "system",
template_content: `你是一个专业的文档信息抽取助手。请从以下{docType}文档中抽取关键信息:
1. 处罚决定书编号
2. 处罚对象名称
3. 处罚事由
4. 处罚依据
5. 处罚内容
6. 处罚金额
7. 发文日期
请将结果以JSON格式输出,包含以上字段。如果某个字段在文档中未找到,则该字段的值设为null。`,
variables: JSON.stringify({ "docType": "文档类型" })
},
{
id: "2",
template_name: "销售合同-甲方信息评估",
template_type: "Evaluation",
description: "评估销售合同中甲方信息是否完整",
version: "v1.2",
status: "active",
created_by: "admin",
template_content: `你是一个专业的合同审核助手。请评估以下{docType}中甲方信息的完整性:
请检查以下要素是否存在且完整:
1. 甲方全称
2. 注册地址
3. 统一社会信用代码
4. 法定代表人
5. 联系方式
请给出评估结果,并标明缺失或不完整的信息。`,
variables: JSON.stringify({ "docType": "文档类型" })
},
{
id: "3",
template_name: "专卖许可证-摘要模板",
template_type: "Summary",
description: "生成专卖许可证申请文件的内容摘要",
version: "v1.0",
status: "active",
created_by: "admin",
template_content: `你是一个专业的文档摘要助手。请为以下{docType}生成一份简洁的摘要:
摘要应包含以下要点:
1. 申请人基本信息
2. 许可证类型
3. 申请事项
4. 经营范围
5. 申请日期
请控制摘要在200字以内,保留关键信息。`,
variables: JSON.stringify({ "docType": "文档类型" })
},
{
id: "4",
template_name: "采购合同-乙方资质抽取",
template_type: "Extraction",
description: "抽取采购合同中乙方的资质信息",
version: "v1.1",
status: "inactive",
created_by: "zhangsan",
template_content: `你是一个专业的合同信息抽取助手。请从以下{docType}中抽取乙方的资质信息:
需要抽取的信息包括:
1. 乙方全称
2. 资质证书类型
3. 资质证书编号
4. 资质等级
5. 证书有效期
请将结果以JSON格式输出,包含以上字段。`,
variables: JSON.stringify({ "docType": "文档类型" })
},
{
id: "5",
template_name: "合同通用-关键条款评估",
template_type: "Evaluation",
description: "评估合同中关键条款是否明确、合规",
version: "v2.0",
status: "active",
created_by: "lisi",
template_content: `你是一个专业的{industry}行业合同审核助手。请评估以下合同中的关键条款是否明确、合规:
请重点关注以下条款:
1. 合同标的
2. 价格条款
3. 付款条件
4. 交付方式
5. 违约责任
6. 争议解决
请对每一项给出评估结果,并指出不明确或存在风险的条款。`,
variables: JSON.stringify({ "industry": "行业类型", "docType": "文档类型" })
}
];
// 定义加载器返回数据类型
interface LoaderData {
templates: PromptTemplateUI[];
total: number;
pageSize: number;
currentPage: number;
error?: string;
}
// 数据加载器
export async function loader() {
export async function loader({ request }: LoaderFunctionArgs) {
try {
// 实际应用中,这里应该调用API获取数据
// const response = await fetch(`${process.env.API_BASE_URL}/api/prompt-templates`);
// if (!response.ok) throw new Error(`获取提示词模板失败: ${response.status}`);
// const templates = await response.json();
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);
// 使用模拟数据
const templates = MOCK_TEMPLATES;
console.log('加载提示词模板参数:', { name, type, status, page, pageSize });
return Response.json({
templates,
total: templates.length,
pageSize: 10,
currentPage: 1
// 从 API 获取数据
const result = await getPromptTemplates({
name,
type,
status,
page,
pageSize
});
if (result.error) {
console.error('获取提示词模板失败:', result.error);
return json<LoaderData>(
{
templates: [],
total: 0,
pageSize,
currentPage: page,
error: result.error
},
{ status: result.status || 500 }
);
}
console.log(`成功加载${result.data?.templates.length || 0}条提示词模板数据`);
return json<LoaderData>({
templates: result.data?.templates || [],
total: result.data?.total || 0,
pageSize,
currentPage: page
});
} catch (error) {
console.error("加载提示词模板失败:", error);
return Response.json(
return json<LoaderData>(
{
templates: [],
total: 0,
@@ -166,6 +106,8 @@ export async function loader() {
export default function PromptsIndex() {
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const { templates, total, currentPage, pageSize, error } = useLoaderData<typeof loader>();
const [isLoading, setIsLoading] = useState(false);
// 处理搜索名称
const handleNameSearch = (value: string) => {
@@ -210,6 +152,12 @@ export default function PromptsIndex() {
setSearchParams(new URLSearchParams());
};
// 处理搜索按钮点击
// const handleSearch = () => {
// // 搜索已经由 URL 参数变化触发,这里不需要额外操作
// console.log('搜索参数:', Object.fromEntries(searchParams.entries()));
// };
// 查看模板详情
const handleViewTemplate = (id: string) => {
navigate(`/prompts/new?id=${id}&mode=view`);
@@ -228,23 +176,48 @@ export default function PromptsIndex() {
};
// 删除模板
const handleDeleteTemplate = (id: string) => {
const handleDeleteTemplate = async (id: string) => {
if (confirm('确定要删除该模板吗?删除后无法恢复。')) {
// 实际应该调用API删除数据
console.log('删除模板ID:', id);
alert('删除成功!');
// 刷新页面
window.location.reload();
setIsLoading(true);
try {
const result = await deletePromptTemplate(id);
if (result.error) {
alert(`删除失败: ${result.error}`);
} else {
alert('删除成功!');
// 刷新页面
window.location.reload();
}
} catch (error) {
alert(`删除失败: ${error instanceof Error ? error.message : '未知错误'}`);
} finally {
setIsLoading(false);
}
}
};
// 处理分页
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: "300px",
render: (_: unknown, record: PromptTemplate) => (
render: (_: unknown, record: PromptTemplateUI) => (
<div className="flex items-center">
<i className="ri-file-list-line text-primary mr-2"></i>
<span className="truncate">{record.template_name}</span>
@@ -255,7 +228,7 @@ export default function PromptsIndex() {
title: "类型",
key: "template_type",
width: "100px",
render: (_: unknown, record: PromptTemplate) => {
render: (_: unknown, record: PromptTemplateUI) => {
let typeText = '';
let typeClass = '';
@@ -284,7 +257,7 @@ export default function PromptsIndex() {
{
title: "描述",
key: "description",
render: (_: unknown, record: PromptTemplate) => (
render: (_: unknown, record: PromptTemplateUI) => (
<div className="text-secondary text-sm truncate max-w-xs" title={record.description}>
{record.description}
</div>
@@ -294,13 +267,13 @@ export default function PromptsIndex() {
title: "版本",
key: "version",
width: "80px",
render: (_: unknown, record: PromptTemplate) => record.version
render: (_: unknown, record: PromptTemplateUI) => record.version
},
{
title: "状态",
key: "status",
width: "80px",
render: (_: unknown, record: PromptTemplate) => {
render: (_: unknown, record: PromptTemplateUI) => {
let statusText = '';
let statusClass = '';
@@ -326,14 +299,15 @@ export default function PromptsIndex() {
title: "创建者",
key: "created_by",
width: "100px",
render: (_: unknown, record: PromptTemplate) => record.created_by
render: (_: unknown, record: PromptTemplateUI) => (
<span className="text-secondary"> {record.created_by}</span>
)
},
{
title: "操作",
key: "operation",
width: "150px",
// align: "center",
render: (_: unknown, record: PromptTemplate) => (
render: (_: unknown, record: PromptTemplateUI) => (
<div>
{record.status === 'system' ? (
<>
@@ -361,6 +335,7 @@ export default function PromptsIndex() {
<button
className="operation-btn text-error"
onClick={() => handleDeleteTemplate(record.id)}
disabled={isLoading}
>
<i className="ri-delete-bin-line"></i>
</button>
@@ -400,12 +375,13 @@ export default function PromptsIndex() {
>
</Button>
<Button
{/* <Button
type="primary"
icon="ri-search-line"
onClick={handleSearch}
>
搜索
</Button>
</Button> */}
</>
}
noActionDivider={true}
@@ -424,7 +400,6 @@ export default function PromptsIndex() {
name="type"
value={searchParams.get('type') || ''}
options={[
// { value: "", label: "全部" },
{ value: "Extraction", label: "抽取(Extraction)" },
{ value: "Evaluation", label: "评估(Evaluation)" },
{ value: "Summary", label: "摘要(Summary)" },
@@ -439,7 +414,6 @@ export default function PromptsIndex() {
name="status"
value={searchParams.get('status') || ''}
options={[
// { value: "", label: "全部" },
{ value: "active", label: "启用" },
{ value: "inactive", label: "停用" },
{ value: "system", label: "系统预设" }
@@ -449,27 +423,34 @@ export default function PromptsIndex() {
/>
</FilterPanel>
{/* 错误信息 */}
{error && (
<div className="error-alert mb-4 p-4 bg-red-50 text-red-700 rounded-md">
<i className="ri-error-warning-line mr-2"></i> {error}
</div>
)}
{/* 数据表格 */}
<Card bodyClassName="px-4 py-4">
<Table
columns={columns}
dataSource={MOCK_TEMPLATES}
dataSource={templates}
rowKey="id"
emptyText="暂无提示词模板数据"
loading={isLoading}
/>
{/* 分页 */}
<Pagination
currentPage={1}
total={MOCK_TEMPLATES.length}
pageSize={10}
onChange={(page) => {
const newParams = new URLSearchParams(searchParams);
newParams.set('page', page.toString());
setSearchParams(newParams);
}}
showTotal={true}
/>
currentPage={currentPage}
total={total}
pageSize={pageSize}
onChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
showTotal={true}
showPageSizeChanger={true}
pageSizeOptions={[10, 20, 30, 50]}
/>
</Card>
</div>
);