完善文档预览的效果修改

This commit is contained in:
2025-04-21 23:02:29 +08:00
parent 5c2c367856
commit cd2f060d87
15 changed files with 718 additions and 565 deletions
+130 -37
View File
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useEffect } 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";
@@ -7,6 +7,10 @@ import documentEditStyles from "~/styles/pages/documents_edit.css?url";
import { getDocument, updateDocument } from "~/api/files/documents";
import { getDocumentTypes } from "~/api/document-types/document-types";
import { FileTag } from "~/components/ui/FileTag";
import { toastService } from "~/components/ui/Toast";
import { Document, Page , pdfjs } from "react-pdf";
pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js';
export function links() {
return [{ rel: "stylesheet", href: documentEditStyles }];
@@ -19,6 +23,10 @@ export const meta: MetaFunction = () => {
];
};
export const handle = {
breadcrumb: "文档编辑"
};
// 文档审核状态定义
@@ -106,31 +114,31 @@ export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
// 从表单数据中提取字段
const type = formData.get("type_id") as string;
// const type = formData.get("type_id") as string;
const documentNumber = formData.get("document_number") as string;
const auditStatus = parseInt(formData.get("audit_status") as string);
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 }
);
}
// if (!type || auditStatus === undefined || isNaN(auditStatus)) {
// return Response.json(
// {
// error: "缺少必填字段",
// fieldErrors: {
// type_id: !type ? "文档类型不能为空" : null,
// audit_status: (auditStatus === undefined || isNaN(auditStatus)) ? "审核状态不能为空" : null
// }
// },
// { status: 400 }
// );
// }
console.log('提交更新:', { type, documentNumber, auditStatus, isTest, remark });
// console.log('提交更新:', { type, documentNumber, auditStatus, isTest, remark });
// 更新文档
const updateResponse = await updateDocument(id, {
type,
// type,
documentNumber,
auditStatus,
isTest,
@@ -138,17 +146,23 @@ export async function action({ request }: ActionFunctionArgs) {
});
if (updateResponse.error) {
console.error('更新文档失败:', updateResponse.error);
console.error('更新文档失败1:', updateResponse.error);
return Response.json({
error: updateResponse.error,
message: "更新文档失败,请检查提交的数据是否正确"
}, { status: updateResponse.status || 500 });
}
// toastService.success('更新文档成功');
// 重定向回文档列表
return redirect("/documents");
// return redirect("/documents");
return Response.json({
success: true,
message: "更新文档成功"
});
} catch (error) {
console.error("更新文档失败:", error);
console.error("更新文档失败2:", error);
return Response.json({
error: "更新文档失败",
message: error instanceof Error ? error.message : "发生未知错误"
@@ -160,8 +174,96 @@ export async function action({ request }: ActionFunctionArgs) {
export default function DocumentEdit() {
const { document, documentTypes } = useLoaderData<typeof loader>();
const actionData = useActionData<typeof action>();
const [numPages, setNumPages] = useState(0);
const [loadError, setLoadError] = useState<string | null>(null);
const navigate = useNavigate();
useEffect(() => {
// console.log('actionData', actionData);
if (actionData?.error) {
toastService.error('更新文档失败:' + actionData.error);
}
if (actionData?.success) {
toastService.success('更新文档成功');
}
}, [actionData]);
const onDocumentLoadSuccess = ({numPages}: {numPages: number}) => {
setNumPages(numPages);
console.log('文档加载成功', numPages);
}
const renderDocumentContent = () => {
return (
<div className="preview-content relative overflow-y-auto max-h-[1000px]">
<Document
file={'http://172.18.0.100:9000/docauditai/'+document.path}
onLoadSuccess={onDocumentLoadSuccess}
onLoadError={(error) => {
console.error("PDF加载错误:", error);
setLoadError("PDF文档加载失败:" + (error.message || "未知错误"));
}}
className="flex flex-col items-center w-full"
error={<div className="text-red-500">PDF文档加载失败</div>}
noData={<div></div>}
loading={<div className="text-center py-10">PDF加载中...</div>}
>
{renderAllPages()}
</Document>
</div>
)
}
const renderAllPages = () => {
// 如果还没有获取到PDF总页数,返回null
if (!numPages) return null;
// 用于存储所有页面组件的数组
const pages = [];
const styles = {
pageContainer: {
display: 'flex',
flexDirection: 'column' as const,
alignItems: 'center',
width: '100%',
position: 'relative' as const,
}
};
// 遍历每一页,生成对应的页面组件
for (let i = 1; i <= numPages; i++) {
// 为每一页创建组件
pages.push(
<div key={i} id={`page-${i}`} style={styles.pageContainer}>
{/* 页码标识,显示在页面上方 */}
<div className="text-center text-gray-500 text-sm mb-2"> {i} </div>
{/* 页面容器,应用缩放变换,设置相对定位用于放置评查点高亮 */}
<div
className="page-wrapper flex justify-center"
style={{
// transform: `scale(${zoomFactor})`, // 根据zoomLevel应用缩放
transformOrigin: 'top center', // 缩放原点设置为顶部中心
position: 'relative', // 相对定位,作为评查点高亮的定位参考
maxWidth: '100%', // 限制最大宽度
}}
>
{/* 渲染PDF页面组件 */}
<Page
pageNumber={i} // 当前页码
renderTextLayer={true} // 启用文本层,使文本可选择
renderAnnotationLayer={true} // 启用注释层,显示PDF内置注释
className="border border-gray-300 shadow-md" // 添加边框和阴影样式
/>
</div>
</div>
);
}
// 返回所有页面组件数组
return pages;
};
// 定义类型
interface DocType {
id: string | number;
@@ -275,7 +377,8 @@ export default function DocumentEdit() {
name="type_id"
className="form-select"
defaultValue={document.type}
disabled={document.fileStatus !== 'Processed'}
// disabled={document.fileStatus !== 'Processed'}
disabled={true}
required
>
{documentTypes.map((type: DocType) => (
@@ -384,22 +487,12 @@ export default function DocumentEdit() {
</Button>
</div>
</div>
<div className="preview-content">
<div className="preview-placeholder">
<i className={`ri-file-${document.fileType}-line`}></i>
<p></p>
<p className="text-xs mt-2">&quot;&quot;</p>
<Button
type="primary"
size="small"
icon="ri-external-link-line"
className="mt-4"
onClick={openPreview}
>
</Button>
</div>
</div>
{/* 预览窗口 */}
{loadError ?(<div className="text-red-500">
{loadError}
</div>):(
renderDocumentContent()
)}
</div>
</Card>