新增提示词列表和提示词修改页面
This commit is contained in:
@@ -0,0 +1,479 @@
|
||||
import { MetaFunction } from "@remix-run/node";
|
||||
import { useSearchParams, useNavigate } from "@remix-run/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";
|
||||
|
||||
// 定义提示词模板类型
|
||||
export interface PromptTemplate {
|
||||
id: string;
|
||||
template_name: string;
|
||||
template_type: 'Common' | 'Extraction' | 'Evaluation' | 'Summary';
|
||||
description: string;
|
||||
version: string;
|
||||
status: 'active' | 'inactive' | 'system';
|
||||
created_by: string;
|
||||
template_content: string;
|
||||
variables: string; // JSON字符串
|
||||
}
|
||||
|
||||
// 样式链接
|
||||
export function links() {
|
||||
return [{ rel: "stylesheet", href: indexStyles }];
|
||||
}
|
||||
|
||||
// 页面元数据
|
||||
export const meta: MetaFunction = () => {
|
||||
return [
|
||||
{ title: "提示词模板管理 - 中国烟草AI合同及卷宗审核系统" },
|
||||
{ name: "description", content: "管理提示词模板,包括创建、编辑和删除提示词模板" },
|
||||
];
|
||||
};
|
||||
|
||||
// 面包屑导航
|
||||
export const handle = {
|
||||
breadcrumb: "提示词模板管理"
|
||||
};
|
||||
|
||||
// 模拟数据
|
||||
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": "文档类型" })
|
||||
}
|
||||
];
|
||||
|
||||
// 数据加载器
|
||||
export async function loader() {
|
||||
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 templates = MOCK_TEMPLATES;
|
||||
|
||||
return Response.json({
|
||||
templates,
|
||||
total: templates.length,
|
||||
pageSize: 10,
|
||||
currentPage: 1
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("加载提示词模板失败:", error);
|
||||
return Response.json(
|
||||
{
|
||||
templates: [],
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
error: "加载提示词模板失败"
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 页面组件
|
||||
export default function PromptsIndex() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
// 处理搜索名称
|
||||
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<HTMLSelectElement>) => {
|
||||
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<HTMLSelectElement>) => {
|
||||
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 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) => {
|
||||
if (confirm('确定要删除该模板吗?删除后无法恢复。')) {
|
||||
// 实际应该调用API删除数据
|
||||
console.log('删除模板ID:', id);
|
||||
alert('删除成功!');
|
||||
// 刷新页面
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
// 定义表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: "模板名称",
|
||||
key: "template_name",
|
||||
width: "300px",
|
||||
render: (_: unknown, record: PromptTemplate) => (
|
||||
<div className="flex items-center">
|
||||
<i className="ri-file-list-line text-primary mr-2"></i>
|
||||
<span>{record.template_name}</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "类型",
|
||||
key: "template_type",
|
||||
width: "100px",
|
||||
render: (_: unknown, record: PromptTemplate) => {
|
||||
let typeText = '';
|
||||
let typeClass = '';
|
||||
|
||||
switch (record.template_type) {
|
||||
case '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 <span className={`type-badge ${typeClass}`}>{typeText}</span>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "描述",
|
||||
key: "description",
|
||||
render: (_: unknown, record: PromptTemplate) => (
|
||||
<div className="text-secondary text-sm truncate max-w-xs" title={record.description}>
|
||||
{record.description}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "版本",
|
||||
key: "version",
|
||||
width: "80px",
|
||||
render: (_: unknown, record: PromptTemplate) => record.version
|
||||
},
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
width: "80px",
|
||||
render: (_: unknown, record: PromptTemplate) => {
|
||||
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 <span className={`status-badge ${statusClass}`}>{statusText}</span>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "创建者",
|
||||
key: "created_by",
|
||||
width: "100px",
|
||||
render: (_: unknown, record: PromptTemplate) => record.created_by
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "operation",
|
||||
width: "150px",
|
||||
render: (_: unknown, record: PromptTemplate) => (
|
||||
<div>
|
||||
{record.status === 'system' ? (
|
||||
<>
|
||||
<button
|
||||
className="operation-btn text-primary"
|
||||
onClick={() => handleViewTemplate(record.id)}
|
||||
>
|
||||
<i className="ri-eye-line"></i> 查看
|
||||
</button>
|
||||
<button
|
||||
className="operation-btn text-primary"
|
||||
onClick={() => handleCloneTemplate(record.id)}
|
||||
>
|
||||
<i className="ri-file-copy-line"></i> 复制
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
className="operation-btn text-primary"
|
||||
onClick={() => handleEditTemplate(record.id)}
|
||||
>
|
||||
<i className="ri-edit-line"></i> 编辑
|
||||
</button>
|
||||
<button
|
||||
className="operation-btn text-error"
|
||||
onClick={() => handleDeleteTemplate(record.id)}
|
||||
>
|
||||
<i className="ri-delete-bin-line"></i> 删除
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="prompt-page">
|
||||
{/* 页面头部 */}
|
||||
<div className="page-header">
|
||||
<h2 className="page-title">提示词模板管理</h2>
|
||||
<div>
|
||||
<Button
|
||||
type="primary"
|
||||
icon="ri-add-line"
|
||||
onClick={() => navigate("/prompts/new")}
|
||||
>
|
||||
新增提示词模板
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 搜索栏 - 使用FilterPanel */}
|
||||
<FilterPanel
|
||||
className="mb-4"
|
||||
actions={
|
||||
<>
|
||||
<Button
|
||||
type="default"
|
||||
icon="ri-refresh-line"
|
||||
onClick={handleReset}
|
||||
className="mr-2"
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon="ri-search-line"
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
noActionDivider={true}
|
||||
>
|
||||
<SearchFilter
|
||||
label="模板名称"
|
||||
placeholder="请输入模板名称"
|
||||
value={searchParams.get('name') || ''}
|
||||
onSearch={handleNameSearch}
|
||||
className="flex-1 min-w-[200px]"
|
||||
instantSearch={true}
|
||||
/>
|
||||
|
||||
<FilterSelect
|
||||
label="模板类型"
|
||||
name="type"
|
||||
value={searchParams.get('type') || ''}
|
||||
options={[
|
||||
{ value: "", label: "全部" },
|
||||
{ value: "Extraction", label: "抽取(Extraction)" },
|
||||
{ value: "Evaluation", label: "评估(Evaluation)" },
|
||||
{ value: "Summary", label: "摘要(Summary)" },
|
||||
{ value: "Common", label: "通用(Common)" }
|
||||
]}
|
||||
onChange={handleTypeChange}
|
||||
className="flex-1 min-w-[200px]"
|
||||
/>
|
||||
|
||||
<FilterSelect
|
||||
label="状态"
|
||||
name="status"
|
||||
value={searchParams.get('status') || ''}
|
||||
options={[
|
||||
{ value: "", label: "全部" },
|
||||
{ value: "active", label: "启用" },
|
||||
{ value: "inactive", label: "停用" },
|
||||
{ value: "system", label: "系统预设" }
|
||||
]}
|
||||
onChange={handleStatusChange}
|
||||
className="flex-1 min-w-[200px]"
|
||||
/>
|
||||
</FilterPanel>
|
||||
|
||||
{/* 数据表格 */}
|
||||
<Card bodyClassName="px-4 py-4">
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={MOCK_TEMPLATES}
|
||||
rowKey="id"
|
||||
emptyText="暂无提示词模板数据"
|
||||
/>
|
||||
|
||||
{/* 分页 */}
|
||||
<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}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user