feat: 1. 重构交叉评查任务的文档列表的显示,对接接口查询当前任务的文档相关信息。

2.文档上传通过接口去查询是否存在同名的文件,做上传前拦截提示。
3.交叉评查的评查结果也同步添加企查查的企业信息查询模块。
4. 封装上传附件和上传模板的模态框的组件,在交叉评查的文档列表中引入显示。
5. 交叉评查的评查结果中关于合同类型的文档同步加入结构比对的功能。
This commit is contained in:
2025-12-13 07:18:37 +08:00
parent daa53289af
commit 1658bb1c6f
11 changed files with 3368 additions and 363 deletions
+87 -39
View File
@@ -38,6 +38,9 @@ import {
ReviewPointsList
} from "~/components/cross-checking";
// 导入文档对比组件
import { ComparePreview } from "~/components/reviews/previewComponents/ComparePreview";
// 从ReviewPointsList组件中导入ReviewPoint类型和CharPosition类型
import { type ReviewPoint, type CharPosition } from '~/components/cross-checking';
import { messageService } from "~/components/ui/MessageModal";
@@ -232,12 +235,13 @@ export async function loader({ request }: LoaderFunctionArgs) {
// 获取评查点数据,传递request对象 旧获取评查点结果的方法
// const reviewData = await getReviewPoints(id, request);
// 获取当前登录用户是否是发起
// 获取当前登录用户是否是负责
const isProposer = await findIsProposer(taskId, userInfo?.user_id, frontendJWT);
// console.log("documentData-------",JSON.stringify(documentData.data,null,2));
// console.log("reviewData-------",JSON.stringify('data' in reviewData ? reviewData.data : '',null,2));
// console.log("reviewData-------",JSON.stringify(reviewData,null,2));
// console.log("reviewData-------",JSON.stringify(reviewData.comparison_document));
if ('error' in reviewData && reviewData.error) {
console.error("获取评查点数据错误:", reviewData.error);
return Response.json({ result: false, message: reviewData.error });
@@ -331,9 +335,16 @@ export default function CrossCheckingResult() {
const navigate = useNavigate();
const loaderData = useLoaderData<typeof loader>();
const { document, reviewPoints, statistics, reviewInfo, scoring_proposals, jwtToken, userInfo, isProposer, taskId, taskName } = loaderData;
const { document, reviewPoints, statistics, reviewInfo, comparison_document, scoring_proposals, jwtToken, userInfo, isProposer, taskId, taskName } = loaderData;
const [isLoading, setIsLoading] = useState(false); // 已经通过loader加载了数据,不需要再显示加载状态
// 视图切换状态:'review' = 评查结果视图, 'compare' = 结构比对视图
const [viewMode, setViewMode] = useState<'review' | 'compare'>('review');
// 判断是否有模板可以进行结构比对
const hasTemplateForCompare = Boolean(comparison_document?.template_contract_path?.trim());
// 权限控制
const { hasPermission } = usePermission();
const canCompleteDocument = hasPermission('cross_review:document:complete'); // 完成评查按钮
@@ -357,9 +368,9 @@ export default function CrossCheckingResult() {
const isProcessingRef = useRef(false);
// 添加组件挂载/卸载日志
useEffect(() => {
console.log('[组件] CrossCheckingResult', isProposer);
}, [isProposer]);
// useEffect(() => {
// console.log('[组件] CrossCheckingResult', isProposer);
// }, [isProposer]);
// 同步外部scoring_proposals到本地状态
useEffect(() => {
@@ -743,6 +754,27 @@ export default function CrossCheckingResult() {
</div>
</div>
{/* 结构比对/查看评查结果按钮 - 仅当文档类型包含"合同"且有模板时显示 */}
{hasTemplateForCompare && (
<button
type="button"
onClick={() => setViewMode(viewMode === 'review' ? 'compare' : 'review')}
className="inline-flex items-center px-3 py-1.5 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 mr-2"
>
{viewMode === 'review' ? (
<>
<i className="ri-file-copy-2-line mr-1.5"></i>
</>
) : (
<>
<i className="ri-file-list-3-line mr-1.5"></i>
</>
)}
</button>
)}
{/* 完成评查按钮 - 需要 isProposer 且拥有 cross_review:document:complete 权限 */}
{isProposer && canCompleteDocument && (
<button
@@ -781,42 +813,58 @@ export default function CrossCheckingResult() {
onConfirmResults={handleConfirmResults}
/> */}
{/* 交叉评查结果内容 */}
<div className="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-4 lg:justify-between">
{/* 左侧:文件预览 */}
<div className="w-full lg:w-[62%]">
<FilePreview
fileContent={document}
reviewPoints={reviewData.reviewPoints}
activeReviewPointResultId={activeReviewPointResultId}
targetPage={targetPage}
charPositions={charPositions}
highlightValue={highlightValue}
aiSuggestionReplace={aiSuggestionReplace}
{/* 根据视图模式切换内容 */}
{viewMode === 'review' ? (
/* 评查结果视图 */
<div className="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-4 lg:justify-between">
{/* 左侧:文件预览 */}
<div className="w-full lg:w-[62%]">
<FilePreview
fileContent={document}
reviewPoints={reviewData.reviewPoints}
activeReviewPointResultId={activeReviewPointResultId}
targetPage={targetPage}
charPositions={charPositions}
highlightValue={highlightValue}
aiSuggestionReplace={aiSuggestionReplace}
/>
</div>
{/* 右侧:评查结果 */}
<div className="w-full lg:w-[35%]">
<ReviewPointsList
reviewPoints={reviewData.reviewPoints}
statistics={reviewData.statistics}
activeReviewPointResultId={activeReviewPointResultId}
onReviewPointSelect={handleReviewPointSelect}
onStatusChange={handleReviewPointStatusChange}
scoringProposals={localScoringProposals}
jwtToken={jwtToken}
userInfo={userInfo}
onOpinionSubmitted={handleOpinionSubmitted}
fileFormat={reviewData.fileInfo.fileFormat}
onAiSuggestionReplace={handleAiSuggestionReplace}
canReadProposal={canReadProposal}
canCreateProposal={canCreateProposal}
canDeleteProposal={canDeleteProposal}
canVoteProposal={canVoteProposal}
/>
</div>
</div>
) : (
/* 结构比对视图 */
<div className="w-full" style={{
height: 'calc(100vh - 120px)',
minHeight: '600px',
display: 'flex',
flexDirection: 'column'
}}>
<ComparePreview
doc1Path={document?.path || ''}
doc2Path={comparison_document?.template_contract_path || ''}
/>
</div>
{/* 右侧:评查结果 */}
<div className="w-full lg:w-[35%]">
<ReviewPointsList
reviewPoints={reviewData.reviewPoints}
statistics={reviewData.statistics}
activeReviewPointResultId={activeReviewPointResultId}
onReviewPointSelect={handleReviewPointSelect}
onStatusChange={handleReviewPointStatusChange}
scoringProposals={localScoringProposals}
jwtToken={jwtToken}
userInfo={userInfo}
onOpinionSubmitted={handleOpinionSubmitted}
fileFormat={reviewData.fileInfo.fileFormat}
onAiSuggestionReplace={handleAiSuggestionReplace}
canReadProposal={canReadProposal}
canCreateProposal={canCreateProposal}
canDeleteProposal={canDeleteProposal}
canVoteProposal={canVoteProposal}
/>
</div>
</div>
)}
</>
)}
</div>