新增主页,优化评查点结果一致性的显示效果
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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'
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
// 非默认组添加边框
|
||||
|
||||
Reference in New Issue
Block a user