优化评查详情,新增信息提示框组件
This commit is contained in:
+169
-66
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useLoaderData, useActionData, useNavigate, Form } from "@remix-run/react";
|
||||
import { redirect, type ActionFunctionArgs, type LoaderFunctionArgs, type MetaFunction } from "@remix-run/node";
|
||||
import { Card } from "~/components/ui/Card";
|
||||
@@ -27,7 +27,18 @@ export const handle = {
|
||||
breadcrumb: "文档编辑"
|
||||
};
|
||||
|
||||
|
||||
// 定义action返回数据类型
|
||||
export interface ActionData {
|
||||
success?: boolean;
|
||||
error?: string;
|
||||
fieldErrors?: {
|
||||
document_number?: string;
|
||||
audit_status?: string;
|
||||
remark?: string;
|
||||
general?: string;
|
||||
};
|
||||
values?: Record<string, string>;
|
||||
}
|
||||
|
||||
// 文档审核状态定义
|
||||
enum DocumentAuditStatus {
|
||||
@@ -120,21 +131,19 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
const isTest = formData.get("is_test_document") === "on";
|
||||
const remark = formData.get("remark") as string;
|
||||
|
||||
// 验证必填字段
|
||||
// if (!type || auditStatus === undefined || isNaN(auditStatus)) {
|
||||
// return Response.json(
|
||||
// {
|
||||
// error: "缺少必填字段",
|
||||
// fieldErrors: {
|
||||
// type_id: !type ? "文档类型不能为空" : null,
|
||||
// audit_status: (auditStatus === undefined || isNaN(auditStatus)) ? "审核状态不能为空" : null
|
||||
// }
|
||||
// },
|
||||
// { status: 400 }
|
||||
// );
|
||||
// }
|
||||
// 表单验证
|
||||
const errors: ActionData["fieldErrors"] = {};
|
||||
|
||||
// console.log('提交更新:', { type, documentNumber, auditStatus, isTest, remark });
|
||||
if (isNaN(auditStatus)) {
|
||||
errors.audit_status = "审核状态必须选择";
|
||||
}
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
return Response.json({
|
||||
fieldErrors: errors,
|
||||
values: Object.fromEntries(formData) as Record<string, string>
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
// 更新文档
|
||||
const updateResponse = await updateDocument(id, {
|
||||
@@ -146,26 +155,25 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
});
|
||||
|
||||
if (updateResponse.error) {
|
||||
console.error('更新文档失败1:', updateResponse.error);
|
||||
console.error('更新文档失败:', updateResponse.error);
|
||||
return Response.json({
|
||||
error: updateResponse.error,
|
||||
message: "更新文档失败,请检查提交的数据是否正确"
|
||||
fieldErrors: {
|
||||
general: "更新文档失败,请检查提交的数据是否正确"
|
||||
},
|
||||
values: Object.fromEntries(formData) as Record<string, string>
|
||||
}, { status: updateResponse.status || 500 });
|
||||
}
|
||||
|
||||
// toastService.success('更新文档成功');
|
||||
|
||||
// 重定向回文档列表
|
||||
// return redirect("/documents");
|
||||
return Response.json({
|
||||
success: true,
|
||||
message: "更新文档成功"
|
||||
});
|
||||
toastService.success('更新文档成功');
|
||||
return redirect("/documents");
|
||||
} catch (error) {
|
||||
console.error("更新文档失败2:", error);
|
||||
return Response.json({
|
||||
error: "更新文档失败",
|
||||
message: error instanceof Error ? error.message : "发生未知错误"
|
||||
fieldErrors: {
|
||||
general: error instanceof Error ? error.message : "发生未知错误"
|
||||
}
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -173,20 +181,117 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
// 文档编辑页面组件
|
||||
export default function DocumentEdit() {
|
||||
const { document, documentTypes } = useLoaderData<typeof loader>();
|
||||
const actionData = useActionData<typeof action>();
|
||||
const actionData = useActionData<ActionData>();
|
||||
const navigate = useNavigate();
|
||||
const [numPages, setNumPages] = useState(0);
|
||||
const [loadError, setLoadError] = useState<string | null>(null);
|
||||
const navigate = useNavigate();
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
|
||||
// 表单状态管理 - 使用受控组件
|
||||
const [formValues, setFormValues] = useState({
|
||||
documentNumber: document.documentNumber || "",
|
||||
auditStatus: document.auditStatus,
|
||||
isTest: document.isTest || false,
|
||||
remark: document.remark || ""
|
||||
});
|
||||
|
||||
// 表单验证错误状态
|
||||
const [formErrors, setFormErrors] = useState<{
|
||||
documentNumber?: string;
|
||||
auditStatus?: string;
|
||||
remark?: string;
|
||||
general?: string;
|
||||
}>({});
|
||||
|
||||
// 字段是否被触摸过(用于确定何时显示错误)
|
||||
const [touchedFields, setTouchedFields] = useState({
|
||||
documentNumber: false,
|
||||
auditStatus: false,
|
||||
remark: false
|
||||
});
|
||||
|
||||
// 从 actionData 初始化表单错误
|
||||
useEffect(() => {
|
||||
// console.log('actionData', actionData);
|
||||
if (actionData?.error) {
|
||||
toastService.error('更新文档失败:' + actionData.error);
|
||||
}
|
||||
if (actionData?.success) {
|
||||
toastService.success('更新文档成功');
|
||||
if (actionData?.fieldErrors) {
|
||||
// general 是loader的时候返回的错误信息
|
||||
setFormErrors(actionData.fieldErrors);
|
||||
}
|
||||
}, [actionData]);
|
||||
|
||||
// 验证表单字段
|
||||
const validateField = (field: string, value: string | number | undefined): string => {
|
||||
switch (field) {
|
||||
case 'auditStatus': {
|
||||
const statusValue = typeof value === 'string' ? parseInt(value) : value;
|
||||
return statusValue === undefined || isNaN(statusValue as number) ? "审核状态必须选择" : "";
|
||||
}
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
// 处理字段改变
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
||||
const { name, value, type } = e.target;
|
||||
|
||||
if (type === 'checkbox') {
|
||||
const checked = (e.target as HTMLInputElement).checked;
|
||||
setFormValues(prev => ({
|
||||
...prev,
|
||||
[name === 'is_test_document' ? 'isTest' : name]: checked
|
||||
}));
|
||||
} else {
|
||||
setFormValues(prev => ({
|
||||
...prev,
|
||||
[name === 'document_number' ? 'documentNumber' :
|
||||
name === 'audit_status' ? 'auditStatus' : name]:
|
||||
name === 'audit_status' ? (parseInt(value)) : value
|
||||
}));
|
||||
}
|
||||
|
||||
// 标记字段为已触摸
|
||||
if (name === 'document_number' || name === 'audit_status' || name === 'remark') {
|
||||
const fieldName = name === 'document_number' ? 'documentNumber' :
|
||||
name === 'audit_status' ? 'auditStatus' : name;
|
||||
setTouchedFields(prev => ({
|
||||
...prev,
|
||||
[fieldName]: true
|
||||
}));
|
||||
}
|
||||
|
||||
// 实时验证
|
||||
if (name === 'audit_status') {
|
||||
const statusValue = parseInt(value);
|
||||
const error = validateField('auditStatus', statusValue);
|
||||
setFormErrors(prev => ({
|
||||
...prev,
|
||||
auditStatus: error
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// 处理表单提交前验证
|
||||
const handleBeforeSubmit = (e: React.FormEvent) => {
|
||||
// 标记所有字段为已触摸
|
||||
setTouchedFields({
|
||||
documentNumber: true,
|
||||
auditStatus: true,
|
||||
remark: true
|
||||
});
|
||||
|
||||
// 验证所有字段
|
||||
const errors = {
|
||||
auditStatus: validateField('auditStatus', formValues.auditStatus)
|
||||
};
|
||||
|
||||
setFormErrors(errors);
|
||||
|
||||
// 如果有错误,阻止提交
|
||||
if (errors.auditStatus) {
|
||||
toastService.error('审核状态不能为空');
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const onDocumentLoadSuccess = ({numPages}: {numPages: number}) => {
|
||||
setNumPages(numPages);
|
||||
@@ -242,10 +347,8 @@ export default function DocumentEdit() {
|
||||
<div
|
||||
className="page-wrapper flex justify-center"
|
||||
style={{
|
||||
// transform: `scale(${zoomFactor})`, // 根据zoomLevel应用缩放
|
||||
transformOrigin: 'top center', // 缩放原点设置为顶部中心
|
||||
position: 'relative', // 相对定位,作为评查点高亮的定位参考
|
||||
maxWidth: '100%', // 限制最大宽度
|
||||
position: 'relative',
|
||||
maxWidth: '100%',
|
||||
}}
|
||||
>
|
||||
{/* 渲染PDF页面组件 */}
|
||||
@@ -270,14 +373,6 @@ export default function DocumentEdit() {
|
||||
name: string;
|
||||
}
|
||||
|
||||
// 状态
|
||||
const [localStatus, setLocalStatus] = useState<number>(document.auditStatus);
|
||||
|
||||
// 处理状态变更
|
||||
const handleStatusChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setLocalStatus(parseInt(e.target.value));
|
||||
};
|
||||
|
||||
// 获取文档类型名称
|
||||
const getDocumentTypeName = (typeId: string): string => {
|
||||
const docType = documentTypes.find((type: DocType) => type.id.toString() === typeId);
|
||||
@@ -299,8 +394,6 @@ export default function DocumentEdit() {
|
||||
|
||||
// 在新窗口打开文档预览
|
||||
const openPreview = () => {
|
||||
// 假设有一个预览URL的格式,比如 /preview?path=xxx
|
||||
// console.log('documentstest', document);
|
||||
const urlBefore = 'http://172.18.0.100:9000/docauditai/'
|
||||
const previewUrl = `${urlBefore}${document.path}`;
|
||||
window.open(previewUrl, '_blank');
|
||||
@@ -368,7 +461,15 @@ export default function DocumentEdit() {
|
||||
<i className="ri-information-line mr-2"></i> 您可以修改此文档的基本信息,但不能更改文档内容。如需修改内容,请删除后重新上传新文档。
|
||||
</div>
|
||||
|
||||
<Form id="edit-form" method="post">
|
||||
{/* 错误提示 */}
|
||||
{formErrors.general && (
|
||||
<div className="general-error mb-4">
|
||||
<i className="ri-error-warning-line mr-2"></i>
|
||||
{formErrors.general}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Form id="edit-form" method="post" ref={formRef} onSubmit={handleBeforeSubmit}>
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<div className="form-group">
|
||||
<label htmlFor="type-id" className="form-label">文档类型 <span className="text-red-500">*</span></label>
|
||||
@@ -376,8 +477,7 @@ export default function DocumentEdit() {
|
||||
id="type-id"
|
||||
name="type_id"
|
||||
className="form-select"
|
||||
defaultValue={document.type}
|
||||
// disabled={document.fileStatus !== 'Processed'}
|
||||
value={document.type}
|
||||
disabled={true}
|
||||
required
|
||||
>
|
||||
@@ -386,9 +486,6 @@ export default function DocumentEdit() {
|
||||
))}
|
||||
</select>
|
||||
<div className="text-sm text-secondary mt-1">更改文档类型将重新应用对应的评查规则</div>
|
||||
{actionData?.fieldErrors?.type_id && (
|
||||
<div className="text-red-500 text-sm mt-1">{actionData.fieldErrors.type_id}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
@@ -399,7 +496,8 @@ export default function DocumentEdit() {
|
||||
name="document_number"
|
||||
className="form-input"
|
||||
placeholder="请输入合同编号、许可证号等"
|
||||
defaultValue={document.documentNumber || ""}
|
||||
value={formValues.documentNumber}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<div className="text-sm text-secondary mt-1">如无编号可留空</div>
|
||||
</div>
|
||||
@@ -409,19 +507,20 @@ export default function DocumentEdit() {
|
||||
<select
|
||||
id="audit-status"
|
||||
name="audit_status"
|
||||
className="form-select"
|
||||
value={localStatus}
|
||||
onChange={handleStatusChange}
|
||||
className={`form-select ${touchedFields.auditStatus && formErrors.auditStatus ? 'error' : ''}`}
|
||||
value={formValues.auditStatus}
|
||||
onChange={handleChange}
|
||||
required
|
||||
>
|
||||
{/* <option value='全部'>请选择审核状态</option> */}
|
||||
<option value={DocumentAuditStatus.WAITING}>{STATUS_LABELS[DocumentAuditStatus.WAITING]}</option>
|
||||
<option value={DocumentAuditStatus.PROCESSING}>{STATUS_LABELS[DocumentAuditStatus.PROCESSING]}</option>
|
||||
<option value={DocumentAuditStatus.PASS}>{STATUS_LABELS[DocumentAuditStatus.PASS]}</option>
|
||||
<option value={DocumentAuditStatus.FAIL}>{STATUS_LABELS[DocumentAuditStatus.FAIL]}</option>
|
||||
</select>
|
||||
<div className="text-sm text-secondary mt-1">更改状态可能会影响此文档在列表中的显示和排序</div>
|
||||
{actionData?.fieldErrors?.audit_status && (
|
||||
<div className="text-red-500 text-sm mt-1">{actionData.fieldErrors.audit_status}</div>
|
||||
{touchedFields.auditStatus && formErrors.auditStatus && (
|
||||
<div className="text-red-500 text-sm mt-1">{formErrors.auditStatus}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -433,7 +532,8 @@ export default function DocumentEdit() {
|
||||
type="checkbox"
|
||||
id="is-test-document"
|
||||
name="is_test_document"
|
||||
defaultChecked={document.isTest}
|
||||
checked={formValues.isTest}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<span className="slider"></span>
|
||||
<span className="sr-only">标记为测试文档</span>
|
||||
@@ -450,7 +550,8 @@ export default function DocumentEdit() {
|
||||
className="form-textarea"
|
||||
placeholder="可输入文档的相关描述或备注信息"
|
||||
rows={3}
|
||||
defaultValue={document.remark || ""}
|
||||
value={formValues.remark}
|
||||
onChange={handleChange}
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -488,9 +589,11 @@ export default function DocumentEdit() {
|
||||
</div>
|
||||
</div>
|
||||
{/* 预览窗口 */}
|
||||
{loadError ?(<div className="text-red-500">
|
||||
{loadError}
|
||||
</div>):(
|
||||
{loadError ? (
|
||||
<div className="text-red-500">
|
||||
{loadError}
|
||||
</div>
|
||||
) : (
|
||||
renderDocumentContent()
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user