fix: 1.接入ai_suggestion.
2. 接入合同起草功能。
This commit is contained in:
+10
-10
@@ -83,7 +83,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
// console.log(`🔑 [Index Loader] 用户${hasSettingsAccess ? '有' : '没有'}系统设置权限`);
|
||||
// console.log(`🔑 [Index Loader] 系统设置子路由数量: ${settingsChildren.length}`);
|
||||
// console.log(`🔑 [Index Loader] 用户${hasCrossCheckingAccess ? '有' : '没有'}交叉评查权限`);
|
||||
// console.log(`🔑 [Index Loader] 用户${hasChatLLMAccess ? '有' : '没有'}智慧法务大模型权限`);
|
||||
// console.log(`🔑 [Index Loader] 用户${hasChatLLMAccess ? '有' : '没有'}智慧法务助手权限`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,8 +176,8 @@ export default function Index() {
|
||||
// 提取文档类型 IDs
|
||||
const typeIds = module.document_types?.map(dt => dt.id) || [];
|
||||
|
||||
// 🔑 验证文档类型(智慧法务大模型除外)
|
||||
if (module.name !== '智慧法务大模型' && typeIds.length === 0) {
|
||||
// 🔑 验证文档类型(智慧法务助手除外)
|
||||
if (module.name !== '智慧法务助手' && typeIds.length === 0) {
|
||||
toastService.error('该入口尚未关联文档类型,无法进入');
|
||||
console.warn('⚠️ [Index] 模块未关联文档类型:', module.name);
|
||||
return; // 阻止进入
|
||||
@@ -207,11 +207,11 @@ export default function Index() {
|
||||
// 合同相关模块 → 跳转到合同模板搜索
|
||||
targetPath = '/contract-template/search';
|
||||
// console.log('📌 [Index] 合同模块,跳转到:', targetPath);
|
||||
} else if (module.name === '智慧法务大模型') {
|
||||
// 智慧法务大模型 → 跳转到 AI 对话
|
||||
} else if (module.name === '智慧法务助手') {
|
||||
// 智慧法务助手 → 跳转到 AI 对话
|
||||
targetPath = '/chat-with-llm/chat';
|
||||
sessionStorage.setItem('selectedModulePicPath', '/images/icon_assistant.png')
|
||||
// console.log('📌 [Index] 智慧法务大模型,跳转到:', targetPath);
|
||||
// console.log('📌 [Index] 智慧法务助手,跳转到:', targetPath);
|
||||
} else {
|
||||
// console.log('📌 [Index] 其他模块,跳转到:', targetPath);
|
||||
}
|
||||
@@ -370,17 +370,17 @@ export default function Index() {
|
||||
{loaderData.entryModules && loaderData.entryModules.length > 0 ? (
|
||||
<>
|
||||
{loaderData.entryModules.map((module) => {
|
||||
// 判断是否为智慧法务大模型,如果是且有交叉评查权限,则在其之前插入交叉评查卡片
|
||||
const isLLMModule = module.name === '智慧法务大模型';
|
||||
// 判断是否为智慧法务助手,如果是且有交叉评查权限,则在其之前插入交叉评查卡片
|
||||
const isLLMModule = module.name === '智慧法务助手';
|
||||
|
||||
// 🔑 如果是智慧法务大模型且用户没有访问权限,则不渲染该模块
|
||||
// 🔑 如果是智慧法务助手且用户没有访问权限,则不渲染该模块
|
||||
if (isLLMModule && !loaderData.hasChatLLMAccess) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment key={module.id}>
|
||||
{/* 在智慧法务大模型之前插入交叉评查入口 */}
|
||||
{/* 在智慧法务助手之前插入交叉评查入口 */}
|
||||
{isLLMModule && loaderData.hasCrossCheckingAccess && (
|
||||
<div
|
||||
className="module-card"
|
||||
|
||||
@@ -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>
|
||||
|
||||
+151
-24
@@ -223,9 +223,11 @@ export default function DocumentsIndex() {
|
||||
const [showAttachmentUpload, setShowAttachmentUpload] = useState<boolean>(false);
|
||||
const [showTemplateUpload, setShowTemplateUpload] = useState<boolean>(false);
|
||||
const [selectedDocumentId, setSelectedDocumentId] = useState<number | null>(null);
|
||||
const [selectedDocumentName, setSelectedDocumentName] = useState<string | null>(null);
|
||||
const [selectedDocumentVersion, setSelectedDocumentVersion] = useState<number | null>(null);
|
||||
const [attachmentFiles, setAttachmentFiles] = useState<File[]>([]);
|
||||
const [templateFile, setTemplateFile] = useState<File | null>(null);
|
||||
const [attachmentMergeMode, setAttachmentMergeMode] = useState<'overwrite' | 'new'>('overwrite');
|
||||
const [attachmentMergeMode, setAttachmentMergeMode] = useState<'overwrite' | 'new'>('new');
|
||||
const [attachmentRemark, setAttachmentRemark] = useState<string>("");
|
||||
const [attachmentUploading, setAttachmentUploading] = useState<boolean>(false);
|
||||
const [templateUploading, setTemplateUploading] = useState<boolean>(false);
|
||||
@@ -356,6 +358,38 @@ export default function DocumentsIndex() {
|
||||
}
|
||||
}, [searchParams, fetchData, documentTypeIds]);
|
||||
|
||||
// 监听 documents 数据变化,自动修正不一致的展开状态
|
||||
useEffect(() => {
|
||||
if (documents.length === 0) return;
|
||||
|
||||
const newExpandedRows = new Set(expandedRows);
|
||||
let hasChanges = false;
|
||||
|
||||
// 检查每个展开的行
|
||||
expandedRows.forEach(docId => {
|
||||
const doc = documents.find(d => d.id === docId);
|
||||
|
||||
// 如果文档不存在或没有历史版本数据,自动折叠
|
||||
if (!doc || !doc.historyVersions || doc.historyVersions.length === 0) {
|
||||
console.warn(`自动折叠文档 ${docId}:数据不完整`);
|
||||
newExpandedRows.delete(docId);
|
||||
hasChanges = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 如果有变化,更新状态
|
||||
if (hasChanges) {
|
||||
setExpandedRows(newExpandedRows);
|
||||
|
||||
// 同时更新 documents 中的 isExpanded 状态
|
||||
setDocuments(prevDocs =>
|
||||
prevDocs.map(d =>
|
||||
newExpandedRows.has(d.id) ? { ...d, isExpanded: true } : { ...d, isExpanded: false }
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [documents, expandedRows]);
|
||||
|
||||
// 使用并更新缓存数据
|
||||
useEffect(() => {
|
||||
// 如果有缓存数据,先显示缓存,再在后台加载新数据
|
||||
@@ -881,6 +915,8 @@ export default function DocumentsIndex() {
|
||||
setAttachmentRemark("");
|
||||
setShowAttachmentUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null);
|
||||
setSelectedDocumentVersion(null);
|
||||
|
||||
// 刷新文档列表
|
||||
if (documentTypeIds && documentTypeIds.length > 0) {
|
||||
@@ -951,6 +987,8 @@ export default function DocumentsIndex() {
|
||||
setTemplateFile(null);
|
||||
setShowTemplateUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null);
|
||||
setSelectedDocumentVersion(null);
|
||||
|
||||
// 刷新文档列表
|
||||
if (documentTypeIds && documentTypeIds.length > 0) {
|
||||
@@ -981,11 +1019,28 @@ export default function DocumentsIndex() {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// 展开前检查是否有历史版本数据
|
||||
const hasHistoryData = doc.historyVersions && doc.historyVersions.length > 0;
|
||||
const hasHistoryCount = doc.historyCount && doc.historyCount > 0;
|
||||
|
||||
// 如果有历史版本计数但没有数据,可能是数据加载失败
|
||||
if (hasHistoryCount && !hasHistoryData) {
|
||||
console.warn(`文档 ${doc.id} 有 ${doc.historyCount} 个历史版本,但数据为空`);
|
||||
toastService.warning('历史版本数据加载失败,请刷新页面重试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果没有历史版本,不允许展开
|
||||
if (!hasHistoryCount) {
|
||||
console.log(`文档 ${doc.id} 没有历史版本`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 展开:显示历史版本
|
||||
newExpanded.add(doc.id);
|
||||
setExpandedRows(newExpanded);
|
||||
|
||||
// 更新展开状态(历史版本数据已经在主数据中了)
|
||||
// 更新展开状态
|
||||
setDocuments(prevDocs =>
|
||||
prevDocs.map(d =>
|
||||
d.id === doc.id ? { ...d, isExpanded: true } : d
|
||||
@@ -1077,6 +1132,36 @@ export default function DocumentsIndex() {
|
||||
<i className="ri-download-line"></i>
|
||||
下载
|
||||
</button>
|
||||
{parentDoc.type === '1' && historyDoc.fileStatus === 'Processed' && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs px-2 py-1 h-7 mr-1 hover:underline hover:text-primary"
|
||||
onClick={() => {
|
||||
setSelectedDocumentId(historyDoc.id);
|
||||
setSelectedDocumentName(historyDoc.name);
|
||||
setSelectedDocumentVersion(historyDoc.versionNumber || null);
|
||||
setShowAttachmentUpload(true);
|
||||
}}
|
||||
>
|
||||
<i className="ri-attachment-line"></i>
|
||||
追加附件
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs px-2 py-1 h-7 mr-1 text-gray-500 hover:underline hover:text-gray-700"
|
||||
onClick={() => {
|
||||
setSelectedDocumentId(historyDoc.id);
|
||||
setSelectedDocumentName(historyDoc.name);
|
||||
setSelectedDocumentVersion(historyDoc.versionNumber || null);
|
||||
setShowTemplateUpload(true);
|
||||
}}
|
||||
>
|
||||
<i className="ri-file-copy-line"></i>
|
||||
上传模板
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs px-2 py-1 h-7 text-error hover:underline hover:text-red-700"
|
||||
@@ -1294,22 +1379,26 @@ export default function DocumentsIndex() {
|
||||
</button>
|
||||
{record.type === '1' && record.fileStatus === 'Processed' && (
|
||||
<>
|
||||
<button
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs px-2 py-1 h-7 mr-1 hover:underline hover:text-primary"
|
||||
onClick={() => {
|
||||
setSelectedDocumentId(record.id);
|
||||
setSelectedDocumentName(record.name);
|
||||
setSelectedDocumentVersion(record.historyCount !== undefined && record.historyCount > 0 ? record.historyCount + 1 : null);
|
||||
setShowAttachmentUpload(true);
|
||||
}}
|
||||
>
|
||||
<i className="ri-attachment-line"></i>
|
||||
追加附件
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs px-2 py-1 h-7 mr-1 text-gray-500 hover:underline hover:text-gray-700"
|
||||
onClick={() => {
|
||||
setSelectedDocumentId(record.id);
|
||||
setSelectedDocumentName(record.name);
|
||||
setSelectedDocumentVersion(record.historyCount !== undefined && record.historyCount > 0 ? record.historyCount + 1 : null);
|
||||
setShowTemplateUpload(true);
|
||||
}}
|
||||
>
|
||||
@@ -1531,22 +1620,34 @@ export default function DocumentsIndex() {
|
||||
))}
|
||||
</tr>
|
||||
{/* 历史版本行 */}
|
||||
{doc.isExpanded && doc.historyVersions && doc.historyVersions.length > 0 && (
|
||||
{doc.isExpanded && (
|
||||
<>
|
||||
{doc.historyVersions.map((historyDoc) => renderHistoryRow(historyDoc, doc))}
|
||||
{/* 正在加载历史版本 */}
|
||||
{loadingHistory.has(doc.id) ? (
|
||||
<tr key={`loading-${doc.id}`} className="history-row">
|
||||
<td colSpan={columns.length} className="px-4 py-3">
|
||||
<div className="version-loading">
|
||||
<i className="ri-loader-4-line"></i>
|
||||
加载历史版本中...
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : doc.historyVersions && doc.historyVersions.length > 0 ? (
|
||||
/* 显示历史版本数据 */
|
||||
doc.historyVersions.map((historyDoc) => renderHistoryRow(historyDoc, doc))
|
||||
) : (
|
||||
/* 数据为空时的提示 */
|
||||
<tr key={`empty-${doc.id}`} className="history-row">
|
||||
<td colSpan={columns.length} className="px-4 py-3">
|
||||
<div className="text-center text-gray-500 text-sm py-2">
|
||||
<i className="ri-information-line mr-1"></i>
|
||||
暂无历史版本数据
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{/* 正在加载历史版本 */}
|
||||
{doc.isExpanded && loadingHistory.has(doc.id) && (
|
||||
<tr key={`loading-${doc.id}`} className="history-row">
|
||||
<td colSpan={columns.length} className="px-4 py-3">
|
||||
<div className="version-loading">
|
||||
<i className="ri-loader-4-line"></i>
|
||||
加载历史版本中...
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</tbody>
|
||||
@@ -1573,6 +1674,8 @@ export default function DocumentsIndex() {
|
||||
onClick={() => {
|
||||
setShowAttachmentUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null);
|
||||
setSelectedDocumentVersion(null);
|
||||
setAttachmentFiles([]);
|
||||
setAttachmentRemark("");
|
||||
}}
|
||||
@@ -1587,6 +1690,8 @@ export default function DocumentsIndex() {
|
||||
onClick={() => {
|
||||
setShowAttachmentUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null);
|
||||
setSelectedDocumentVersion(null);
|
||||
setAttachmentFiles([]);
|
||||
setAttachmentRemark("");
|
||||
}}
|
||||
@@ -1600,10 +1705,18 @@ export default function DocumentsIndex() {
|
||||
{/* 文档信息 */}
|
||||
<div className="bg-gray-50 p-3 rounded">
|
||||
<p className="text-sm text-gray-600">
|
||||
目标文档ID: <span className="font-medium">{selectedDocumentId}</span>
|
||||
{/* 目标文档ID: <span className="font-medium">{selectedDocumentId}</span> */}
|
||||
目标文档名称: <span className="font-medium">{selectedDocumentName}</span>
|
||||
{selectedDocumentVersion && (
|
||||
<span className="ml-2 text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">
|
||||
v{selectedDocumentVersion}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
支持.pdf、.docx、ZIP、RAR格式。ZIP/RAR内需要保证文件格式一致,否则报错
|
||||
支持.pdf、.docx、ZIP、RAR格式。
|
||||
<i className="ri-information-line mr-1"></i>
|
||||
ZIP/RAR内需要保证文件格式一致,否则报错
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1624,7 +1737,7 @@ export default function DocumentsIndex() {
|
||||
<label htmlFor="attachment-file-input" className="cursor-pointer">
|
||||
<i className="ri-attachment-line text-3xl text-gray-400 mb-2 block"></i>
|
||||
<p className="text-sm text-gray-600">点击选择文件或拖拽文件到此处</p>
|
||||
<p className="text-xs text-gray-500 mt-1">支持PDF、Word、ZIP、RAR格式,可多选</p>
|
||||
<p className="text-xs text-gray-500 mt-1">支持.pdf、.docx、.zip、.rar格式,可多选</p>
|
||||
</label>
|
||||
</div>
|
||||
{attachmentFiles.length > 0 && (
|
||||
@@ -1650,7 +1763,7 @@ export default function DocumentsIndex() {
|
||||
合并模式
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center">
|
||||
{/* <label className="flex items-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="mergeMode"
|
||||
@@ -1660,7 +1773,7 @@ export default function DocumentsIndex() {
|
||||
className="mr-2"
|
||||
/>
|
||||
<span className="text-sm">覆盖原文档(推荐)</span>
|
||||
</label>
|
||||
</label> */}
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
type="radio"
|
||||
@@ -1696,6 +1809,8 @@ export default function DocumentsIndex() {
|
||||
onClick={() => {
|
||||
setShowAttachmentUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null);
|
||||
setSelectedDocumentVersion(null);
|
||||
setAttachmentFiles([]);
|
||||
setAttachmentRemark("");
|
||||
}}
|
||||
@@ -1723,11 +1838,13 @@ export default function DocumentsIndex() {
|
||||
onClick={() => {
|
||||
setShowTemplateUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null);
|
||||
setSelectedDocumentVersion(null);
|
||||
setTemplateFile(null);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="bg-white rounded-lg p-6 w-full max-w-lg mx-4"
|
||||
className="bg-white rounded-lg p-6 w-full max-w-xl mx-4"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
@@ -1736,6 +1853,8 @@ export default function DocumentsIndex() {
|
||||
onClick={() => {
|
||||
setShowTemplateUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null);
|
||||
setSelectedDocumentVersion(null);
|
||||
setTemplateFile(null);
|
||||
}}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
@@ -1748,7 +1867,13 @@ export default function DocumentsIndex() {
|
||||
{/* 文档信息 */}
|
||||
<div className="bg-gray-50 p-3 rounded">
|
||||
<p className="text-sm text-gray-600">
|
||||
目标文档ID: <span className="font-medium">{selectedDocumentId}</span>
|
||||
{/* 目标文档ID: <span className="font-medium">{selectedDocumentId}</span> */}
|
||||
目标文档名称: <span className="font-medium">{selectedDocumentName}</span>
|
||||
{selectedDocumentVersion && (
|
||||
<span className="ml-2 text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">
|
||||
v{selectedDocumentVersion}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
支持.pdf、.docx格式,用于与合同文档进行结构对比
|
||||
@@ -1794,6 +1919,8 @@ export default function DocumentsIndex() {
|
||||
onClick={() => {
|
||||
setShowTemplateUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null);
|
||||
setSelectedDocumentVersion(null);
|
||||
setTemplateFile(null);
|
||||
}}
|
||||
disabled={templateUploading}
|
||||
|
||||
+28
-14
@@ -336,8 +336,9 @@ export default function FilesUpload() {
|
||||
// 附件追加状态
|
||||
const [showAttachmentUpload, setShowAttachmentUpload] = useState<boolean>(false);
|
||||
const [selectedDocumentId, setSelectedDocumentId] = useState<number | null>(null);
|
||||
const [selectedDocumentName, setSelectedDocumentName] = useState<string | null>(null);
|
||||
const [attachmentFiles, setAttachmentFiles] = useState<File[]>([]);
|
||||
const [attachmentMergeMode, setAttachmentMergeMode] = useState<'overwrite' | 'new'>('overwrite');
|
||||
const [attachmentMergeMode, setAttachmentMergeMode] = useState<'overwrite' | 'new'>('new');
|
||||
const [attachmentRemark, setAttachmentRemark] = useState<string>("");
|
||||
const [attachmentUploading, setAttachmentUploading] = useState<boolean>(false);
|
||||
|
||||
@@ -887,7 +888,7 @@ export default function FilesUpload() {
|
||||
if (files.length > 0) {
|
||||
// 检查主文件类型
|
||||
const selectedDocument = queueFiles.find(doc => doc.id === selectedDocumentId);
|
||||
const isMainFileDocx = selectedDocument?.path.toLowerCase().endsWith('.docx');
|
||||
const isMainFileDocx = selectedDocument?.path?.toLowerCase().endsWith('.docx');
|
||||
|
||||
// 验证文件类型,支持PDF、Word、ZIP、RAR
|
||||
const validFiles: File[] = [];
|
||||
@@ -899,7 +900,7 @@ export default function FilesUpload() {
|
||||
const isPdf = file.type === 'application/pdf' || fileName.endsWith('.pdf');
|
||||
const isValidType =
|
||||
isPdf ||
|
||||
file.type === 'application/msword' || fileName.endsWith('.doc') ||
|
||||
// file.type === 'application/msword' || fileName.endsWith('.doc') ||
|
||||
file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' || fileName.endsWith('.docx') ||
|
||||
file.type === 'application/zip' || fileName.endsWith('.zip') ||
|
||||
file.type === 'application/x-rar-compressed' || fileName.endsWith('.rar');
|
||||
@@ -973,6 +974,7 @@ export default function FilesUpload() {
|
||||
setAttachmentRemark("");
|
||||
setShowAttachmentUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null)
|
||||
|
||||
// 刷新文档列表
|
||||
await filterDocuments(documentTypeIds);
|
||||
@@ -1042,6 +1044,7 @@ export default function FilesUpload() {
|
||||
setTemplateFile(null);
|
||||
setShowTemplateUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null)
|
||||
|
||||
// 刷新文档列表
|
||||
await filterDocuments(documentTypeIds);
|
||||
@@ -2114,6 +2117,7 @@ export default function FilesUpload() {
|
||||
icon="ri-attachment-line"
|
||||
onClick={() => {
|
||||
setSelectedDocumentId(record.id);
|
||||
setSelectedDocumentName(record.name)
|
||||
setShowAttachmentUpload(true);
|
||||
}}
|
||||
className="text-xs px-2 py-1 h-7"
|
||||
@@ -2126,6 +2130,7 @@ export default function FilesUpload() {
|
||||
icon="ri-file-copy-line"
|
||||
onClick={() => {
|
||||
setSelectedDocumentId(record.id);
|
||||
setSelectedDocumentName(record.name)
|
||||
setShowTemplateUpload(true);
|
||||
}}
|
||||
className="text-xs px-2 py-1 h-7"
|
||||
@@ -2290,7 +2295,7 @@ export default function FilesUpload() {
|
||||
ref={contractMainFileRef}
|
||||
multiple={false}
|
||||
accept=".pdf,.docx"
|
||||
tipText="请上传合同主文件,格式:PDF/Word"
|
||||
tipText="请上传合同主文件,格式:.pdf/.docx"
|
||||
mainText="上传合同主文件"
|
||||
buttonText="选择主文件"
|
||||
icon="ri-file-text-line"
|
||||
@@ -2327,7 +2332,7 @@ export default function FilesUpload() {
|
||||
ref={contractAttachmentFileRef}
|
||||
multiple={false}
|
||||
accept=".pdf,.docx"
|
||||
tipText="请上传合同附件,格式:PDF/Word"
|
||||
tipText="请上传合同附件,格式:.pdf/.docx"
|
||||
mainText="上传合同附件"
|
||||
buttonText="选择附件"
|
||||
icon="ri-file-copy-line"
|
||||
@@ -2362,7 +2367,7 @@ export default function FilesUpload() {
|
||||
onFilesSelected={handleContractTemplateFilesSelected}
|
||||
multiple={false}
|
||||
accept=".pdf,.docx"
|
||||
tipText="请上传合同模板,格式:PDF/Word"
|
||||
tipText="请上传合同模板,格式:.pdf/.docx"
|
||||
mainText="上传合同模板"
|
||||
buttonText="选择模板"
|
||||
icon="ri-file-copy-line"
|
||||
@@ -2600,11 +2605,13 @@ export default function FilesUpload() {
|
||||
if (e.target === e.currentTarget) {
|
||||
setShowAttachmentUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null)
|
||||
setAttachmentFiles([]);
|
||||
setAttachmentRemark("");
|
||||
setAttachmentMergeMode('overwrite');
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
>
|
||||
<div className="bg-white rounded-lg p-6 w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
@@ -2613,6 +2620,7 @@ export default function FilesUpload() {
|
||||
onClick={() => {
|
||||
setShowAttachmentUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null)
|
||||
setAttachmentFiles([]);
|
||||
setAttachmentRemark("");
|
||||
}}
|
||||
@@ -2626,10 +2634,11 @@ export default function FilesUpload() {
|
||||
{/* 文档信息 */}
|
||||
<div className="bg-gray-50 p-3 rounded">
|
||||
<p className="text-sm text-gray-600">
|
||||
目标文档ID: <span className="font-medium">{selectedDocumentId}</span>
|
||||
{/* 目标文档ID: <span className="font-medium">{selectedDocumentId}</span> */}
|
||||
目标文档名称: <span className="font-medium">{selectedDocumentName}</span>
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
支持PDF、Word、ZIP、RAR格式,ZIP/RAR内仅合并其中的PDF文件
|
||||
支持.pdf、.docx、.zip、.rar格式。<i className="ri-information-2-line mr-1"></i>ZIP/RAR内需要保证文件格式一致,否则报错
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -2642,7 +2651,7 @@ export default function FilesUpload() {
|
||||
onFilesSelected={handleAttachmentFilesSelected}
|
||||
multiple={true}
|
||||
accept=".pdf,.docx,.zip,.rar"
|
||||
tipText="支持PDF、Word、ZIP、RAR格式,可多选"
|
||||
tipText="支持.pdf、.docx、.zip、.rar格式,可多选"
|
||||
mainText="选择附件文件"
|
||||
buttonText="选择文件"
|
||||
icon="ri-attachment-line"
|
||||
@@ -2652,7 +2661,7 @@ export default function FilesUpload() {
|
||||
<p className="text-sm text-green-600 mb-2">
|
||||
<i className="ri-checkbox-circle-line"></i> 已选择 {attachmentFiles.length} 个文件
|
||||
</p>
|
||||
<div className="space-y-1 max-h-32 overflow-y-auto">
|
||||
<div className="space-y-1 max-h-32 overflow-y-auto">
|
||||
{attachmentFiles.map((file, index) => (
|
||||
<div key={index} className="text-xs text-gray-600 bg-gray-50 p-2 rounded">
|
||||
<i className="ri-file-line mr-1"></i>
|
||||
@@ -2670,7 +2679,7 @@ export default function FilesUpload() {
|
||||
合并模式
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center">
|
||||
{/* <label className="flex items-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="mergeMode"
|
||||
@@ -2680,7 +2689,7 @@ export default function FilesUpload() {
|
||||
className="mr-2"
|
||||
/>
|
||||
<span className="text-sm">覆盖原文档(推荐)</span>
|
||||
</label>
|
||||
</label> */}
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
type="radio"
|
||||
@@ -2716,6 +2725,7 @@ export default function FilesUpload() {
|
||||
onClick={() => {
|
||||
setShowAttachmentUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null)
|
||||
setAttachmentFiles([]);
|
||||
setAttachmentRemark("");
|
||||
}}
|
||||
@@ -2746,6 +2756,7 @@ export default function FilesUpload() {
|
||||
if (e.target === e.currentTarget) {
|
||||
setShowTemplateUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null)
|
||||
setTemplateFile(null);
|
||||
}
|
||||
}}
|
||||
@@ -2757,6 +2768,7 @@ export default function FilesUpload() {
|
||||
onClick={() => {
|
||||
setShowTemplateUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null)
|
||||
setTemplateFile(null);
|
||||
}}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
@@ -2769,7 +2781,8 @@ export default function FilesUpload() {
|
||||
{/* 文档信息 */}
|
||||
<div className="bg-gray-50 p-3 rounded">
|
||||
<p className="text-sm text-gray-600">
|
||||
目标文档ID: <span className="font-medium">{selectedDocumentId}</span>
|
||||
{/* 目标文档ID: <span className="font-medium">{selectedDocumentId}</span> */}
|
||||
目标文档名称: <span className="font-medium">{selectedDocumentName}</span>
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
支持.pdf、.docx格式,用于与合同文档进行结构对比
|
||||
@@ -2810,6 +2823,7 @@ export default function FilesUpload() {
|
||||
onClick={() => {
|
||||
setShowTemplateUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
setSelectedDocumentName(null)
|
||||
setTemplateFile(null);
|
||||
}}
|
||||
disabled={templateUploading}
|
||||
|
||||
@@ -157,17 +157,17 @@ export default function PromptsIndex() {
|
||||
const canViewTemplate = canView('prompt_template');
|
||||
|
||||
// 调试信息
|
||||
// useEffect(() => {
|
||||
// console.log('📋 [Prompts] 模板数据:', templates);
|
||||
// console.log('📋 [Prompts] 用户角色:', userRole);
|
||||
// console.log('📋 [Prompts] 权限列表:', permissions);
|
||||
// console.log('📋 [Prompts] 权限检查结果:', {
|
||||
// canCreate: canCreateTemplate,
|
||||
// canEdit: canEditTemplate,
|
||||
// canDelete: canDeleteTemplate,
|
||||
// canView: canViewTemplate
|
||||
// });
|
||||
// }, [userRole, permissions, templates, canCreateTemplate, canEditTemplate, canDeleteTemplate, canViewTemplate]);
|
||||
useEffect(() => {
|
||||
console.log('📋 [Prompts] 模板数据:', templates);
|
||||
// console.log('📋 [Prompts] 用户角色:', userRole);
|
||||
// console.log('📋 [Prompts] 权限列表:', permissions);
|
||||
// console.log('📋 [Prompts] 权限检查结果:', {
|
||||
// canCreate: canCreateTemplate,
|
||||
// canEdit: canEditTemplate,
|
||||
// canDelete: canDeleteTemplate,
|
||||
// canView: canViewTemplate
|
||||
// });
|
||||
}, [userRole, permissions, templates, canCreateTemplate, canEditTemplate, canDeleteTemplate, canViewTemplate]);
|
||||
|
||||
// 处理搜索名称
|
||||
const handleNameSearch = (value: string) => {
|
||||
|
||||
@@ -1361,9 +1361,18 @@ export default function RolePermissions() {
|
||||
marginLeft: '8px',
|
||||
padding: '2px 8px',
|
||||
fontSize: '12px',
|
||||
backgroundColor: selectedPermCount > 0 ? '#e6f7ed' : '#f5f5f5',
|
||||
color: selectedPermCount > 0 ? '#52c41a' : '#666',
|
||||
border: selectedPermCount > 0 ? '1px solid #b7eb8f' : '1px solid #d9d9d9',
|
||||
backgroundColor:
|
||||
selectedPermCount === permissions.length ? '#e6f7ed' : // 全部选中:绿色
|
||||
selectedPermCount > 0 ? '#fff7e6' : // 部分选中:浅橙色
|
||||
'#f5f5f5', // 未选中:灰色
|
||||
color:
|
||||
selectedPermCount === permissions.length ? '#52c41a' : // 全部选中:绿色
|
||||
selectedPermCount > 0 ? '#fa8c16' : // 部分选中:橙色
|
||||
'#666', // 未选中:灰色
|
||||
border:
|
||||
selectedPermCount === permissions.length ? '1px solid #b7eb8f' : // 全部选中:绿色
|
||||
selectedPermCount > 0 ? '1px solid #ffd591' : // 部分选中:浅橙色
|
||||
'1px solid #d9d9d9', // 未选中:灰色
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
display: 'inline-flex',
|
||||
|
||||
Reference in New Issue
Block a user