feat: 1. 完善起草合同页面的逻辑交互,对接minio的接口操作

This commit is contained in:
2025-12-05 20:17:37 +08:00
parent 3d1dbb3f97
commit 91b7518c99
21 changed files with 1249 additions and 1057 deletions
+66 -29
View File
@@ -1,18 +1,17 @@
import type { MetaFunction, LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node';
import { redirect } from '@remix-run/node';
import { useLoaderData, useNavigate, useSubmit } from '@remix-run/react';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { getContractTemplate } from '~/api/contract-template/templates';
import type { ContractTemplate } from '~/api/contract-template/templates';
import styles from '~/styles/pages/contract-template.css?url';
import filePreviewStyles from '~/styles/components/file-preview-isolation.css?url';
import { getUserSession } from '~/api/login/auth.server';
import { createDraftContract } from '~/api/contracts/draft-service.server';
import { apiRequest, downloadFile } from '~/api/axios-client';
// 导入FilePreview组件
import { FilePreview } from '~/components/reviews';
// 导入统一的下载方法和提示服务
import { downloadFile } from '~/api/axios-client';
import { toastService } from '~/components/ui/Toast';
export const links = () => [
@@ -70,7 +69,7 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
* Action 函数:处理起草合同请求
*/
export async function action({ request, params }: ActionFunctionArgs) {
const templateId = parseInt(params.id || '0');
const templateId = params.id!;
if (!templateId) {
return Response.json({ error: '模板ID无效' }, { status: 400 });
@@ -86,27 +85,60 @@ export async function action({ request, params }: ActionFunctionArgs) {
// 解析表单数据
const formData = await request.formData();
const title = formData.get('title') as string;
const draftFilePath = formData.get('draftFilePath') as string | null;
const originalFilePath = formData.get('originalFilePath') as string;
if (!title) {
return Response.json({ error: '标题不能为空' }, { status: 400 });
}
// 创建草稿记录(到时候可以换成接口,使用接口来在minio中生成备份文件:备份文件可以用时间戳+uuid来保证唯一性。)
// const draft = await createDraftContract(
// {
// templateId,
// title,
// draftFilePath: draftFilePath || undefined
// },
// parseInt(userInfo.sub),
// draftFilePath || undefined,
// frontendJWT || undefined
// );
if (!originalFilePath) {
return Response.json({ error: '文件路径不存在' }, { status: 400 });
}
// 重定向到草稿编辑页面
// return redirect(`/contract-draft/${draft.id}`);
return redirect(`/contract-draft/1`);
// 生成新文件路径
const area = userInfo.area || 'unknown';
const timestamp = Date.now();
const uuid = crypto.randomUUID();
// 提取文件目录和文件名
const lastSlashIndex = originalFilePath.lastIndexOf('/');
const directory = lastSlashIndex >= 0 ? originalFilePath.substring(0, lastSlashIndex) : '';
const fileName = lastSlashIndex >= 0 ? originalFilePath.substring(lastSlashIndex + 1) : originalFilePath;
// 提取文件扩展名
const lastDotIndex = fileName.lastIndexOf('.');
const baseName = lastDotIndex >= 0 ? fileName.substring(0, lastDotIndex) : fileName;
const extension = lastDotIndex >= 0 ? fileName.substring(lastDotIndex) : '';
// 构建新文件名
const newFileName = `${baseName}_${area}_${timestamp}_${uuid}${extension}`;
const newFilePath = directory ? `${directory}/${newFileName}` : newFileName;
console.log('[Draft] 复制文件:', { originalFilePath, newFilePath });
// 调用 MinIO 复制文件 API(需要传递 JWT)
const jwt = frontendJWT || undefined;
const copyResponse = await apiRequest('/api/v2/storage/files/copy', {
method: 'POST',
data: {
source_path: originalFilePath,
destination_path: newFilePath
},
headers: {
'Authorization': jwt ? `Bearer ${jwt}` : ''
}
});
if (copyResponse.error) {
console.error('[Draft] 文件复制失败:', copyResponse.error);
return Response.json({ error: `文件复制失败: ${copyResponse.error}` }, { status: 500 });
}
console.log('[Draft] 文件复制成功:', copyResponse.data);
// 重定向到草稿编辑页面,通过 URL 参数传递文件路径和模板 ID
const draftUrl = `/contract-draft/1?filePath=${encodeURIComponent(newFilePath)}&templateId=${templateId}&title=${encodeURIComponent(title)}`;
return redirect(draftUrl);
} catch (error) {
console.error('[Template Detail] 创建草稿失败:', error);
return Response.json(
@@ -124,6 +156,12 @@ export default function ContractTemplateDetail() {
// 注释掉收藏功能
// const [isFavorited, setIsFavorited] = useState(false);
// 防止页面加载时自动滚动到预览区域(由 Collabora iframe 的 tabIndex 导致)
useEffect(() => {
// 页面加载后立即滚动回顶部
window.scrollTo({ top: 0, behavior: 'instant' });
}, []);
const handleBack = () => {
navigate(-1);
};
@@ -181,21 +219,20 @@ export default function ContractTemplateDetail() {
const handleStartDraft = () => {
if (isCreatingDraft) return;
// 生成默认标题
// const defaultTitle = `${template.title}-${new Date().toLocaleDateString('zh-CN').replace(/\//g, '')}`;
if (!template.file_path) {
toastService.error('模板文件路径不存在,无法起草');
return;
}
// // 提示用户输入标题
// const title = prompt('请输入合同标题:', defaultTitle);
// if (!title) return;
// 生成默认标题
const defaultTitle = `${template.title}-${new Date().toLocaleDateString('zh-CN').replace(/\//g, '')}`;
setIsCreatingDraft(true);
// 使用 Remix 的 submit 提交表单
const formData = new FormData();
// formData.append('title', title.trim());
formData.append('title', '买卖合同-拟起草合同');
// 可选:如果需要复制文件,可以先调用文件复制服务,然后传递 draftFilePath
// formData.append('draftFilePath', draftFilePath);
formData.append('title', defaultTitle);
formData.append('originalFilePath', template.file_path);
submit(formData, { method: 'post' });
};
@@ -430,7 +467,7 @@ export default function ContractTemplateDetail() {
{/* 合同预览 - 只有当存在pdf_file_path时才显示 */}
{fileContent && (
<div className="content-section mb-8" id="template-preview">
<div className="content-section mb-8">
<h3 className="section-title text-xl font-semibold mb-4"></h3>
<div className="border border-gray-200 rounded-lg overflow-hidden">
<div