fix: 1.接入ai_suggestion.
2. 接入合同起草功能。
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import type { MetaFunction, LoaderFunctionArgs } from '@remix-run/node';
|
||||
import { useLoaderData, useNavigate } from '@remix-run/react';
|
||||
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 { 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';
|
||||
|
||||
// 导入FilePreview组件
|
||||
import { FilePreview } from '~/components/reviews';
|
||||
@@ -36,26 +39,26 @@ export const handle = {
|
||||
|
||||
export async function loader({ params, request }: LoaderFunctionArgs) {
|
||||
const templateId = params.id!;
|
||||
|
||||
|
||||
// 获取 JWT
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
const jwt = frontendJWT || undefined;
|
||||
|
||||
|
||||
try {
|
||||
const response = await getContractTemplate(templateId, jwt);
|
||||
|
||||
|
||||
if (response.error) {
|
||||
throw new Response(response.error, { status: response.status || 404 });
|
||||
}
|
||||
|
||||
|
||||
if (!response.data) {
|
||||
throw new Response('模板未找到', { status: 404 });
|
||||
}
|
||||
|
||||
|
||||
// 添加调试信息
|
||||
// console.log('模板详情数据:', response.data);
|
||||
// console.log('分类信息:', response.data.category);
|
||||
|
||||
|
||||
return { template: response.data };
|
||||
} catch (error) {
|
||||
console.error('加载模板详情失败:', error);
|
||||
@@ -63,9 +66,61 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action 函数:处理起草合同请求
|
||||
*/
|
||||
export async function action({ request, params }: ActionFunctionArgs) {
|
||||
const templateId = parseInt(params.id || '0');
|
||||
|
||||
if (!templateId) {
|
||||
return Response.json({ error: '模板ID无效' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 获取用户信息和JWT
|
||||
const { userInfo, frontendJWT } = await getUserSession(request);
|
||||
if (!userInfo?.sub) {
|
||||
return Response.json({ error: '未登录' }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析表单数据
|
||||
const formData = await request.formData();
|
||||
const title = formData.get('title') as string;
|
||||
const draftFilePath = formData.get('draftFilePath') as string | null;
|
||||
|
||||
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
|
||||
// );
|
||||
|
||||
// 重定向到草稿编辑页面
|
||||
// return redirect(`/contract-draft/${draft.id}`);
|
||||
return redirect(`/contract-draft/1`);
|
||||
} catch (error) {
|
||||
console.error('[Template Detail] 创建草稿失败:', error);
|
||||
return Response.json(
|
||||
{ error: error instanceof Error ? error.message : '创建草稿失败' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function ContractTemplateDetail() {
|
||||
const { template }: { template: ContractTemplate } = useLoaderData<typeof loader>();
|
||||
const navigate = useNavigate();
|
||||
const submit = useSubmit();
|
||||
const [isCreatingDraft, setIsCreatingDraft] = useState(false);
|
||||
// 注释掉收藏功能
|
||||
// const [isFavorited, setIsFavorited] = useState(false);
|
||||
|
||||
@@ -75,6 +130,7 @@ export default function ContractTemplateDetail() {
|
||||
|
||||
// 使用统一的下载方法(与 rules-files.tsx 相同)
|
||||
const handleDownload = async () => {
|
||||
|
||||
if (!template.file_path) {
|
||||
toastService.error('文件路径不存在,无法下载');
|
||||
return;
|
||||
@@ -84,6 +140,7 @@ export default function ContractTemplateDetail() {
|
||||
// 使用axios封装的下载方法
|
||||
const blob = await downloadFile(template.file_path);
|
||||
|
||||
|
||||
// 创建Blob URL
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
@@ -120,6 +177,29 @@ export default function ContractTemplateDetail() {
|
||||
}
|
||||
};
|
||||
|
||||
// 起草合同
|
||||
const handleStartDraft = () => {
|
||||
if (isCreatingDraft) return;
|
||||
|
||||
// 生成默认标题
|
||||
// const defaultTitle = `${template.title}-${new Date().toLocaleDateString('zh-CN').replace(/\//g, '')}`;
|
||||
|
||||
// // 提示用户输入标题
|
||||
// const title = prompt('请输入合同标题:', defaultTitle);
|
||||
// if (!title) return;
|
||||
|
||||
setIsCreatingDraft(true);
|
||||
|
||||
// 使用 Remix 的 submit 提交表单
|
||||
const formData = new FormData();
|
||||
// formData.append('title', title.trim());
|
||||
formData.append('title', '买卖合同-拟起草合同');
|
||||
// 可选:如果需要复制文件,可以先调用文件复制服务,然后传递 draftFilePath
|
||||
// formData.append('draftFilePath', draftFilePath);
|
||||
|
||||
submit(formData, { method: 'post' });
|
||||
};
|
||||
|
||||
/* const handleFavorite = () => {
|
||||
setIsFavorited(!isFavorited);
|
||||
console.log('收藏状态:', !isFavorited);
|
||||
@@ -247,15 +327,32 @@ export default function ContractTemplateDetail() {
|
||||
</div>
|
||||
|
||||
<div className="detail-actions flex gap-3">
|
||||
<button
|
||||
<button
|
||||
className="detail-btn primary bg-primary text-white px-6 py-3 rounded-lg flex items-center gap-2 hover:bg-primary-hover"
|
||||
onClick={handleStartDraft}
|
||||
disabled={isCreatingDraft}
|
||||
>
|
||||
{isCreatingDraft ? (
|
||||
<>
|
||||
<i className="ri-loader-4-line animate-spin"></i>
|
||||
创建中...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<i className="ri-edit-line"></i>
|
||||
起草合同
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
className="detail-btn secondary bg-white border border-gray-200 px-6 py-3 rounded-lg flex items-center gap-2 hover:border-primary"
|
||||
onClick={handleDownload}
|
||||
>
|
||||
<i className="ri-download-line"></i>
|
||||
立即下载使用
|
||||
下载模板
|
||||
</button>
|
||||
{template.pdf_file_path && (
|
||||
<button
|
||||
<button
|
||||
className="detail-btn secondary bg-white border border-gray-200 px-6 py-3 rounded-lg flex items-center gap-2 hover:border-primary"
|
||||
onClick={handlePreview}
|
||||
>
|
||||
@@ -336,38 +433,15 @@ export default function ContractTemplateDetail() {
|
||||
<div className="content-section mb-8" id="template-preview">
|
||||
<h3 className="section-title text-xl font-semibold mb-4">合同预览</h3>
|
||||
<div className="border border-gray-200 rounded-lg overflow-hidden">
|
||||
{/* 使用更强的样式隔离 */}
|
||||
<div
|
||||
className="file-preview-isolation"
|
||||
style={{
|
||||
// 使用CSS变量避免继承
|
||||
'--font-family': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||||
'--font-size': '14px',
|
||||
'--line-height': '1.5',
|
||||
'--text-color': '#333333',
|
||||
'--bg-color': '#ffffff',
|
||||
|
||||
// 强制重置所有可能的样式
|
||||
all: 'unset',
|
||||
display: 'block',
|
||||
fontFamily: 'var(--font-family)',
|
||||
fontSize: 'var(--font-size)',
|
||||
lineHeight: 'var(--line-height)',
|
||||
color: 'var(--text-color)',
|
||||
backgroundColor: 'var(--bg-color)',
|
||||
width: '100%',
|
||||
minHeight: '600px',
|
||||
position: 'relative',
|
||||
isolation: 'isolate', // 创建新的层叠上下文
|
||||
contain: 'layout style', // CSS容器化
|
||||
zIndex: 0
|
||||
} as React.CSSProperties}
|
||||
<div
|
||||
className="file-preview-isolation w-full"
|
||||
>
|
||||
<FilePreview
|
||||
fileContent={fileContent}
|
||||
activeReviewPointResultId={null}
|
||||
targetPage={undefined}
|
||||
isStructuredView={false}
|
||||
isTemplate={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user