删除所有console.log输出,优化评查结果的表格的显示,添加新的页码获取逻辑
This commit is contained in:
@@ -60,7 +60,7 @@ export function TemplateCard({ template, onClick }: TemplateCardProps) {
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
console.log('开始下载文件:', cleanFileName);
|
||||
// console.log('开始下载文件:', cleanFileName);
|
||||
} catch (error) {
|
||||
console.error('下载文件失败:', error);
|
||||
alert('下载失败,请稍后重试');
|
||||
@@ -86,7 +86,7 @@ export function TemplateCard({ template, onClick }: TemplateCardProps) {
|
||||
navigate(`/contract-template/detail/${template.id}`);
|
||||
break;
|
||||
default:
|
||||
console.log(`执行操作: ${action}`, template.id);
|
||||
// console.log(`执行操作: ${action}`, template.id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ export function ErrorBoundary({ status, statusText, message }: ErrorBoundaryProp
|
||||
</Link>
|
||||
</div>
|
||||
<div className="mt-12 text-center">
|
||||
<p className="text-sm text-gray-500">如果问题持续存在,请联系系统管理员</p>
|
||||
<p className="text-sm text-gray-500 mt-1">技术支持:support@tobacco-ai-system.com</p>
|
||||
<p className="text-sm text-gray-500">如果问题持续存在,请联系技术开发团队</p>
|
||||
{/* <p className="text-sm text-gray-500 mt-1">技术支持:support@tobacco-ai-system.com</p> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,9 +3,11 @@ import { Sidebar } from './Sidebar';
|
||||
// import { Header } from './Header';
|
||||
import { Breadcrumb } from './Breadcrumb';
|
||||
import { useMatches, useLocation } from '@remix-run/react';
|
||||
import type { UserRole } from '~/root';
|
||||
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
userRole?: UserRole;
|
||||
}
|
||||
|
||||
// 添加一个接口表示路由handle可能包含的属性
|
||||
@@ -20,7 +22,7 @@ interface Match {
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
export function Layout({ children }: LayoutProps) {
|
||||
export function Layout({ children, userRole = 'developer' }: LayoutProps) {
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
const matches = useMatches() as Match[];
|
||||
const location = useLocation();
|
||||
@@ -58,6 +60,7 @@ export function Layout({ children }: LayoutProps) {
|
||||
<Sidebar
|
||||
collapsed={sidebarCollapsed}
|
||||
onToggle={toggleSidebar}
|
||||
userRole={userRole}
|
||||
/>
|
||||
|
||||
<div className={`main-content ${sidebarCollapsed ? 'sidebar-collapsed' : ''}`}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Link, useLocation } from '@remix-run/react';
|
||||
import type { UserRole } from '~/root';
|
||||
|
||||
interface MenuItem {
|
||||
id: string;
|
||||
@@ -7,15 +8,17 @@ interface MenuItem {
|
||||
path: string;
|
||||
icon: string;
|
||||
hideBreadcrumb?: boolean;
|
||||
requiredRole?: UserRole;
|
||||
children?: MenuItem[];
|
||||
}
|
||||
|
||||
interface SidebarProps {
|
||||
onToggle: () => void;
|
||||
collapsed: boolean;
|
||||
userRole: UserRole;
|
||||
}
|
||||
|
||||
export function Sidebar({ onToggle, collapsed }: SidebarProps) {
|
||||
export function Sidebar({ onToggle, collapsed, userRole }: SidebarProps) {
|
||||
const location = useLocation();
|
||||
const [expandedMenus, setExpandedMenus] = useState<Record<string, boolean>>({});
|
||||
|
||||
@@ -109,12 +112,14 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
|
||||
title: '系统设置',
|
||||
path: '/settings',
|
||||
icon: 'ri-settings-4-line',
|
||||
requiredRole: 'developer',
|
||||
children: [
|
||||
{
|
||||
id: 'config-lists',
|
||||
title: '配置列表',
|
||||
path: '/config-lists',
|
||||
icon: 'ri-list-check-3'
|
||||
icon: 'ri-list-check-3',
|
||||
requiredRole: 'developer'
|
||||
},
|
||||
// {
|
||||
// id: 'basic-settings',
|
||||
@@ -126,13 +131,15 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
|
||||
id: 'document-types',
|
||||
title: '文档类型',
|
||||
path: '/document-types',
|
||||
icon: 'ri-file-list-line'
|
||||
icon: 'ri-file-list-line',
|
||||
requiredRole: 'developer'
|
||||
},
|
||||
{
|
||||
id: 'prompt-management',
|
||||
title: '提示词管理',
|
||||
path: '/prompts',
|
||||
icon: 'ri-chat-1-line'
|
||||
icon: 'ri-chat-1-line',
|
||||
requiredRole: 'developer'
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -150,11 +157,10 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
|
||||
}, []);
|
||||
|
||||
const toggleMenu = (id: string, e: React.MouseEvent) => {
|
||||
// 防止事件冒泡和默认行为
|
||||
e.preventDefault();
|
||||
// 我们只防止事件冒泡,不阻止默认行为
|
||||
e.stopPropagation();
|
||||
|
||||
// console.log('%c父菜单展开/折叠 ===> ', 'background: #f5222d; color: white; padding: 2px 4px; border-radius: 2px;', id);
|
||||
// console.log('父菜单展开/折叠:', id);
|
||||
|
||||
setExpandedMenus(prev => ({
|
||||
...prev,
|
||||
@@ -168,18 +174,27 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
|
||||
|
||||
// 处理侧边栏切换事件
|
||||
const handleToggleSidebar = (e: React.MouseEvent) => {
|
||||
// console.log('%c侧边栏折叠/展开 ===> ', 'background: #1890ff; color: white; padding: 2px 4px; border-radius: 2px;');
|
||||
e.preventDefault();
|
||||
// console.log('侧边栏折叠/展开');
|
||||
// 只防止事件冒泡,不阻止默认行为
|
||||
e.stopPropagation();
|
||||
onToggle();
|
||||
};
|
||||
|
||||
// 处理子菜单项点击事件
|
||||
const handleSubMenuClick = (child: MenuItem, e: React.MouseEvent) => {
|
||||
// 需要阻止冒泡,否则会触发父级菜单的展开/折叠事件
|
||||
// 只需要阻止冒泡,不阻止默认行为
|
||||
e.stopPropagation();
|
||||
// console.log('%c子菜单点击 ===> ', 'background: #00684a; color: white; padding: 2px 4px; border-radius: 2px;', child.title, '路径:', child.path);
|
||||
// console.log('子菜单点击:', child.title, '路径:', child.path);
|
||||
};
|
||||
|
||||
// 根据用户角色过滤菜单项
|
||||
const filteredMenuItems = menuItems.filter(item => {
|
||||
// 如果菜单项需要特定角色但用户没有
|
||||
if (item.requiredRole && item.requiredRole !== userRole) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={`sidebar ${collapsed ? 'collapsed' : ''}`}>
|
||||
@@ -211,15 +226,16 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
|
||||
)} */}
|
||||
|
||||
<div className="py-4 px-[10px]">
|
||||
{menuItems.map((item) => (
|
||||
{filteredMenuItems.map((item) => (
|
||||
<div key={item.id} className={`${collapsed ? 'px-0' : ''}`}>
|
||||
{!item.children ? (
|
||||
<Link
|
||||
to={item.path}
|
||||
className={`sidebar-menu-item ${isActive(item.path) ? 'active' : ''} flex items-center ${collapsed ? 'justify-center' : ''}`}
|
||||
onClick={(e) => {
|
||||
// 只阻止冒泡,不阻止默认行为
|
||||
e.stopPropagation();
|
||||
// console.log('%c单级菜单点击 ===> ', 'background: #52c41a; color: white; padding: 2px 4px; border-radius: 2px;', item.title, '路径:', item.path);
|
||||
// console.log('单级菜单点击:', item.title, '路径:', item.path);
|
||||
}}
|
||||
>
|
||||
<i className={`${item.icon} ${collapsed ? 'text-xl' : 'mr-3'}`}></i>
|
||||
@@ -258,7 +274,9 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
|
||||
className={`submenu-container ${collapsed ? 'border-l-0 pl-0' : 'border-l border-gray-100 ml-4 pl-3'} z-20`}
|
||||
id={`submenu-${item.id}`}
|
||||
>
|
||||
{item.children.map((child) => (
|
||||
{item.children
|
||||
.filter(child => !child.requiredRole || child.requiredRole === userRole)
|
||||
.map((child) => (
|
||||
<Link
|
||||
key={child.id}
|
||||
to={child.path}
|
||||
|
||||
@@ -139,7 +139,7 @@ export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsP
|
||||
{renderInfoRow('文件格式', fileInfo.fileFormat)}
|
||||
{renderInfoRow('页数', `${fileInfo.pageCount}页`)}
|
||||
{renderInfoRow('上传时间', fileInfo.uploadTime)}
|
||||
{renderInfoRow('上传用户', fileInfo.uploadUser)}
|
||||
{/* {renderInfoRow('上传用户', fileInfo.uploadUser)} */}
|
||||
</div>
|
||||
))}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ export function FilePreview({ fileContent, activeReviewPointResultId, targetPage
|
||||
|
||||
const pageElement = document.getElementById(pageElementId);
|
||||
if (pageElement) {
|
||||
console.log(`跳转到第${newTargetPage}页,对应评查点结果ID: ${activeReviewPointResultId}`);
|
||||
// console.log(`跳转到第${newTargetPage}页,对应评查点结果ID: ${activeReviewPointResultId}`);
|
||||
pageElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
} else {
|
||||
console.warn(`未找到页面元素: ${pageElementId}`);
|
||||
@@ -262,7 +262,7 @@ export function FilePreview({ fileContent, activeReviewPointResultId, targetPage
|
||||
// PDF文档加载成功回调函数
|
||||
function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
|
||||
setNumPages(numPages);
|
||||
console.log("PDF加载成功,页数:", numPages);
|
||||
// console.log("PDF加载成功,页数:", numPages);
|
||||
}
|
||||
|
||||
// 计算页面在缩放后的实际间距
|
||||
|
||||
@@ -131,13 +131,6 @@ interface Statistics {
|
||||
score: number;
|
||||
}
|
||||
|
||||
// 统一规则的类型
|
||||
// interface pointRule {
|
||||
// id: string;
|
||||
// type: string;
|
||||
// config: Record<string, unknown>;
|
||||
// }
|
||||
|
||||
interface ReviewPointsListProps {
|
||||
reviewPoints: ReviewPoint[];
|
||||
statistics: Statistics;
|
||||
@@ -240,11 +233,14 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const textRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const isTableLike = content.includes('\t') && content.includes('\n');
|
||||
|
||||
useEffect(() => {
|
||||
const checkTextOverflow = () => {
|
||||
const element = textRef.current;
|
||||
if (element) {
|
||||
setShowTooltip(element.scrollHeight > element.clientHeight);
|
||||
// 如果是表格格式,总是显示tooltip;否则只在文本溢出时显示
|
||||
setShowTooltip(isTableLike || element.scrollHeight > element.clientHeight);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -253,7 +249,7 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
return () => {
|
||||
window.removeEventListener('resize', checkTextOverflow);
|
||||
};
|
||||
}, [content]);
|
||||
}, [content, isTableLike]);
|
||||
|
||||
// 解析表格数据
|
||||
const parseTableData = (text: string) => {
|
||||
@@ -268,7 +264,7 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
const hasHeader = tableData.length > 0;
|
||||
|
||||
return (
|
||||
<div className="overflow-auto max-h-[400px]">
|
||||
<div>
|
||||
<table className="min-w-full border-collapse border border-gray-300">
|
||||
{hasHeader && (
|
||||
<thead>
|
||||
@@ -307,8 +303,7 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 检测内容是否像表格
|
||||
const isTableLike = content.includes('\t') && content.includes('\n');
|
||||
|
||||
|
||||
return (
|
||||
<div className="text-xs p-1 rounded cursor-text w-full text-left">
|
||||
@@ -321,6 +316,8 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
|
||||
showArrow={true}
|
||||
className="tooltip-custom-offset"
|
||||
// fixedPlacement={true}
|
||||
// scrollable={true}
|
||||
maxHeight={400}
|
||||
>
|
||||
<div className="text-gray-800 break-all overflow-hidden line-clamp-2" ref={textRef}>
|
||||
{content}
|
||||
@@ -352,8 +349,8 @@ export function ReviewPointsList({
|
||||
// 添加重新审核意见的状态/ 用户输入的修改内容 / 用户提前写好的修改内容
|
||||
const [manualReviewNotes, setManualReviewNotes] = useState<Record<string, string>>({});
|
||||
|
||||
// 存放属于有无判断,格式判断,逻辑判断,正则表达式这一类的评查点规则设置
|
||||
// const [otherRule, setOtherRule] = useState<Record<string, unknown>[]>([]);
|
||||
// 存放评查点ID与有效页码的映射
|
||||
const [effectivePages, setEffectivePages] = useState<Record<string, number>>({});
|
||||
|
||||
// 初始化建议文本
|
||||
useEffect(() => {
|
||||
@@ -874,6 +871,35 @@ export function ReviewPointsList({
|
||||
// 处理配对数据
|
||||
const pairs = config.pairs;
|
||||
|
||||
// 获取第一个有效页码
|
||||
if (reviewPoint.id && !effectivePages[reviewPoint.id]) {
|
||||
for (const pair of pairs) {
|
||||
// 检查sourceField中是否有有效页码
|
||||
const sourceFieldKey = Object.keys(pair.sourceField)[0];
|
||||
if (sourceFieldKey && pair.sourceField[sourceFieldKey].page &&
|
||||
Number(pair.sourceField[sourceFieldKey].page) > 0) {
|
||||
// 保存页码
|
||||
setEffectivePages(prev => ({
|
||||
...prev,
|
||||
[reviewPoint.id || '']: Number(pair.sourceField[sourceFieldKey].page)
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果sourceField没有有效页码,检查targetField
|
||||
const targetFieldKey = Object.keys(pair.targetField)[0];
|
||||
if (targetFieldKey && pair.targetField[targetFieldKey].page &&
|
||||
Number(pair.targetField[targetFieldKey].page) > 0) {
|
||||
// 保存页码
|
||||
setEffectivePages(prev => ({
|
||||
...prev,
|
||||
[reviewPoint.id || '']: Number(pair.targetField[targetFieldKey].page)
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找链条关系
|
||||
const findChains = () => {
|
||||
type ChainItem = {
|
||||
@@ -1169,7 +1195,11 @@ export function ReviewPointsList({
|
||||
if (reviewPointId && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPointId, Number(item.data.page));
|
||||
}
|
||||
}else{
|
||||
}
|
||||
else if(reviewPoint.contentPage && reviewPoint.contentPage[item.field]){
|
||||
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[item.field]));
|
||||
}
|
||||
else{
|
||||
toastService.error(`没有找到${item.field}对应的索引内容`);
|
||||
}
|
||||
}}
|
||||
@@ -1177,7 +1207,7 @@ export function ReviewPointsList({
|
||||
>
|
||||
<div className="flex justify-between w-full">
|
||||
<ReactTableTooltip content={item.data.value?.toString() || ''} />
|
||||
{!item.data.page && !item.data.value && (
|
||||
{!item.data.page && (reviewPoint.contentPage && !reviewPoint.contentPage[item.field]) && (
|
||||
<i className="ri-information-line text-red-500 text-xs" title="没有找到对应的文书内容"></i>
|
||||
)}
|
||||
</div>
|
||||
@@ -1248,14 +1278,18 @@ export function ReviewPointsList({
|
||||
if (reviewPointId && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPointId, chain[0].data.page);
|
||||
}
|
||||
}else{
|
||||
}
|
||||
else if(reviewPoint.contentPage && reviewPoint.contentPage[chain[0].field]){
|
||||
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[chain[0].field]));
|
||||
}
|
||||
else{
|
||||
toastService.error(`没有找到${chain[0].field}对应的索引内容`);
|
||||
}
|
||||
}}
|
||||
aria-label={`查看${chain[0].field}内容详情`}
|
||||
>
|
||||
<div className="value-source text-xs text-gray-500 mb-1">{chain[0].field}
|
||||
{!chain[0].data.page && !chain[0].data.value && (
|
||||
{!chain[0].data.page && (reviewPoint.contentPage && !reviewPoint.contentPage[chain[0].field]) && (
|
||||
<i className="ri-information-line text-red-500 text-xs ml-1" title="没有找到对应的文书内容"></i>
|
||||
)}
|
||||
</div>
|
||||
@@ -1270,14 +1304,18 @@ export function ReviewPointsList({
|
||||
if (reviewPointId && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPointId, chain[1].data.page);
|
||||
}
|
||||
}else{
|
||||
}
|
||||
else if(reviewPoint.contentPage && reviewPoint.contentPage[chain[1].field]){
|
||||
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[chain[1].field]));
|
||||
}
|
||||
else{
|
||||
toastService.error(`没有找到${chain[1].field}对应的索引内容`);
|
||||
}
|
||||
}}
|
||||
aria-label={`查看${chain[1].field}内容详情`}
|
||||
>
|
||||
<div className="value-source text-xs text-gray-500 mb-1">{chain[1].field}
|
||||
{!chain[1].data.page && !chain[1].data.value && (
|
||||
{!chain[1].data.page && (reviewPoint.contentPage && !reviewPoint.contentPage[chain[1].field]) && (
|
||||
<i className="ri-information-line text-red-500 text-xs ml-1" title="没有找到对应的文书内容"></i>
|
||||
)}
|
||||
</div>
|
||||
@@ -1404,6 +1442,8 @@ export function ReviewPointsList({
|
||||
e.stopPropagation();
|
||||
if (mainTypeValue.page && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPoint.id, Number(mainTypeValue.page));
|
||||
}else if(reviewPoint.contentPage && reviewPoint.contentPage[fieldKey]){
|
||||
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[fieldKey]));
|
||||
}else{
|
||||
toastService.error(`没有找到${fieldKey}对应的索引内容`);
|
||||
}
|
||||
@@ -1425,7 +1465,7 @@ export function ReviewPointsList({
|
||||
{/* 字段名称 */}
|
||||
<div className="text-xs text-gray-500 mb-1">
|
||||
{fieldKey}
|
||||
{!mainTypeValue.page && !mainTypeValue.value && (
|
||||
{!mainTypeValue.page && (reviewPoint.contentPage && !reviewPoint.contentPage[fieldKey]) && (
|
||||
<i className="ri-information-line text-red-500 text-xs ml-1" title="没有找到对应的文书内容"></i>
|
||||
)}
|
||||
{/* 缺失显示 */}
|
||||
@@ -1492,6 +1532,19 @@ export function ReviewPointsList({
|
||||
|
||||
// 如果配置不存在,不渲染任何内容
|
||||
if (!config) return null;
|
||||
|
||||
// 获取第一个有效页码
|
||||
if (reviewPoint.id && !effectivePages[reviewPoint.id] && config.fields) {
|
||||
for (const field of Object.values(config.fields || {})) {
|
||||
if (field.page && Number(field.page) > 0) {
|
||||
setEffectivePages(prev => ({
|
||||
...prev,
|
||||
[reviewPoint.id || '']: Number(field.page)
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建一个数组来存储需要渲染的JSX元素
|
||||
const fieldElements: JSX.Element[] = [];
|
||||
@@ -1510,6 +1563,8 @@ export function ReviewPointsList({
|
||||
e.stopPropagation();
|
||||
if (value.page && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPoint.id, Number(value.page));
|
||||
}else if(reviewPoint.contentPage && reviewPoint.contentPage[key]){
|
||||
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[key]));
|
||||
}else{
|
||||
toastService.error(`没有找到${key}对应的索引内容`);
|
||||
}
|
||||
@@ -1520,6 +1575,8 @@ export function ReviewPointsList({
|
||||
e.preventDefault();
|
||||
if (value.page && typeof onReviewPointSelect === 'function') {
|
||||
onReviewPointSelect(reviewPoint.id, Number(value.page));
|
||||
}else if(reviewPoint.contentPage && reviewPoint.contentPage[key]){
|
||||
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[key]));
|
||||
}else{
|
||||
toastService.error(`没有找到${key}对应的索引内容`);
|
||||
}
|
||||
@@ -1533,7 +1590,7 @@ export function ReviewPointsList({
|
||||
<div className="text-xs text-left text-gray-500 mb-1">
|
||||
{key}
|
||||
{/* 没有抽取到目录内容,page和value都为空 */}
|
||||
{!value.page && !value.value && (
|
||||
{!value.page && (reviewPoint.contentPage && !reviewPoint.contentPage[key]) && (
|
||||
<i className="ri-information-line text-red-500 text-xs ml-1" title="没有找到对应的文书内容"></i>
|
||||
)}
|
||||
{/* 缺失显示 */}
|
||||
@@ -1851,6 +1908,34 @@ export function ReviewPointsList({
|
||||
for (const key in fieldKeyMap) {
|
||||
mergedRules.push(fieldKeyMap[key]);
|
||||
}
|
||||
|
||||
// 获取第一个有效页码
|
||||
if (reviewPoint.id && !effectivePages[reviewPoint.id]) {
|
||||
// 遍历合并后的规则数组,查找第一个有效页码
|
||||
for (const rule of mergedRules) {
|
||||
// 遍历字段类型对象
|
||||
const typeEntries = Object.entries(rule.fieldValue.type);
|
||||
|
||||
// 遍历每种类型规则
|
||||
for (const [, typeValue] of typeEntries) {
|
||||
// 检查是否有有效页码
|
||||
if (typeValue.page && Number(typeValue.page) > 0) {
|
||||
// 找到有效页码,设置状态并跳出循环
|
||||
setEffectivePages(prev => ({
|
||||
...prev,
|
||||
[reviewPoint.id || '']: Number(typeValue.page)
|
||||
}));
|
||||
// 使用break跳出当前循环
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果已经找到有效页码,跳出外层循环
|
||||
if (reviewPoint.id && effectivePages[reviewPoint.id]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返回合并后的规则数组
|
||||
return mergedRules;
|
||||
@@ -2148,24 +2233,19 @@ export function ReviewPointsList({
|
||||
const reviewPoint = reviewPoints.find(result => result.id === id);
|
||||
|
||||
// 如果评查点存在
|
||||
if (reviewPoint) {
|
||||
// // 使用checkContentPage方法获取页码和key
|
||||
// const { pageIndex, key } = checkContentPage(reviewPoint);
|
||||
|
||||
// // 如果有有效页码,传递ID和页码
|
||||
// if (pageIndex > 0) {
|
||||
// console.log(`跳转到页面 ${pageIndex},对应内容 ${key || '未知'}`);
|
||||
// onReviewPointSelect(id, pageIndex);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // 没有有效页码,只传递ID
|
||||
onReviewPointSelect(id);
|
||||
// console.log(`没有有效页码---评查点ID:${reviewPoint.pointId},评查点结果ID:${id}`);
|
||||
if (reviewPoint) {
|
||||
// 如果effectivePages有值,使用它
|
||||
if (reviewPoint.id && effectivePages[reviewPoint.id]) {
|
||||
// console.log('effectivePages', effectivePages[reviewPoint.id]);
|
||||
onReviewPointSelect(id, effectivePages[reviewPoint.id]);
|
||||
// return;
|
||||
} else {
|
||||
// 没有有效页码,只传递ID
|
||||
onReviewPointSelect(id);
|
||||
}
|
||||
} else {
|
||||
// // 没有找到评查点,只传递ID
|
||||
// 没有找到评查点,只传递ID
|
||||
onReviewPointSelect(id);
|
||||
// console.log(`没有找到评查点---评查点结果ID:${id}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2225,7 +2305,7 @@ export function ReviewPointsList({
|
||||
tabIndex={0}
|
||||
style={{ userSelect: 'text' }}
|
||||
onClick={() => {
|
||||
console.log('reviewPoint', reviewPoint);
|
||||
// console.log('reviewPoint', reviewPoint);
|
||||
handleReviewPointClick(reviewPoint.id);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
|
||||
@@ -3,25 +3,30 @@ interface ActionButtonsProps {
|
||||
onSave?: () => void;
|
||||
onSaveDraft?: () => void;
|
||||
isEditMode?: boolean;
|
||||
showButtons?: boolean;
|
||||
}
|
||||
|
||||
export function ActionButtons({ onSave, onSaveDraft, isEditMode }: ActionButtonsProps) {
|
||||
export function ActionButtons({ onSave, onSaveDraft, isEditMode, showButtons = true }: ActionButtonsProps) {
|
||||
return (
|
||||
<div className="flex justify-center space-x-4 mt-8 mb-4">
|
||||
<button
|
||||
type="button"
|
||||
className="ant-btn ant-btn-primary min-w-[120px]"
|
||||
onClick={onSave}
|
||||
>
|
||||
<i className="ri-save-line mr-1"></i> {isEditMode ? '保存修改' : '保存'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="ant-btn ant-btn-default min-w-[120px] !hidden"
|
||||
onClick={onSaveDraft}
|
||||
>
|
||||
<i className="ri-draft-line mr-1"></i> {isEditMode ? '另存为草稿' : '保存草稿'}
|
||||
</button>
|
||||
{showButtons && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="ant-btn ant-btn-primary min-w-[120px]"
|
||||
onClick={onSave}
|
||||
>
|
||||
<i className="ri-save-line mr-1"></i> {isEditMode ? '保存修改' : '保存'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="ant-btn ant-btn-default min-w-[120px] !hidden"
|
||||
onClick={onSaveDraft}
|
||||
>
|
||||
<i className="ri-draft-line mr-1"></i> {isEditMode ? '另存为草稿' : '保存草稿'}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
type="default"
|
||||
className="min-w-[120px] focus:!ring-gray-300"
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
interface PageHeaderProps {
|
||||
title: string;
|
||||
onSave?: () => void;
|
||||
showSaveButton?: boolean;
|
||||
}
|
||||
|
||||
export function PageHeader({ title, onSave }: PageHeaderProps) {
|
||||
export function PageHeader({ title, onSave, showSaveButton = true }: PageHeaderProps) {
|
||||
return (
|
||||
<div className="flex justify-between items-center pb-2 mb-4 border-b border-gray-200">
|
||||
<h1 className="text-xl font-medium text-gray-800">{title}</h1>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="ant-btn ant-btn-primary"
|
||||
onClick={onSave}
|
||||
>
|
||||
<i className="ri-save-line mr-1"></i> 保存
|
||||
</button>
|
||||
{showSaveButton && (
|
||||
<button
|
||||
type="button"
|
||||
className="ant-btn ant-btn-primary"
|
||||
onClick={onSave}
|
||||
>
|
||||
<i className="ri-save-line mr-1"></i> 保存
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -243,7 +243,7 @@ export function ReviewSettings({
|
||||
}
|
||||
|
||||
// 记录初始化处理
|
||||
console.log("ReviewSettings开始初始化,数据:", initialData);
|
||||
// console.log("ReviewSettings开始初始化,数据:", initialData);
|
||||
|
||||
// 保存初始数据引用,用于后续比较
|
||||
initialDataRef.current = JSON.parse(JSON.stringify(initialData));
|
||||
@@ -255,7 +255,7 @@ export function ReviewSettings({
|
||||
if (initialData) {
|
||||
// 处理初始规则数据
|
||||
if (initialData.rules && Array.isArray(initialData.rules) && initialData.rules.length > 0) {
|
||||
console.log("设置初始规则数据:", initialData.rules);
|
||||
// console.log("设置初始规则数据:", initialData.rules);
|
||||
|
||||
const validRules = initialData.rules.map(rule => {
|
||||
// 确保每个规则都有id
|
||||
@@ -394,7 +394,7 @@ export function ReviewSettings({
|
||||
const newFields = uniqueFields.filter((field: string) => !availableFields.includes(field));
|
||||
|
||||
if (newFields.length > 0 || deletedFields.length > 0) {
|
||||
console.log('Updating fields in checkAndUpdateFields - deleted:', deletedFields, 'new:', newFields);
|
||||
// console.log('Updating fields in checkAndUpdateFields - deleted:', deletedFields, 'new:', newFields);
|
||||
// 设置最新的可用字段列表
|
||||
setAvailableFields(uniqueFields);
|
||||
|
||||
@@ -420,7 +420,7 @@ export function ReviewSettings({
|
||||
|
||||
// 处理已删除字段的函数
|
||||
const handleDeletedFields = (deletedFields: string[]) => {
|
||||
console.log("处理已删除字段:", deletedFields);
|
||||
// console.log("处理已删除字段:", deletedFields);
|
||||
|
||||
// 如果没有删除的字段,则直接返回
|
||||
if (!deletedFields || deletedFields.length === 0) return;
|
||||
@@ -513,7 +513,7 @@ export function ReviewSettings({
|
||||
|
||||
// 如果配置有实质性修改,记录日志
|
||||
if (configModified) {
|
||||
console.log(`规则(ID: ${rule.id}, 类型: ${rule.type})已清除对已删除字段的引用`);
|
||||
// console.log(`规则(ID: ${rule.id}, 类型: ${rule.type})已清除对已删除字段的引用`);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -877,7 +877,7 @@ export function ReviewSettings({
|
||||
!(config.availableFields as string[]).every((field) => availableFields.includes(field))))) {
|
||||
// 延迟更新以避免在渲染过程中修改状态
|
||||
setTimeout(() => {
|
||||
console.log('Updating rule config with new available fields:', availableFields);
|
||||
// console.log('Updating rule config with new available fields:', availableFields);
|
||||
const updatedConfig = { ...config, availableFields: availableFields };
|
||||
handleRuleConfigChange(id, updatedConfig);
|
||||
}, 0);
|
||||
@@ -915,7 +915,7 @@ export function ReviewSettings({
|
||||
value="and"
|
||||
checked={!config.logic || config.logic === 'and'}
|
||||
onChange={(e) => {
|
||||
console.log(`[调试] 选择判断逻辑 and,规则ID: ${id}, 当前值: ${config.logic}`);
|
||||
// console.log(`[调试] 选择判断逻辑 and,规则ID: ${id}, 当前值: ${config.logic}`);
|
||||
handleRuleConfigChange(id, { logic: e.target.value });
|
||||
// 直接触发配置更新
|
||||
generateEvaluationConfig();
|
||||
@@ -932,7 +932,7 @@ export function ReviewSettings({
|
||||
value="or"
|
||||
checked={config.logic === 'or'}
|
||||
onChange={(e) => {
|
||||
console.log(`[调试] 选择判断逻辑 or,规则ID: ${id}, 当前值: ${config.logic}`);
|
||||
// console.log(`[调试] 选择判断逻辑 or,规则ID: ${id}, 当前值: ${config.logic}`);
|
||||
handleRuleConfigChange(id, { logic: e.target.value });
|
||||
// 直接触发配置更新
|
||||
generateEvaluationConfig();
|
||||
|
||||
@@ -93,12 +93,12 @@ export function DateRangePicker({
|
||||
try {
|
||||
// 检查并记录showPicker是否可用
|
||||
const hasShowPicker = typeof inputRef.current.showPicker === 'function';
|
||||
console.log('showPicker API available:', hasShowPicker);
|
||||
// console.log('showPicker API available:', hasShowPicker);
|
||||
|
||||
// 尝试使用showPicker API
|
||||
if (hasShowPicker) {
|
||||
inputRef.current.showPicker();
|
||||
console.log('showPicker called successfully');
|
||||
// console.log('showPicker called successfully');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to show date picker:', error);
|
||||
@@ -236,7 +236,7 @@ export function SimpleDateRangePicker({
|
||||
// 处理日期输入框全局点击
|
||||
const handleWrapperClick = (inputRef: React.RefObject<HTMLInputElement>) => {
|
||||
if (inputRef.current) {
|
||||
console.log('Wrapper clicked, triggering date input');
|
||||
// console.log('Wrapper clicked, triggering date input');
|
||||
// 点击整个包装器区域时,触发输入框点击
|
||||
inputRef.current.focus();
|
||||
inputRef.current.click();
|
||||
@@ -244,12 +244,12 @@ export function SimpleDateRangePicker({
|
||||
try {
|
||||
// 检查并记录showPicker是否可用
|
||||
const hasShowPicker = typeof inputRef.current.showPicker === 'function';
|
||||
console.log('showPicker API available:', hasShowPicker);
|
||||
// console.log('showPicker API available:', hasShowPicker);
|
||||
|
||||
// 尝试使用showPicker API
|
||||
if (hasShowPicker) {
|
||||
inputRef.current.showPicker();
|
||||
console.log('showPicker called successfully');
|
||||
// console.log('showPicker called successfully');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to show date picker:', error);
|
||||
|
||||
@@ -24,6 +24,8 @@ export interface TooltipProps {
|
||||
className?: string; // 自定义类名
|
||||
onVisibleChange?: (visible: boolean) => void; // 显示状态变化回调
|
||||
fixedPlacement?: boolean; // 是否固定显示位置,不自动切换
|
||||
maxHeight?: number | string; // 最大高度,超出将显示滚动条
|
||||
scrollable?: boolean; // 是否可滚动
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +47,9 @@ export function Tooltip({
|
||||
maxWidth = 320,
|
||||
className = '',
|
||||
onVisibleChange,
|
||||
fixedPlacement = false // 默认不固定位置
|
||||
fixedPlacement = false, // 默认不固定位置
|
||||
maxHeight = 300, // 默认最大高度300px
|
||||
scrollable = true // 默认允许滚动
|
||||
}: TooltipProps) {
|
||||
// 使用内部状态管理提示框显示状态(非受控模式)
|
||||
const [visible, setVisible] = useState(false);
|
||||
@@ -59,6 +63,7 @@ export function Tooltip({
|
||||
// 引用DOM元素
|
||||
const triggerRef = useRef<HTMLDivElement>(null);
|
||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// 保存当前实际显示位置
|
||||
const [actualPlacement, setActualPlacement] = useState<TooltipPlacement>(placement);
|
||||
@@ -99,6 +104,17 @@ export function Tooltip({
|
||||
? `${maxWidth}px`
|
||||
: String(maxWidth);
|
||||
|
||||
// 如果内容可滚动,设置最大高度,但仅应用于内容容器而不是整个tooltip
|
||||
if (scrollable && contentRef.current) {
|
||||
contentRef.current.style.maxHeight = typeof maxHeight === 'number'
|
||||
? `${maxHeight}px`
|
||||
: String(maxHeight);
|
||||
|
||||
// 确保只有内容区域显示滚动条,整个tooltip容器不滚动
|
||||
contentRef.current.style.overflowY = 'auto';
|
||||
tooltipRef.current.style.overflow = 'visible';
|
||||
}
|
||||
|
||||
// 计算各个方向的位置
|
||||
let top = 0, left = 0;
|
||||
let arrowTop = 0, arrowLeft = 0;
|
||||
@@ -154,7 +170,7 @@ export function Tooltip({
|
||||
if (top < 0) {
|
||||
if (currentPlacement === 'top') {
|
||||
// 如果上方放不下,切换到下方
|
||||
top = triggerRect.bottom + arrowSize;
|
||||
top = triggerRect.bottom + arrowSize - 8;
|
||||
arrowTop = -arrowSize;
|
||||
|
||||
// 更新实际位置为bottom
|
||||
@@ -347,19 +363,22 @@ export function Tooltip({
|
||||
|
||||
// 渲染提示框内容
|
||||
const renderTooltipContent = () => {
|
||||
// 根据是否启用滚动来确定内容容器类名
|
||||
const contentClassName = scrollable ? 'tooltip-content-inner tooltip-scrollable' : 'tooltip-content-inner';
|
||||
|
||||
if (rich) {
|
||||
return (
|
||||
<div className="tooltip-content">
|
||||
<div className="tooltip-content" ref={contentRef}>
|
||||
{header && <div className="tooltip-header">{header}</div>}
|
||||
<div className="tooltip-body">{content}</div>
|
||||
<div className={contentClassName}>{content}</div>
|
||||
{footer && <div className="tooltip-footer">{footer}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="tooltip-content">
|
||||
<div>{content}</div>
|
||||
<div className="tooltip-content" ref={contentRef}>
|
||||
<div className={contentClassName}>{content}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user