新增主页,优化评查点结果一致性的显示效果

This commit is contained in:
2025-05-28 17:37:23 +08:00
parent 690d369f57
commit 08fb737cbf
10 changed files with 2596 additions and 458 deletions
+13 -3
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { Sidebar } from './Sidebar';
// import { Header } from './Header';
import { Breadcrumb } from './Breadcrumb';
import { useMatches } from '@remix-run/react';
import { useMatches, useLocation } from '@remix-run/react';
interface LayoutProps {
children: React.ReactNode;
@@ -11,7 +11,7 @@ interface LayoutProps {
// 添加一个接口表示路由handle可能包含的属性
interface RouteHandle {
hideBreadcrumb?: boolean;
[key: string]: any;
[key: string]: unknown;
}
interface Match {
@@ -23,9 +23,14 @@ interface Match {
export function Layout({ children }: LayoutProps) {
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const matches = useMatches() as Match[];
const location = useLocation();
// 检查当前路径是否应该隐藏侧边栏
const noLayoutPaths = ['/login', '/'];
const shouldHideSidebar = noLayoutPaths.includes(location.pathname);
// 检查当前路由是否应该隐藏默认面包屑
const shouldHideBreadcrumb = matches.some(match =>
const shouldHideBreadcrumb = shouldHideSidebar || matches.some(match =>
match.handle && match.handle.hideBreadcrumb === true
);
@@ -43,6 +48,11 @@ export function Layout({ children }: LayoutProps) {
localStorage.setItem('sidebarCollapsed', String(newState));
};
// 如果是无布局页面,只渲染内容
if (shouldHideSidebar) {
return <>{children}</>;
}
return (
<div className="layout-container">
<Sidebar
+8 -1
View File
@@ -19,10 +19,17 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
const [expandedMenus, setExpandedMenus] = useState<Record<string, boolean>>({});
const menuItems: MenuItem[] = [
{
id: 'contract-search',
title: '智能搜索',
path: '/contract-search',
hideBreadcrumb: true,
icon: 'ri-search-line'
},
{
id: 'home',
title: '系统概览',
path: '/',
path: '/home',
icon: 'ri-home-line'
},
{
+137 -2
View File
@@ -459,15 +459,24 @@ export function ReviewPointsList({
// 获取所有consistency规则中的fields
const allConsistencyFields: string[][] = [];
// 存储 sourceField 和 targetField 的映射关系
const pairsMapping: Record<string, string> = {};
consistencyRules.forEach(rule => {
if (rule.config?.fields) {
allConsistencyFields.push(rule.config.fields);
}else if (rule.config?.pairs) {
} else if (rule.config?.pairs) {
// 处理pairs情况,提取sourceField和targetField
const fields: string[] = [];
rule.config.pairs.forEach(pair => {
if (pair.sourceField) fields.push(pair.sourceField);
if (pair.targetField) fields.push(pair.targetField);
// 记录 sourceField 和 targetField 的映射关系
if (pair.sourceField && pair.targetField) {
pairsMapping[pair.sourceField] = pair.targetField;
}
});
if (fields.length > 0) {
allConsistencyFields.push(fields);
@@ -507,10 +516,136 @@ export function ReviewPointsList({
}
});
// 对每个分组内的条目按照 sourceField 和 targetField 的关系进行排序
Object.keys(groupedContent).forEach(groupKey => {
if (groupKey !== 'default' && groupedContent[groupKey].length > 1) {
// 创建一个新数组用于存储排序后的结果
const sortedEntries: Array<[string, { page?: number | string, value?: object }]> = [];
const entriesMap = new Map(groupedContent[groupKey]);
// 找出所有的源字段和目标字段对
const processed = new Set<string>();
// 构建一个字段之间的连接关系图,用于处理嵌套关系
const fieldChains: Array<string[]> = [];
// 遍历所有映射关系,构建字段链
const buildFieldChains = () => {
// 创建一个图结构,记录每个字段的后继字段
const graph: Record<string, string[]> = {};
// 根据映射关系建立图
Object.entries(pairsMapping).forEach(([source, target]) => {
if (!graph[source]) graph[source] = [];
graph[source].push(target);
// 确保目标字段在图中有一个空数组
if (!graph[target]) graph[target] = [];
});
// 查找所有在当前分组中的字段
const fieldsInGroup = new Set(Array.from(entriesMap.keys()));
// 找出入度为0的节点(即只作为sourceField而不是任何targetField的字段)
const startNodes: string[] = [];
for (const field of fieldsInGroup) {
// 检查该字段是否作为targetField存在
const isTarget = Object.values(pairsMapping).includes(field);
// 如果该字段是sourceField但不是targetField,则为起始节点
if (!isTarget && field in pairsMapping) {
startNodes.push(field);
}
}
// 从每个起始节点开始,使用DFS构建字段链
for (const startNode of startNodes) {
const chain: string[] = [];
const dfs = (node: string) => {
// 如果该节点不在当前分组中,则跳过
if (!fieldsInGroup.has(node)) return;
chain.push(node);
// 遍历所有后继节点
for (const nextNode of graph[node] || []) {
dfs(nextNode);
}
};
dfs(startNode);
// 如果链不为空,则添加到字段链列表中
if (chain.length > 0) {
fieldChains.push(chain);
}
}
// 处理环形依赖或没有入度为0的节点的情况
// 找出未被处理的字段
const processedInChains = new Set(fieldChains.flat());
const remainingFields = Array.from(fieldsInGroup).filter(f => !processedInChains.has(f));
// 将剩余字段按照pairsMapping的关系组织成链
while (remainingFields.length > 0) {
const field = remainingFields.shift()!;
// 如果该字段已经在某个链中,则跳过
if (processedInChains.has(field)) continue;
const chain: string[] = [field];
processedInChains.add(field);
// 向后查找链
let currentField = field;
while (currentField in pairsMapping) {
const nextField = pairsMapping[currentField];
// 如果下一个字段不在分组中或已处理,则中断
if (!fieldsInGroup.has(nextField) || processedInChains.has(nextField)) break;
chain.push(nextField);
processedInChains.add(nextField);
currentField = nextField;
// 从剩余字段中移除
const index = remainingFields.indexOf(nextField);
if (index !== -1) {
remainingFields.splice(index, 1);
}
}
if (chain.length > 0) {
fieldChains.push(chain);
}
}
};
buildFieldChains();
// 根据字段链构建排序后的结果
fieldChains.forEach(chain => {
chain.forEach(field => {
if (entriesMap.has(field) && !processed.has(field)) {
sortedEntries.push([field, entriesMap.get(field)!]);
processed.add(field);
}
});
});
// 添加剩余未处理的字段
for (const [key] of groupedContent[groupKey]) {
if (!processed.has(key)) {
sortedEntries.push([key, entriesMap.get(key)!]);
}
}
// 用排序后的结果替换原数组
groupedContent[groupKey] = sortedEntries;
}
});
return (
<>
{/* 渲染各个分组 */}
{Object.entries(groupedContent).map(([groupKey, entries], groupIndex) => {
{Object.entries(groupedContent).map(([groupKey, entries]) => {
if (entries.length === 0) return null;
// 非默认组添加边框