fix: 1.将主页和法务助手对话设置成手机也能够正确加载的响应式布局。

2. 修改合同重新上传模板的可接受文件类型,修改对接的上传模板对应的接口。
3. 交叉评查任务列表去除任务名称的点击效果。
4. 交叉评查文件预览在点击完成评查的按钮后会返回任务列表并打开任务的文档列表。
5.修复点击完成评查按钮造成页面刷新。
6. 修复创建任务的第3步无法返回列表。
This commit is contained in:
2025-11-12 15:51:39 +08:00
parent c20c168a13
commit 8a50671c39
11 changed files with 578 additions and 182 deletions
+12 -5
View File
@@ -38,21 +38,28 @@ export function Layout({ children, userRole = 'developer' as UserRole, frontendJ
const [selectedApp, setSelectedApp] = useState<AppModule>('');
const matches = useMatches() as Match[];
const location = useLocation();
// 检查当前路径是否应该隐藏侧边栏
const noLayoutPaths = ['/login', '/'];
const shouldHideSidebar = noLayoutPaths.includes(location.pathname);
// 检查当前路由是否应该隐藏默认面包屑
const shouldHideBreadcrumb = shouldHideSidebar || matches.some(match =>
const shouldHideBreadcrumb = shouldHideSidebar || matches.some(match =>
match.handle && match.handle.hideBreadcrumb === true
);
// 从sessionStorage中获取侧边栏状态和reviewType
useEffect(() => {
// 检查是否为移动端
const isMobile = window.innerWidth <= 768;
// 从localStorage获取侧边栏状态
const savedState = localStorage.getItem('sidebarCollapsed');
if (savedState) {
// 移动端默认收起,桌面端使用保存的状态
if (isMobile) {
setSidebarCollapsed(true);
} else if (savedState) {
setSidebarCollapsed(savedState === 'true');
}
+38 -3
View File
@@ -39,8 +39,24 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '', selec
const [isLoading, setIsLoading] = useState<boolean>(true); // 添加加载状态
const [menuItems, setMenuItems] = useState<MenuItem[]>([]); // 动态菜单项
const [isLoadingRoutes, setIsLoadingRoutes] = useState<boolean>(true); // 路由加载状态
const [isMobile, setIsMobile] = useState<boolean>(false); // 移动端检测
const navigate = useNavigate();
// 移动端检测
useEffect(() => {
const checkMobile = () => {
const mobile = window.innerWidth <= 768; // 768px以下视为移动端
setIsMobile(mobile);
};
// 初始检测
checkMobile();
// 监听窗口大小变化
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}, []);
// 获取用户路由权限
useEffect(() => {
const fetchUserRoutes = async () => {
@@ -254,8 +270,26 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '', selec
// })
return (
<div className={`sidebar ${collapsed ? 'collapsed' : ''} flex flex-col`}>
<div className="py-6 px-4 border-b border-gray-100 flex justify-between items-center">
<>
{/* 移动端遮罩层 */}
{isMobile && !collapsed && (
<div
className="sidebar-overlay"
onClick={onToggle}
role="button"
tabIndex={0}
aria-label="关闭侧边栏"
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onToggle();
}
}}
/>
)}
<div className={`sidebar ${collapsed ? 'collapsed' : ''} ${isMobile ? 'sidebar-mobile' : ''} flex flex-col`}>
<div className="py-6 px-4 border-b border-gray-100 flex justify-between items-center">
<div className="flex items-center"
onClick={() => {
navigate('/');
@@ -396,6 +430,7 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '', selec
{!collapsed && <span className="text-base"></span>}
</a>
</div>
</div>
</div>
</>
);
}
+90 -83
View File
@@ -10,7 +10,7 @@ import { UploadArea, type UploadAreaRef } from '~/components/ui/UploadArea';
import { Button } from '~/components/ui/Button';
import { toastService } from '~/components/ui/Toast';
// import { DOCUMENT_URL } from "~/api/axios-client";
import { uploadFileToBinary, uploadDocumentToServer } from '~/api/files/files-upload';
import { uploadContractTemplate } from '~/api/files/files-upload';
interface ReviewTabsProps {
activeTab: string;
@@ -22,6 +22,7 @@ interface ReviewTabsProps {
path?: string;
auditStatus?: number;
type?: string;
comparisonId?: number;
};
onConfirmResults: () => void;
jwtToken?: string | null;
@@ -117,23 +118,32 @@ export function ReviewTabs({ activeTab, onTabChange, children, fileInfo, onConfi
const handleTemplateFilesSelected = (files: FileList) => {
try {
if (files.length > 0) {
// 验证文件类型,允许PDF文件
// 验证文件类型,允许PDF和Word文件
const validFiles: File[] = [];
let hasInvalidFiles = false;
Array.from(files).forEach(file => {
if (file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf')) {
const fileName = file.name.toLowerCase();
const isValidType =
file.type === 'application/pdf' ||
fileName.endsWith('.pdf') ||
file.type === 'application/msword' ||
file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
fileName.endsWith('.doc') ||
fileName.endsWith('.docx');
if (isValidType) {
validFiles.push(file);
} else {
hasInvalidFiles = true;
console.error(`无效的文件类型: ${file.name}, 类型: ${file.type}`);
}
});
if (hasInvalidFiles) {
toastService.error('只能上传PDF格式的文件');
toastService.error('只能上传PDF或Word格式的文件');
}
if (validFiles.length > 0) {
setSelectedTemplateFiles(validFiles);
}
@@ -152,57 +162,42 @@ export function ReviewTabs({ activeTab, onTabChange, children, fileInfo, onConfi
return;
}
// 验证必需的参数
if (!fileInfo.id) {
toastService.error('文档ID不能为空');
return;
}
try {
setIsUploading(true);
// 这里可以调用上传API
let binaryData: ArrayBuffer;
try {
binaryData = await uploadFileToBinary(selectedTemplateFiles[0]);
} catch (error) {
console.error('上传文件失败:', error);
throw new Error(`文件 ${selectedTemplateFiles[0].name} 转换失败: ${error instanceof Error ? error.message : '未知错误'}`);
console.log('【重新上传模板】开始上传:', {
fileName: selectedTemplateFiles[0].name,
documentId: fileInfo.id,
comparisonId: fileInfo.comparisonId
});
// 调用专门的合同模板上传接口
const uploadResult = await uploadContractTemplate(
selectedTemplateFiles[0], // file: File 对象
fileInfo.id, // documentId: 合同文件的id
fileInfo.comparisonId, // comparisonId: 模板预览文件的id (可选)
jwtToken || undefined // jwtToken
);
console.log('【重新上传模板】上传结果:', uploadResult);
if (uploadResult.error) {
throw new Error(uploadResult.error);
}
// const uploadInfo = {
// binaryData,
// fileName: selectedTemplateFiles[0].name,
// fileType: 'pdf',
// documentType: '1',
// priority: 'normal',
// documentNumber: null,
// remark: null,
// isTestDocument: false,
// documentId: fileInfo
// };
// console.log('uploadInfo',uploadInfo);
const uploadResult = await uploadDocumentToServer(
binaryData,
selectedTemplateFiles[0].name,
'pdf', //file_type 文件类型:pdf
'1', //fileType(type_id) 合同id1
'normal', //priority 优先级:normal
null, //document_number 文档编号
null, //remark 备注
false, //is_test_document 是否为测试文档:false
fileInfo.id, //document_id 主文档id
true, //is_reupload 是否为重新上传:true
jwtToken || undefined, //jwtToken
);
// console.log('重新上传合同模板',uploadResult);
if (uploadResult.error) {
throw new Error(uploadResult.error);
}
toastService.success('模板文件上传成功,结构比对数据将会发生更新,即将返回上一页...');
await new Promise(resolve => setTimeout(resolve, 2000));
handleCloseReuploadModal();
handleBack();
} catch (error) {
console.error('上传模板文件失败:', error);
toastService.error(`上传失败: ${error instanceof Error ? error.message : '未知错误'}`);
@@ -335,21 +330,21 @@ export function ReviewTabs({ activeTab, onTabChange, children, fileInfo, onConfi
<p></p>
<p className="mt-2 text-orange-600">
<i className="ri-information-line mr-1"></i>
PDF格式的文件
PDF和Word格式的文件
</p>
</div>
<UploadArea
ref={uploadAreaRef}
onFilesSelected={handleTemplateFilesSelected}
accept=".pdf,application/pdf"
accept=".pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
multiple={false}
icon="ri-file-pdf-line"
icon="ri-file-text-line"
buttonText="选择模板文件"
mainText="点击或拖拽PDF文件到此区域"
mainText="点击或拖拽文件到此区域"
tipText={
<span className="text-xs text-gray-500">
PDF
PDFDOCDOCX
</span>
}
disabled={isUploading}
@@ -360,38 +355,50 @@ export function ReviewTabs({ activeTab, onTabChange, children, fileInfo, onConfi
<div className="mt-4">
<h4 className="text-sm font-medium text-gray-900 mb-2"></h4>
<div className="space-y-2">
{selectedTemplateFiles.map((file, index) => (
<div
key={index}
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg border"
>
<div className="flex items-center">
<i className="ri-file-pdf-line text-red-500 mr-2"></i>
<div>
<div className="text-sm font-medium text-gray-900">
{file.name}
</div>
<div className="text-xs text-gray-500">
{formatFileSize(file.size)}
{selectedTemplateFiles.map((file, index) => {
// 根据文件类型确定图标和颜色
const fileName = file.name.toLowerCase();
let fileIcon = 'ri-file-pdf-line';
let iconColor = 'text-red-500';
if (fileName.endsWith('.doc') || fileName.endsWith('.docx')) {
fileIcon = 'ri-file-word-2-line';
iconColor = 'text-blue-600';
}
return (
<div
key={index}
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg border"
>
<div className="flex items-center">
<i className={`${fileIcon} ${iconColor} mr-2`}></i>
<div>
<div className="text-sm font-medium text-gray-900">
{file.name}
</div>
<div className="text-xs text-gray-500">
{formatFileSize(file.size)}
</div>
</div>
</div>
<button
className="text-gray-400 hover:text-red-500 transition-colors"
onClick={() => {
setSelectedTemplateFiles(prev =>
prev.filter((_, i) => i !== index)
);
if (uploadAreaRef.current) {
uploadAreaRef.current.resetFileInput();
}
}}
disabled={isUploading}
>
<i className="ri-close-line"></i>
</button>
</div>
<button
className="text-gray-400 hover:text-red-500 transition-colors"
onClick={() => {
setSelectedTemplateFiles(prev =>
prev.filter((_, i) => i !== index)
);
if (uploadAreaRef.current) {
uploadAreaRef.current.resetFileInput();
}
}}
disabled={isUploading}
>
<i className="ri-close-line"></i>
</button>
</div>
))}
);
})}
</div>
</div>
)}