fix: 完善提示词管理页面的优化,数据库中添加相关字段来区分vlm和llm提示词。评查点设置中抽取设置的多模态抽取的类型通过查询数据库来返回数据。

This commit is contained in:
2025-11-11 01:16:27 +08:00
parent ddad57529d
commit 95381ddcc2
7 changed files with 261 additions and 76 deletions
+8 -7
View File
@@ -8,6 +8,7 @@ import { FilterPanel, FilterSelect, SearchFilter } from "~/components/ui/FilterP
import { Table } from "~/components/ui/Table";
import { Pagination } from "~/components/ui/Pagination";
import { getPromptTemplates, deletePromptTemplate, type PromptTemplateUI } from "~/api/prompts/prompts";
import { toastService } from "~/components/ui";
// 样式链接
export function links() {
@@ -65,7 +66,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
if (result.error) {
console.error('获取提示词模板失败:', result.error);
return Response.json(
{
{
templates: [],
total: 0,
pageSize,
@@ -75,10 +76,10 @@ export async function loader({ request }: LoaderFunctionArgs) {
{ status: result.status || 500 }
);
}
// console.log(`成功加载${result.data?.templates.length || 0}条提示词模板数据`);
return Response.json({
return Response.json({
templates: result.data?.templates || [],
total: result.data?.total || 0,
pageSize,
@@ -218,10 +219,10 @@ export default function PromptsIndex() {
useEffect(() => {
if (fetcher.state === 'idle' && fetcher.data) {
if (fetcher.data.success) {
alert('删除成功!');
window.location.reload();
toastService.success('删除成功!');
// Remix 会自动重新验证 loader 数据,无需手动刷新页面
} else if (fetcher.data.error) {
alert(`删除失败: ${fetcher.data.error}`);
toastService.error(`删除失败: ${fetcher.data.error}`);
}
}
}, [fetcher.state, fetcher.data]);
+85 -11
View File
@@ -4,6 +4,7 @@ import { Link, useLoaderData, useNavigation, useActionData, Form } from "@remix-
import { Button } from "~/components/ui/Button";
import newStyles from "~/styles/pages/prompts_new.css?url";
import { getPromptTemplate, createPromptTemplate, updatePromptTemplate, type PromptTemplateUI } from "~/api/prompts/prompts";
// import { toastService } from "~/components/ui";
// 样式链接
export function links() {
@@ -40,6 +41,8 @@ interface LoaderData {
// 定义本地表单数据接口
interface FormDataState extends Omit<PromptTemplateUI, 'variables'> {
variables: string; // 在表单状态中我们保存变量为 JSON 字符串
template_code?: string; // 模板代码
template_abbreviation?: string; // 模板简称
}
interface ActionData {
@@ -48,6 +51,8 @@ interface ActionData {
template_name?: string;
template_type?: string;
template_content?: string;
template_code?: string;
template_abbreviation?: string;
general?: string;
};
formData?: {
@@ -58,6 +63,8 @@ interface ActionData {
variables: string;
status: "active" | "inactive" | "system";
version: string;
template_code?: string;
template_abbreviation?: string;
};
}
@@ -110,8 +117,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
export async function action({ request }: ActionFunctionArgs) {
const { getUserSession } = await import("~/api/login/auth.server");
const { userInfo, frontendJWT } = await getUserSession(request);
const userId = userInfo.get('user_id')
const userId = userInfo.user_id;
const formData = await request.formData();
const id = formData.get("id") as string;
const template_name = formData.get("template_name") as string;
@@ -121,6 +128,8 @@ export async function action({ request }: ActionFunctionArgs) {
const variables = formData.get("variables") as string;
const status = formData.get("status") as string;
const version = formData.get("version") as string;
const template_code = formData.get("template_code") as string;
const template_abbreviation = formData.get("template_abbreviation") as string;
const errors: ActionData["errors"] = {};
@@ -137,6 +146,19 @@ export async function action({ request }: ActionFunctionArgs) {
errors.template_content = "模板内容不能为空";
}
// VLM_Extraction 类型的额外验证
if (template_type === 'VLM_Extraction') {
if (!template_code || template_code.trim() === "") {
errors.template_code = "VLM抽取类型必须填写模板code";
// toastService.error('VLM抽取类型必须填写模板code')
}
if (!template_abbreviation || template_abbreviation.trim() === "") {
errors.template_abbreviation = "VLM抽取类型必须填写模板简称";
// toastService.error('VLM抽取类型必须填写模板简称')
}
}
if (Object.keys(errors).length > 0) {
return Response.json({ errors });
}
@@ -160,7 +182,9 @@ export async function action({ request }: ActionFunctionArgs) {
template_content,
variables: variablesData,
status: status === "active" ? "active" : "inactive",
version: version || "v1.0"
version: version || "v1.0",
template_code: template_code || undefined,
template_abbreviation: template_abbreviation || undefined
};
let result;
@@ -173,7 +197,7 @@ export async function action({ request }: ActionFunctionArgs) {
}
if (result.error) {
return Response.json({
return Response.json({
errors: { general: result.error },
formData: {
template_name,
@@ -182,7 +206,9 @@ export async function action({ request }: ActionFunctionArgs) {
template_content,
variables,
status,
version
version,
template_code,
template_abbreviation
}
});
}
@@ -190,9 +216,9 @@ export async function action({ request }: ActionFunctionArgs) {
return redirect("/prompts");
} catch (error) {
console.error("保存提示词模板失败:", error);
return Response.json({
errors: {
general: error instanceof Error ? error.message : "保存提示词模板失败"
return Response.json({
errors: {
general: error instanceof Error ? error.message : "保存提示词模板失败"
},
formData: {
template_name,
@@ -201,7 +227,9 @@ export async function action({ request }: ActionFunctionArgs) {
template_content,
variables,
status,
version
version,
template_code,
template_abbreviation
}
});
}
@@ -252,7 +280,9 @@ export default function PromptsNew() {
created_by: 1,
variables: "{}",
created_at: "",
updated_at: ""
updated_at: "",
template_code: "",
template_abbreviation: ""
});
// 模式状态
@@ -497,7 +527,7 @@ export default function PromptsNew() {
<label htmlFor="template-type" className="form-label mb-1">
<span className="text-error">*</span>
</label>
<select
<select
className={`form-select py-1 ${isViewMode ? 'read-only-field' : ''} ${actionData?.errors?.template_type ? 'input-error' : ''}`}
id="template-type"
name="template_type"
@@ -517,6 +547,50 @@ export default function PromptsNew() {
<div className="error-message">{actionData.errors.template_type}</div>
)}
</div>
{/* 模板codeVLM_Extraction类型时必填) */}
<div className="form-group mb-3">
<label htmlFor="template-code" className="form-label mb-1">
code {formData.template_type === 'VLM_Extraction' && <span className="text-error">*</span>}
</label>
<input
type="text"
className={`form-input py-1 ${isViewMode ? 'read-only-field' : ''} ${actionData?.errors?.template_code ? 'input-error' : ''}`}
id="template-code"
name="template_code"
placeholder="请输入模板code,如:vlm_seal_prompt"
value={formData.template_code || ''}
onChange={handleInputChange}
readOnly={isViewMode}
required={formData.template_type === 'VLM_Extraction'}
/>
{actionData?.errors?.template_code && (
<div className="error-message">{actionData.errors.template_code}</div>
)}
<div className="help-text text-xs">VLM抽取类型必须填写</div>
</div>
{/* 模板简称(VLM_Extraction类型时必填) */}
<div className="form-group mb-3">
<label htmlFor="template-abbreviation" className="form-label mb-1">
{formData.template_type === 'VLM_Extraction' && <span className="text-error">*</span>}
</label>
<input
type="text"
className={`form-input py-1 ${isViewMode ? 'read-only-field' : ''} ${actionData?.errors?.template_abbreviation ? 'input-error' : ''}`}
id="template-abbreviation"
name="template_abbreviation"
placeholder="请输入模板简称,如:印章"
value={formData.template_abbreviation || ''}
onChange={handleInputChange}
readOnly={isViewMode}
required={formData.template_type === 'VLM_Extraction'}
/>
{actionData?.errors?.template_abbreviation && (
<div className="error-message">{actionData.errors.template_abbreviation}</div>
)}
<div className="help-text text-xs">VLM抽取类型必须填写</div>
</div>
{/* 模板描述 */}
<div className="form-group mb-3">
+6 -3
View File
@@ -9,6 +9,7 @@ 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 { toastService } from "~/components/ui";
export function links() {
return [{ rel: "stylesheet", href: indexStyles }];
@@ -214,13 +215,15 @@ export default function RuleGroupsIndex() {
setExpandedGroups(prev => prev.filter(id => id !== groupId));
// 显示成功消息
alert('删除成功');
// alert('删除成功');
toastService.success('删除成功')
} else {
alert(`删除失败: ${result.error}`);
toastService.error(`删除失败: ${result.error}`);
console.error(`删除失败: ${result.error}`);
}
} catch (error) {
console.error('删除分组失败:', error);
alert('删除分组失败,请稍后重试');
toastService.error('删除分组失败,请稍后重试');
}
}
};
+38 -4
View File
@@ -51,6 +51,7 @@ import { RuleContext } from "~/contexts/RuleContext";
import { postgrestGet, postgrestPost, postgrestPut } from "~/api/postgrest-client";
import { toastService } from '~/components/ui/Toast';
import type { UserRole } from '~/root';
import { getPromptTemplateOptions } from '~/api/prompts/prompts';
export const meta: MetaFunction = () => {
return [
@@ -160,13 +161,16 @@ export default function RuleNew() {
const [formData, setFormData] = useState<EvaluationPoint>({});
const [evaluationPointGroups, setEvaluationPointGroups] = useState<EvaluationPointGroup[]>([]);
// 判断表单是否为只读模式
const isReadOnly = userRole === 'common';
// 添加用于共享的字段数据状态
const [extractionFields, setExtractionFields] = useState<string[]>([]);
// VLM字段类型选项
const [vlmFieldTypeOptions, setVlmFieldTypeOptions] = useState<Array<{ value: string; label: string }>>([]);
/**
* 从表单数据中提取所有字段
* 用于编辑模式下初始化字段数据
@@ -355,6 +359,34 @@ export default function RuleNew() {
}
}, [frontendJWT]);
/**
* 获取VLM字段类型选项
* 从API获取多模态抽取的字段类型选项
*/
const fetchVlmFieldTypeOptions = useCallback(async () => {
try {
// console.log("获取VLM字段类型选项");
const response = await getPromptTemplateOptions('VLM_Extraction', frontendJWT);
if (response.data && Array.isArray(response.data)) {
// 添加自定义选项
const optionsWithCustom = [
...response.data,
{ value: 'custom', label: '自定义' }
];
setVlmFieldTypeOptions(optionsWithCustom);
} else if (response.error) {
console.error('获取VLM字段类型选项失败:', response.error);
// 使用默认选项作为备选
setVlmFieldTypeOptions(EVALUATION_OPTIONS.vlmFieldTypeOptions);
}
} catch (error) {
console.error('获取VLM字段类型选项失败:', error);
// 使用默认选项作为备选
setVlmFieldTypeOptions(EVALUATION_OPTIONS.vlmFieldTypeOptions);
}
}, [frontendJWT]);
const handleSave = async () => {
// console.log("保存评查点", formData);
@@ -917,7 +949,9 @@ export default function RuleNew() {
// 获取评查点组数据
fetchEvaluationPointGroups();
}, [location.search, fetchEvaluationPoint, fetchEvaluationPointGroups, resetFormData]);
// 获取VLM字段类型选项
fetchVlmFieldTypeOptions();
}, [location.search, fetchEvaluationPoint, fetchEvaluationPointGroups, fetchVlmFieldTypeOptions, resetFormData]);
// 渲染页面内容
return (
@@ -958,7 +992,7 @@ export default function RuleNew() {
onChange={handleExtractionSettingsChange}
initialData={formData}
promptTypeOptions={EVALUATION_OPTIONS.llmPromptTypeOptions}
vlmFieldTypeOptions={EVALUATION_OPTIONS.vlmFieldTypeOptions}
vlmFieldTypeOptions={vlmFieldTypeOptions.length > 0 ? vlmFieldTypeOptions : EVALUATION_OPTIONS.vlmFieldTypeOptions}
/>
</div>