feat: 1. 将交叉评查转移在入口页。
2. 交叉评查渲染的pdf预览组件复用评查点详情的,同时在评查结果中的数据也添加坐标信息。
This commit is contained in:
@@ -80,6 +80,16 @@ const getRuleTypeText = (type?: string): string => {
|
||||
return ruleTypeMap[type] || type;
|
||||
};
|
||||
|
||||
/**
|
||||
* 字符位置类型定义
|
||||
* 用于定位文档中具体的文字位置
|
||||
*/
|
||||
export interface CharPosition {
|
||||
box: number[][]; // 字符边界框坐标
|
||||
char: string; // 字符内容
|
||||
score: number; // OCR识别置信度
|
||||
}
|
||||
|
||||
/**
|
||||
* 评查点类型定义
|
||||
* 用于展示单个评查结果
|
||||
@@ -172,7 +182,7 @@ interface ReviewPointsListProps {
|
||||
reviewPoints: ReviewPoint[];
|
||||
statistics: Statistics;
|
||||
activeReviewPointResultId: string | null;
|
||||
onReviewPointSelect: (id: string, page?: number) => void;
|
||||
onReviewPointSelect: (id: string, page?: number, charPositions?: CharPosition[]) => void;
|
||||
onStatusChange?: (id: string, editAuditStatusId: string | number, status: string, message: string) => void;
|
||||
scoringProposals?: ScoringProposal[];
|
||||
jwtToken?: string; // 添加JWT token参数
|
||||
@@ -1110,15 +1120,15 @@ export function ReviewPointsList({
|
||||
|
||||
// console.log('singleReviewPoint-------', singleReviewPoint);
|
||||
// 检查是否存在配置和pairs数组
|
||||
const config = singleReviewPoint.config as {
|
||||
logic?: string;
|
||||
pairs?: Array<{
|
||||
sourceField: Record<string, { page: number; value: string }>;
|
||||
targetField: Record<string, { page: number; value: string }>;
|
||||
const config = singleReviewPoint.config as {
|
||||
logic?: string;
|
||||
pairs?: Array<{
|
||||
sourceField: Record<string, { page: number; value: string; char_positions?: CharPosition[] }>;
|
||||
targetField: Record<string, { page: number; value: string; char_positions?: CharPosition[] }>;
|
||||
res: boolean;
|
||||
compareMethod?: string;
|
||||
}>;
|
||||
selectedFields?: string[]
|
||||
}>;
|
||||
selectedFields?: string[]
|
||||
} | undefined;
|
||||
|
||||
if (!config || !config.pairs || !Array.isArray(config.pairs) || config.pairs.length === 0) {
|
||||
@@ -1159,27 +1169,28 @@ export function ReviewPointsList({
|
||||
|
||||
// 查找链条关系
|
||||
const findChains = () => {
|
||||
type ChainItem = {
|
||||
field: string;
|
||||
data: {
|
||||
key: string;
|
||||
page: number;
|
||||
value: string
|
||||
};
|
||||
type ChainItem = {
|
||||
field: string;
|
||||
data: {
|
||||
key: string;
|
||||
page: number;
|
||||
value: string;
|
||||
char_positions?: CharPosition[];
|
||||
};
|
||||
res: boolean;
|
||||
compareMethod?: string;
|
||||
compareMethod?: string;
|
||||
};
|
||||
|
||||
|
||||
const chains: Array<Array<ChainItem>> = [];
|
||||
const visited = new Set<string>();
|
||||
|
||||
|
||||
// 构建字段映射关系
|
||||
const fieldMap = new Map<string, Array<{
|
||||
targetField: string;
|
||||
const fieldMap = new Map<string, Array<{
|
||||
targetField: string;
|
||||
data: {
|
||||
source: { key: string; page: number; value: string };
|
||||
target: { key: string; page: number; value: string };
|
||||
};
|
||||
source: { key: string; page: number; value: string; char_positions?: CharPosition[] };
|
||||
target: { key: string; page: number; value: string; char_positions?: CharPosition[] };
|
||||
};
|
||||
res: boolean;
|
||||
compareMethod?: string;
|
||||
}>>();
|
||||
@@ -1396,7 +1407,7 @@ export function ReviewPointsList({
|
||||
for (const item of chain) {
|
||||
if (item.data.page && typeof onReviewPointSelect === 'function') {
|
||||
hasPage = true;
|
||||
onReviewPointSelect(reviewPoint.id, Number(item.data.page));
|
||||
onReviewPointSelect(reviewPoint.id, Number(item.data.page), item.data.char_positions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1410,7 +1421,7 @@ export function ReviewPointsList({
|
||||
// 遍历chain找到第一个有效的page
|
||||
for (const item of chain) {
|
||||
if (item.data.page && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPoint.id, Number(item.data.page));
|
||||
onReviewPointSelect(reviewPoint.id, Number(item.data.page), item.data.char_positions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1450,7 +1461,7 @@ export function ReviewPointsList({
|
||||
// 假设onReviewPointSelect在作用域内可用
|
||||
const reviewPointId = reviewPoint.id as string;
|
||||
if (reviewPointId && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPointId, Number(item.data.page));
|
||||
onReviewPointSelect(reviewPointId, Number(item.data.page), item.data.char_positions);
|
||||
}
|
||||
}
|
||||
else if(reviewPoint.contentPage && reviewPoint.contentPage[item.field]){
|
||||
@@ -1533,7 +1544,7 @@ export function ReviewPointsList({
|
||||
if (chain[0].data.page) {
|
||||
const reviewPointId = reviewPoint.id as string;
|
||||
if (reviewPointId && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPointId, chain[0].data.page);
|
||||
onReviewPointSelect(reviewPointId, chain[0].data.page, chain[0].data.char_positions);
|
||||
}
|
||||
}
|
||||
else if(reviewPoint.contentPage && reviewPoint.contentPage[chain[0].field]){
|
||||
@@ -1559,7 +1570,7 @@ export function ReviewPointsList({
|
||||
if (chain[1].data.page) {
|
||||
const reviewPointId = reviewPoint.id as string;
|
||||
if (reviewPointId && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPointId, chain[1].data.page);
|
||||
onReviewPointSelect(reviewPointId, chain[1].data.page, chain[1].data.char_positions);
|
||||
}
|
||||
}
|
||||
else if(reviewPoint.contentPage && reviewPoint.contentPage[chain[1].field]){
|
||||
@@ -1632,12 +1643,13 @@ export function ReviewPointsList({
|
||||
*/
|
||||
const renderOtherRule = (otherRule: Record<string, unknown>, reviewPoint: ReviewPoint) => {
|
||||
const fieldKey = otherRule.fieldKey as string;
|
||||
const fieldValue = otherRule.fieldValue as {
|
||||
type: Record<string, {
|
||||
const fieldValue = otherRule.fieldValue as {
|
||||
type: Record<string, {
|
||||
res: boolean;
|
||||
page?: number | string;
|
||||
value?: string;
|
||||
}>;
|
||||
char_positions?: CharPosition[];
|
||||
}>;
|
||||
};
|
||||
|
||||
// 获取res的综合结果
|
||||
@@ -1698,7 +1710,7 @@ export function ReviewPointsList({
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (mainTypeValue.page && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPoint.id, Number(mainTypeValue.page));
|
||||
onReviewPointSelect(reviewPoint.id, Number(mainTypeValue.page), mainTypeValue.char_positions);
|
||||
}else if(reviewPoint.contentPage && reviewPoint.contentPage[fieldKey]){
|
||||
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[fieldKey]));
|
||||
}else{
|
||||
@@ -1709,7 +1721,7 @@ export function ReviewPointsList({
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
if (mainTypeValue.page && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPoint.id, Number(mainTypeValue.page));
|
||||
onReviewPointSelect(reviewPoint.id, Number(mainTypeValue.page), mainTypeValue.char_positions);
|
||||
}else{
|
||||
toastService.error(`没有找到${fieldKey}对应的索引内容`);
|
||||
}
|
||||
@@ -1772,12 +1784,13 @@ export function ReviewPointsList({
|
||||
const renderModelRule = (aiRule: Record<string, unknown>, reviewPoint: ReviewPoint) => {
|
||||
|
||||
// 从aiRule中提取配置信息
|
||||
const config = aiRule.config as {
|
||||
model?: string;
|
||||
fields?: Record<string, {
|
||||
const config = aiRule.config as {
|
||||
model?: string;
|
||||
fields?: Record<string, {
|
||||
page: number | string;
|
||||
value: string;
|
||||
}>;
|
||||
char_positions?: CharPosition[];
|
||||
}>;
|
||||
message?: string;
|
||||
res?: boolean;
|
||||
} | undefined;
|
||||
@@ -1819,7 +1832,7 @@ export function ReviewPointsList({
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (value.page && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPoint.id, Number(value.page));
|
||||
onReviewPointSelect(reviewPoint.id, Number(value.page), value.char_positions);
|
||||
}else if(reviewPoint.contentPage && reviewPoint.contentPage[key]){
|
||||
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[key]));
|
||||
}else{
|
||||
@@ -1831,7 +1844,7 @@ export function ReviewPointsList({
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
if (value.page && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPoint.id, Number(value.page));
|
||||
onReviewPointSelect(reviewPoint.id, Number(value.page), value.char_positions);
|
||||
}else if(reviewPoint.contentPage && reviewPoint.contentPage[key]){
|
||||
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[key]));
|
||||
}else{
|
||||
@@ -1946,6 +1959,7 @@ export function ReviewPointsList({
|
||||
interface RuleFieldValue {
|
||||
page?: number | string;
|
||||
value?: string;
|
||||
char_positions?: CharPosition[];
|
||||
type: Record<string, boolean>;
|
||||
}
|
||||
|
||||
@@ -1964,7 +1978,7 @@ export function ReviewPointsList({
|
||||
// 使用类型断言获取config对象的具体结构
|
||||
const config = rule.config as {
|
||||
res: boolean;
|
||||
fields: Record<string, { page: number; value: string }>;
|
||||
fields: Record<string, { page: number; value: string; char_positions?: CharPosition[] }>;
|
||||
logic?: string;
|
||||
};
|
||||
|
||||
@@ -2011,7 +2025,7 @@ export function ReviewPointsList({
|
||||
// 使用类型断言获取config对象的具体结构
|
||||
const config = rule.config as {
|
||||
res: boolean;
|
||||
field: Record<string, { page: string | number; value: string }>;
|
||||
field: Record<string, { page: string | number; value: string; char_positions?: CharPosition[]}>;
|
||||
formatType?: string;
|
||||
parameters?: string;
|
||||
};
|
||||
@@ -2045,7 +2059,7 @@ export function ReviewPointsList({
|
||||
logic: string;
|
||||
res: boolean;
|
||||
conditions: Array<{
|
||||
field: Record<string, { page: number | string; value: string }>;
|
||||
field: Record<string, { page: number | string; value: string; char_positions?: CharPosition[] }>;
|
||||
value: string;
|
||||
operator: string;
|
||||
res: boolean;
|
||||
@@ -2080,7 +2094,7 @@ export function ReviewPointsList({
|
||||
// 使用类型断言获取config对象的具体结构
|
||||
const config = rule.config as {
|
||||
res: boolean;
|
||||
field: Record<string, { page: number | string; value: string }>;
|
||||
field: Record<string, { page: number | string; value: string; char_positions?: CharPosition[] }>;
|
||||
pattern?: string;
|
||||
matchType?: string;
|
||||
selectedFields?: string[];
|
||||
@@ -2115,6 +2129,7 @@ export function ReviewPointsList({
|
||||
res: boolean;
|
||||
page?: number | string;
|
||||
value?: string;
|
||||
char_positions?: CharPosition[]
|
||||
}>;
|
||||
};
|
||||
}> = [];
|
||||
@@ -2127,6 +2142,7 @@ export function ReviewPointsList({
|
||||
res: boolean;
|
||||
page?: number | string;
|
||||
value?: string;
|
||||
char_positions?: CharPosition[]
|
||||
}>;
|
||||
};
|
||||
}> = {};
|
||||
@@ -2138,9 +2154,10 @@ export function ReviewPointsList({
|
||||
const typeKey = Object.keys(fieldValue.type)[0]; // 获取类型名称(exists/logic/regex/format)
|
||||
const typeValue = fieldValue.type[typeKey]; // 获取类型值(true/false)
|
||||
|
||||
// 提取页码和值
|
||||
// 提取页码和值和字符位置
|
||||
const page = fieldValue.page;
|
||||
const value = fieldValue.value;
|
||||
const char_positions = fieldValue.char_positions
|
||||
|
||||
// 如果是第一次遇到这个fieldKey,创建新条目
|
||||
if (!fieldKeyMap[fieldKey]) {
|
||||
@@ -2157,7 +2174,8 @@ export function ReviewPointsList({
|
||||
fieldKeyMap[fieldKey].fieldValue.type[typeKey] = {
|
||||
res: typeValue,
|
||||
page,
|
||||
value
|
||||
value,
|
||||
char_positions
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
export { FileInfo } from './FileInfo';
|
||||
export { FilePreview } from './FilePreview';
|
||||
export { FilePreview } from '../reviews/FilePreview';
|
||||
export { ReviewPointsList } from './ReviewPointsList';
|
||||
export type { ReviewPoint } from './ReviewPointsList';
|
||||
export { DocumentListModal } from './DocumentListModal';
|
||||
@@ -90,16 +90,18 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '' }: Sid
|
||||
fetchUserRoutes();
|
||||
}, [userRole, frontendJWT, navigate]);
|
||||
|
||||
// 🔑 检查是否处于系统设置模式
|
||||
// 🔑 检查是否处于系统设置模式或交叉评查模式
|
||||
const [isSettingsMode, setIsSettingsMode] = useState<boolean>(false);
|
||||
const [isCrossCheckingMode, setIsCrossCheckingMode] = useState<boolean>(false);
|
||||
|
||||
// 从 sessionStorage 读取当前选中的模块名称和图片路径,以及系统设置模式标志
|
||||
// 从 sessionStorage 读取当前选中的模块名称和图片路径,以及各种模式标志
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
try {
|
||||
const moduleName = sessionStorage.getItem('selectedModuleName');
|
||||
const modulePicPath = sessionStorage.getItem('selectedModulePicPath');
|
||||
const settingsMode = sessionStorage.getItem('settingsMode');
|
||||
const crossCheckingMode = sessionStorage.getItem('crossCheckingMode');
|
||||
|
||||
if (moduleName) {
|
||||
setSelectedModuleName(moduleName);
|
||||
@@ -114,9 +116,19 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '' }: Sid
|
||||
// 🔑 检查是否处于系统设置模式
|
||||
if (settingsMode === 'true') {
|
||||
setIsSettingsMode(true);
|
||||
setIsCrossCheckingMode(false); // 互斥
|
||||
console.log('⚙️ [Sidebar] 进入系统设置模式');
|
||||
} else {
|
||||
}
|
||||
// 🔑 检查是否处于交叉评查模式
|
||||
else if (crossCheckingMode === 'true') {
|
||||
setIsCrossCheckingMode(true);
|
||||
setIsSettingsMode(false); // 互斥
|
||||
console.log('🔀 [Sidebar] 进入交叉评查模式');
|
||||
}
|
||||
// 普通模式
|
||||
else {
|
||||
setIsSettingsMode(false);
|
||||
setIsCrossCheckingMode(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ [Sidebar] 读取 sessionStorage 失败:', error);
|
||||
@@ -177,11 +189,21 @@ export function Sidebar({ onToggle, collapsed, userRole, frontendJWT = '' }: Sid
|
||||
return item.path === '/settings' || item.path?.startsWith('/settings/');
|
||||
}
|
||||
|
||||
// 🔑 优先检查:如果处于交叉评查模式,只显示 /cross-checking 及其子路由
|
||||
if (isCrossCheckingMode) {
|
||||
return item.path === '/cross-checking' || item.path?.startsWith('/cross-checking/');
|
||||
}
|
||||
|
||||
// 🔑 重要:非系统设置模式下,隐藏所有 /settings 相关菜单
|
||||
if (item.path === '/settings' || item.path?.startsWith('/settings/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 🔑 重要:非交叉评查模式下,隐藏所有 /cross-checking 相关菜单
|
||||
if (item.path === '/cross-checking' || item.path?.startsWith('/cross-checking/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果是省局访问
|
||||
// if(isPort51707){
|
||||
// if (selectedModuleName === '智慧法务大模型'){
|
||||
|
||||
@@ -181,18 +181,18 @@ export function FilePreview({ fileContent, activeReviewPointResultId, targetPage
|
||||
}, [targetPage, numPages, fileContent, activeReviewPointResultId, isStructuredView, isPdf]);
|
||||
|
||||
// 调试日志
|
||||
console.log('[FilePreview] 组件渲染', {
|
||||
real_path,
|
||||
fileExtension,
|
||||
isDocx,
|
||||
isPdf,
|
||||
hasPath: !!fileContent.path,
|
||||
hasTemplatePath: !!fileContent.template_contract_path
|
||||
});
|
||||
// console.log('[FilePreview] 组件渲染', {
|
||||
// real_path,
|
||||
// fileExtension,
|
||||
// isDocx,
|
||||
// isPdf,
|
||||
// hasPath: !!fileContent.path,
|
||||
// hasTemplatePath: !!fileContent.template_contract_path
|
||||
// });
|
||||
|
||||
// 如果是PDF文件,直接使用PdfPreview组件
|
||||
if (isPdf && real_path) {
|
||||
console.log('[FilePreview] 渲染PDF预览', { real_path, targetPage, charPositions });
|
||||
// console.log('[FilePreview] 渲染PDF预览', { real_path, targetPage, charPositions });
|
||||
const pageOffset = fileContent.ocrResult?.__meta?.page_offset || 0;
|
||||
return (
|
||||
<PdfPreview
|
||||
|
||||
@@ -56,14 +56,14 @@ export function PdfPreview({
|
||||
onZoomChange
|
||||
}: PdfPreviewProps) {
|
||||
// 调试日志
|
||||
console.log('[PdfPreview] 组件渲染', {
|
||||
filePath,
|
||||
targetPage,
|
||||
charPositions,
|
||||
isStructuredView,
|
||||
activeReviewPointResultId,
|
||||
pageOffset
|
||||
});
|
||||
// console.log('[PdfPreview] 组件渲染', {
|
||||
// filePath,
|
||||
// targetPage,
|
||||
// charPositions,
|
||||
// isStructuredView,
|
||||
// activeReviewPointResultId,
|
||||
// pageOffset
|
||||
// });
|
||||
|
||||
// ============ 状态管理 ============
|
||||
const [numPages, setNumPages] = useState<number | null>(null);
|
||||
|
||||
Reference in New Issue
Block a user