feat: 1. 添加交叉评查中的相关页面的按钮与权限的绑定控制。 2. 完善权限校验的hook函数,添加指定的交叉评查的相关的权限。

fix: 1. 修复交叉评查中无法高亮文档的问题。
This commit is contained in:
2025-12-12 16:10:05 +08:00
parent d4000cd292
commit 847f7b2b5a
9 changed files with 214 additions and 140 deletions
+3 -3
View File
@@ -251,9 +251,9 @@ export default function CrossCheckingIndex() {
const fetcher = useFetcher();
// 权限控制
const { canCreate, canView } = usePermission();
const canCreateTask = canCreate('cross_review');
const canViewTask = canView('cross_review');
const { hasPermission } = usePermission();
const canCreateTask = hasPermission('cross_review:task:create');
const canViewTask = hasPermission('cross_review:task:read');
// 状态管理
const [isDeleting, setIsDeleting] = useState(false);
+23 -5
View File
@@ -29,6 +29,7 @@ import crossCheckingStyles from "~/styles/cross-checking-result.css?url";
import { getReviewPoints, updateReviewResult, getReviewPoints_fromApi} from "~/api/evaluation_points/reviews";
import { toastService } from "~/components/ui/Toast";
import { confirmReviewResults, checkProposalVotes, findIsProposer } from "~/api/cross-checking/cross-file-result";
import { usePermission } from "~/hooks/usePermission";
// 导入交叉评查详情页面组件
import {
@@ -332,10 +333,19 @@ export default function CrossCheckingResult() {
const loaderData = useLoaderData<typeof loader>();
const { document, reviewPoints, statistics, reviewInfo, scoring_proposals, jwtToken, userInfo, isProposer, taskId, taskName } = loaderData;
const [isLoading, setIsLoading] = useState(false); // 已经通过loader加载了数据,不需要再显示加载状态
// 权限控制
const { hasPermission } = usePermission();
const canCompleteDocument = hasPermission('cross_review:document:complete'); // 完成评查按钮
const canReadProposal = hasPermission('cross_review:proposal:read'); // 查看意见列表
const canCreateProposal = hasPermission('cross_review:proposal:create'); // 提出建议按钮
const canDeleteProposal = hasPermission('cross_review:proposal:delete'); // 撤销意见按钮
const canVoteProposal = hasPermission('cross_review:proposal:vote'); // 赞同/反对按钮
const [reviewData, setReviewData] = useState<ReviewData | null>(null);
const [activeReviewPointResultId, setActiveReviewPointResultId] = useState<string | null>(null);
const [targetPage, setTargetPage] = useState<number | undefined>(undefined);
const [charPositions, setCharPositions] = useState<CharPosition[] | undefined>(undefined);
const [highlightValue, setHighlightValue] = useState<string | undefined>(undefined);
const [localScoringProposals, setLocalScoringProposals] = useState<ScoringProposal[]>(scoring_proposals || []); // 本地状态管理scoringProposals
const [aiSuggestionReplace, setAiSuggestionReplace] = useState<{
searchText: string;
@@ -419,22 +429,25 @@ export default function CrossCheckingResult() {
}, [document, reviewPoints, statistics, reviewInfo]);
const handleReviewPointSelect = useCallback((reviewPointId: string, page?: number, charPositions?: CharPosition[]) => {
const handleReviewPointSelect = useCallback((reviewPointId: string, page?: number, charPositions?: CharPosition[], value?: string) => {
// 如果点击的是相同的评查点,但有page参数,先重置targetPage以确保useEffect能够触发
if (reviewPointId === activeReviewPointResultId && page) {
setTargetPage(undefined);
setCharPositions(undefined);
// 使用setTimeout确保状态更新后再设置新的targetPage和charPositions
setHighlightValue(undefined);
// 使用setTimeout确保状态更新后再设置新的targetPage、charPositions和highlightValue
setTimeout(() => {
setActiveReviewPointResultId(reviewPointId);
setTargetPage(page);
setCharPositions(charPositions);
setHighlightValue(value);
}, 0);
} else {
// 正常设置activeReviewPointId、targetPagecharPositions
// 正常设置activeReviewPointId、targetPagecharPositions和highlightValue
setActiveReviewPointResultId(reviewPointId);
setTargetPage(page);
setCharPositions(charPositions);
setHighlightValue(value);
}
}, [activeReviewPointResultId]);
@@ -730,8 +743,8 @@ export default function CrossCheckingResult() {
</div>
</div>
{/* 完成评查按钮 */}
{isProposer && (
{/* 完成评查按钮 - 需要 isProposer 且拥有 cross_review:document:complete 权限 */}
{isProposer && canCompleteDocument && (
<button
type="button"
onClick={(event) => {
@@ -778,6 +791,7 @@ export default function CrossCheckingResult() {
activeReviewPointResultId={activeReviewPointResultId}
targetPage={targetPage}
charPositions={charPositions}
highlightValue={highlightValue}
aiSuggestionReplace={aiSuggestionReplace}
/>
</div>
@@ -796,6 +810,10 @@ export default function CrossCheckingResult() {
onOpinionSubmitted={handleOpinionSubmitted}
fileFormat={reviewData.fileInfo.fileFormat}
onAiSuggestionReplace={handleAiSuggestionReplace}
canReadProposal={canReadProposal}
canCreateProposal={canCreateProposal}
canDeleteProposal={canDeleteProposal}
canVoteProposal={canVoteProposal}
/>
</div>
</div>
+7 -7
View File
@@ -322,11 +322,11 @@ export default function ReviewDetails() {
} | undefined>(undefined);
// 🐛 调试:打印 loader 返回的完整数据到浏览器控制台
useEffect(() => {
if (typeof window !== 'undefined') {
console.group('📦 [Reviews] Loader 数据');
// useEffect(() => {
// if (typeof window !== 'undefined') {
// console.group('📦 [Reviews] Loader 数据');
// console.log('完整数据:', loaderData);
console.log('文档信息:', document);
// console.log('文档信息:', document);
// console.log('评查点数量:', reviewPoints?.length);
// console.log('评查点数量:', reviewPoints);
// console.log('统计信息:', statistics);
@@ -334,9 +334,9 @@ export default function ReviewDetails() {
// console.log('比对文档:', comparison_document);
// console.log('用户信息:', loaderData.userInfo);
// console.log('JWT Token (前20位):', frontendJWT?.substring(0, 20) + '...');
console.groupEnd();
}
}, [loaderData, document, reviewPoints, statistics, reviewInfo, comparison_document, frontendJWT]);
// console.groupEnd();
// }
// }, [loaderData, document, reviewPoints, statistics, reviewInfo, comparison_document, frontendJWT]);
// loader 数据加载出错
useEffect(()=>{