新增提示Toast组件

This commit is contained in:
2025-04-21 09:22:13 +08:00
parent 01d93522b8
commit 5c2c367856
36 changed files with 2609 additions and 478 deletions
+300 -114
View File
@@ -25,6 +25,7 @@ import { useState, useEffect } from 'react';
*/
export interface ReviewPoint {
id: string;
pointName: string;
title: string;
groupName: string;
status: string;
@@ -34,7 +35,7 @@ export interface ReviewPoint {
humanReviewNote?: string;
humanReviewBy?: string;
humanReviewTime?: string;
contentPage?: number[];
contentPage?: Record<string, number[]>;
position?: {
section: string;
index: number;
@@ -133,7 +134,7 @@ export function ReviewPointsList({
// 清除编辑状态
setEditingReviewPoint(null);
alert(`${action === 'approve' ? '通过' : '不通过'}了评查点 ${reviewPointResultId},审核内容: ${message}`);
// alert(`${action === 'approve' ? '通过' : '不通过'}了评查点 ${reviewPointResultId},审核内容: ${message}`);
};
/**
@@ -143,6 +144,7 @@ export function ReviewPointsList({
const filteredReviewPoints = reviewPoints.filter(point => {
// 匹配搜索文本
const matchesSearch = searchText === '' ||
point.pointName.toLowerCase().includes(searchText.toLowerCase()) ||
point.title.toLowerCase().includes(searchText.toLowerCase()) ||
point.groupName.toLowerCase().includes(searchText.toLowerCase()) ||
(typeof point.content === 'string' && point.content.toLowerCase().includes(searchText.toLowerCase())) ||
@@ -297,7 +299,7 @@ export function ReviewPointsList({
<i className="ri-search-line absolute left-2 top-0.5 text-gray-400"></i>
{searchText && (
<button
className="absolute right-2 top-1.5 text-gray-400 hover:text-gray-600"
className="absolute right-2 top-0.5 text-gray-400 hover:text-gray-600"
onClick={() => setSearchText('')}
>
<i className="ri-close-line"></i>
@@ -430,8 +432,8 @@ export function ReviewPointsList({
// 如果当前评查点不处于编辑状态,只显示简单信息
if (editingReviewPoint !== reviewPoint.id) {
// 根据result和status决定渲染哪种样式
if (reviewPoint.result === true || (reviewPoint.result === undefined && reviewPoint.status === 'success')) {
// 已通过的评查点只显示基本信息和人工审核注释
if (reviewPoint.result === true ){
// 已通过的评查点只显示基本信息和人工审核注释 delete
if (reviewPoint.needsHumanReview && reviewPoint.humanReviewNote) {
return (
<div className="mt-2">
@@ -449,6 +451,7 @@ export function ReviewPointsList({
// 处理 result=true 且 postAction=manual 的情况
if (reviewPoint.postAction === 'manual') {
// 处理重新审核意见的提交
const handleReReview = (reviewPointId: string, status: string) => {
const note = manualReviewNotes[reviewPointId] || '';
if (!note.trim()) {
@@ -461,6 +464,7 @@ export function ReviewPointsList({
// 可以添加提交成功后的状态更新等操作
};
// 处理重新审核意见的输入
const handleNoteChange = (reviewPointId: string, text: string) => {
setManualReviewNotes(prev => ({
...prev,
@@ -503,7 +507,70 @@ export function ReviewPointsList({
);
}
return null;
// 处理 result=true 且 postAction!=manual 的情况
return (
<>
{checkContentPage(reviewPoint).pageIndex === 0 && (
<p className="text-xs text-red-500 select-text text-left"></p>
)}
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
{typeof reviewPoint.content === 'object' && reviewPoint.content !== null ? (
// 当 content 是对象时的渲染方式
<div>
{Object.entries(reviewPoint.content).map(([key, value], index) => (
<div
key={index}
className="mb-2 pb-2 border-b border-gray-100 last:border-b-0 last:mb-0 last:pb-0 cursor-pointer hover:bg-gray-100 transition-colors duration-200 rounded p-1"
onClick={(e) => {
// 阻止事件冒泡,防止触发父元素的点击事件
e.stopPropagation();
console.log(`非通过:单独点击${key}----`,reviewPoint);
// 检查评查点是否有 contentPage 以及当前 key 对应的页码数组
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
// 获取当前 key 对应的第一个页码并跳转
console.log(`非通过:单独点击${key}----页码---`,reviewPoint.contentPage[key][0]);
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
} else {
// 如果没有对应页码,弹出提示
// alert(`无法找到"${key}"对应的内容页面`);
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
} else {
// alert(`无法找到"${key}"对应的内容页面`);
}
}
}}
role="button"
tabIndex={0}
aria-label={`查看${key}内容详情`}
>
{/* 使用flex布局使key和状态标签左右对齐 */}
<div className="flex justify-between items-center mb-1">
<span className="text-xs">{key}</span>
<span className={`text-xs ${value ? 'text-error' : 'text-warning'}`}>
{value ? '' : '缺失'}
</span>
</div>
<p className="text-xs text-left select-text">{value || (value === '' ? <span className="invisible"></span> : '')}</p>
</div>
))}
</div>
) : (
// 当 content 是字符串时的渲染方式
<>
</>
)}
</div>
</>
);
}
// 非通过状态,显示内容和修改建议
@@ -512,35 +579,108 @@ export function ReviewPointsList({
return (
<div className="mt-2">
{/* 没有索引内容提示 */}
{reviewPoint.contentPage &&
checkContentPage(reviewPoint).pageIndex === 0 && (
// <div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
<p className="text-xs text-red-500 select-text text-left"></p>
// </div>
)}
{/* 建议内容显示区域 */}
{reviewPoint.suggestion && (
<div className="p-2 bg-blue-50 rounded border border-blue-200 text-xs mb-3 select-text">
<div className="flex items-start">
<i className="ri-information-line text-blue-500 mr-2 mt-0.5"></i>
<p className="text-xs text-gray-600 select-text text-left">{reviewPoint.suggestion}</p>
</div>
</div>
)}
{/* 法律依据内容 */}
{reviewPoint.legalBasis && (typeof reviewPoint.legalBasis === 'object') && (
(reviewPoint.legalBasis.name || reviewPoint.legalBasis.content ||
(reviewPoint.legalBasis.articles && Array.isArray(reviewPoint.legalBasis.articles) && reviewPoint.legalBasis.articles.length > 0)) && (
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
<div className="flex justify-between items-center mb-1">
<span className="text-xs font-medium"></span>
</div>
{reviewPoint.legalBasis.name && (
<p className="text-xs text-left mb-1 select-text">{reviewPoint.legalBasis.name}</p>
)}
{reviewPoint.legalBasis.content && (
<p className="text-xs text-left mb-1 select-text"><span className="font-medium"></span>{reviewPoint.legalBasis.content}</p>
)}
{reviewPoint.legalBasis.articles && Array.isArray(reviewPoint.legalBasis.articles) && reviewPoint.legalBasis.articles.length > 0 && (
<div>
<p className="text-xs text-left font-medium mb-1"></p>
<ul className="list-disc pl-4 select-text">
{reviewPoint.legalBasis.articles.map((item, index) => (
<li key={index} className="text-xs text-left select-text">
{typeof item === 'string' ? item :
typeof item === 'object' && item !== null ?
(item.name ? `${item.name}: ${item.content || ''}` :
item.content || JSON.stringify(item)) :
String(item)}
</li>
))}
</ul>
</div>
)}
</div>
)
)}
{reviewPoint.content !== null && (
(typeof reviewPoint.content === 'string' && reviewPoint.content !== '') ||
(typeof reviewPoint.content === 'object' && Object.keys(reviewPoint.content).length > 0)
) && (
<>
{/* 建议内容显示区域 */}
{reviewPoint.suggestion && (
<div className="p-2 bg-blue-50 rounded border border-blue-200 text-xs mb-3 select-text">
<div className="flex items-start">
<i className="ri-information-line text-blue-500 mr-2 mt-0.5"></i>
<p className="text-xs text-gray-600 select-text">{reviewPoint.suggestion}</p>
</div>
</div>
)}
{/* 内容显示区域 */}
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
{/* 移除顶部的"当前值"标题,在每个内容项中显示 */}
{typeof reviewPoint.content === 'object' && reviewPoint.content !== null ? (
// 当 content 是对象时的渲染方式
<div>
{Object.entries(reviewPoint.content).map(([key, value], index) => (
<div key={index} className="mb-2 pb-2 border-b border-gray-100 last:border-b-0 last:mb-0 last:pb-0">
<div
key={index}
className="mb-2 pb-2 border-b border-gray-100 last:border-b-0 last:mb-0 cursor-pointer hover:bg-gray-100 transition-colors duration-200 rounded p-1"
onClick={(e) => {
// 阻止事件冒泡,防止触发父元素的点击事件
e.stopPropagation();
console.log(`非通过:单独点击${key}----`,reviewPoint);
// 检查评查点是否有 contentPage 以及当前 key 对应的页码数组
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
// 获取当前 key 对应的第一个页码并跳转
console.log(`非通过:单独点击${key}----页码---`,reviewPoint.contentPage[key][0]);
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
} else {
// 如果没有对应页码,弹出提示
// alert(`无法找到"${key}"对应的内容页面`);
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
} else {
// alert(`无法找到"${key}"对应的内容页面`);
}
}
}}
role="button"
tabIndex={0}
aria-label={`查看${key}内容详情`}
>
{/* 使用flex布局使key和状态标签左右对齐 */}
<div className="flex justify-between items-center mb-1">
<span className="text-xs">{key}</span>
{/* <span className={`text-xs ${isErrorStatus ? 'text-error' : 'text-warning'}`}>
{isErrorStatus ? '不符合规范' : '需优化'}
</span> */}
<span className={`text-xs ${isErrorStatus ? 'text-error' : 'text-warning'}`}>
{value ? '' : '缺失'}
</span>
@@ -549,6 +689,8 @@ export function ReviewPointsList({
</div>
))}
</div>
) : (
// 当 content 是字符串时的渲染方式
<>
@@ -563,96 +705,65 @@ export function ReviewPointsList({
</>
)}
</div>
{/* 法律依据内容 */}
{reviewPoint.legalBasis && (typeof reviewPoint.legalBasis === 'object') && (
(reviewPoint.legalBasis.name || reviewPoint.legalBasis.content ||
(reviewPoint.legalBasis.articles && Array.isArray(reviewPoint.legalBasis.articles) && reviewPoint.legalBasis.articles.length > 0)) && (
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
<div className="flex justify-between items-center mb-1">
<span className="text-xs font-medium"></span>
</div>
{reviewPoint.legalBasis.name && (
<p className="text-xs text-left mb-1 select-text">{reviewPoint.legalBasis.name}</p>
)}
{reviewPoint.legalBasis.content && (
<p className="text-xs text-left mb-1 select-text"><span className="font-medium"></span>{reviewPoint.legalBasis.content}</p>
)}
{reviewPoint.legalBasis.articles && Array.isArray(reviewPoint.legalBasis.articles) && reviewPoint.legalBasis.articles.length > 0 && (
<div>
<p className="text-xs text-left font-medium mb-1"></p>
<ul className="list-disc pl-4 select-text">
{reviewPoint.legalBasis.articles.map((item, index) => (
<li key={index} className="text-xs text-left select-text">
{typeof item === 'string' ? item :
typeof item === 'object' && item !== null ?
(item.name ? `${item.name}: ${item.content || ''}` :
item.content || JSON.stringify(item)) :
String(item)}
</li>
))}
</ul>
</div>
)}
</div>
)
)}
{/* 建议修改区域 */}
{/* {(reviewPoint.postAction !== 'none') && ( */}
<div className="mb-2">
<div className="flex justify-between items-center mb-2">
<span className="text-gray-700 text-[0.8rem]">{reviewPoint.postAction === 'manual' ? "审核意见:" : "建议修改为:"}</span>
{/* <span className="text-green-500">符合规范</span> */}
</div>
<textarea
value={manualReviewNotes[reviewPoint.id] || ''}
placeholder={reviewPoint.postAction === 'manual' ? "请输入审核意见(可选)..." : "请输入建议修改内容..."}
onChange={(e) => handleManualReviewNotesChange(reviewPoint.id, e.target.value)}
className="text-xs w-full p-2 border rounded bg-white min-h-[100px] focus:outline-none focus:border-[#00684a] focus:shadow-[0_0_0_2px_rgba(0,104,74,0.2)]"
/>
</div>
{/* )} */}
{/* 操作按钮区域 */}
<div className="flex space-x-2 mt-2">
{/* 一键替换按钮 - 只有非人工审核的点或未通过的人工审核点才显示 */}
{(reviewPoint.postAction !== 'manual') && (
<button
className="replace-action flex-1 justify-center"
onClick={() => handleReplace(reviewPoint.id)}
>
<i className="ri-replace-line"></i>
</button>
)}
{/* 人工审核按钮 */}
{reviewPoint.postAction === 'manual' && (
<div className="w-full flex justify-end gap-2">
<button
className="bg-[#1890ff] hover:bg-blue-600 text-white py-1 px-2 rounded-md text-sm"
onClick={() => {
const note = manualReviewNotes[reviewPoint.id] || '';
handleReviewAction(reviewPoint.id, 'approve', note);
}}
>
<i className="ri-check-line mr-1"></i>
</button>
<button
className="bg-[#f5222d] hover:bg-red-600 text-white py-1 px-2 rounded-md text-sm"
onClick={() => {
const note = manualReviewNotes[reviewPoint.id] || '';
handleReviewAction(reviewPoint.id, 'reject', note);
}}
>
<i className="ri-close-line mr-1"></i>
</button>
</div>
)}
</div>
</>
)}
{/* 建议修改区域 */}
{/* {((reviewPoint.postAction === 'manual') || (reviewPoint.content !== null && Object.keys(reviewPoint.content).length > 0)) && ( */}
{(reviewPoint.postAction === 'manual') && (
<div className="mb-2">
<div className="flex justify-between items-center mb-2">
<span className="text-gray-700 text-[0.8rem]">{reviewPoint.postAction === 'manual' ? "审核意见:" : "建议修改为:"}</span>
{/* <span className="text-green-500">符合规范</span> */}
</div>
<textarea
value={manualReviewNotes[reviewPoint.id] || ''}
placeholder={reviewPoint.postAction === 'manual' ? "请输入审核意见(可选)..." : "请输入建议修改内容..."}
onChange={(e) => handleManualReviewNotesChange(reviewPoint.id, e.target.value)}
className="text-xs w-full p-2 border rounded bg-white min-h-[100px] focus:outline-none focus:border-[#00684a] focus:shadow-[0_0_0_2px_rgba(0,104,74,0.2)]"
/>
</div>
)}
{/* 操作按钮区域 */}
{/* {((reviewPoint.postAction === 'manual') || (reviewPoint.content !== null && Object.keys(reviewPoint.content).length > 0)) && ( */}
{(reviewPoint.postAction === 'manual') && (
<div className="flex space-x-2 mt-2">
{/* 一键替换按钮 - 只有非人工审核的点或未通过的人工审核点才显示 */}
{(reviewPoint.postAction !== 'manual') && (
<button
className="replace-action flex-1 justify-center"
onClick={() => handleReplace(reviewPoint.id)}
>
<i className="ri-replace-line"></i>
</button>
)}
{/* 人工审核按钮 */}
{reviewPoint.postAction === 'manual' && (
<div className="w-full flex justify-end gap-2">
<button
className="bg-[#1890ff] hover:bg-blue-600 text-white py-1 px-2 rounded-md text-sm"
onClick={() => {
const note = manualReviewNotes[reviewPoint.id] || '';
handleReviewAction(reviewPoint.id, 'approve', note);
}}
>
<i className="ri-check-line mr-1"></i>
</button>
<button
className="bg-[#f5222d] hover:bg-red-600 text-white py-1 px-2 rounded-md text-sm"
onClick={() => {
const note = manualReviewNotes[reviewPoint.id] || '';
handleReviewAction(reviewPoint.id, 'reject', note);
}}
>
<i className="ri-close-line mr-1"></i>
</button>
</div>
)}
</div>
)}
</div>
);
}
@@ -670,12 +781,45 @@ export function ReviewPointsList({
// 当 content 是对象时的渲染方式
<div>
{Object.entries(reviewPoint.content).map(([key, value], index) => (
<div key={index} className="mb-2 pb-2 border-b border-gray-100 last:border-b-0 last:mb-0 last:pb-0">
<div
key={index}
className="mb-2 pb-2 border-b border-gray-100 last:border-b-0 last:mb-0 last:pb-0 cursor-pointer hover:bg-gray-50 transition-colors duration-200 rounded p-1"
onClick={(e) => {
// 阻止事件冒泡,防止触发父元素的点击事件
e.stopPropagation();
console.log(`非通过:单独点击${key}----`,reviewPoint);
// 检查评查点是否有 contentPage 以及当前 key 对应的页码数组
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
// 获取当前 key 对应的第一个页码并跳转
console.log(`非通过:单独点击${key}----页码---`,reviewPoint.contentPage[key][0]);
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
} else {
// 如果没有对应页码,弹出提示
alert(`无法找到"${key}"对应的内容页面`);
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
} else {
alert(`无法找到"${key}"对应的内容页面`);
}
}
}}
role="button"
tabIndex={0}
aria-label={`查看${key}内容详情`}
>
{/* 使用flex布局使key和状态标签左右对齐 */}
<div className="flex justify-between items-center mb-1">
<span className="text-xs">{key}</span>
<span className={`text-xs ${isErrorStatus ? 'text-error' : 'text-warning'}`}>
{isErrorStatus ? '不符合规范' : '需优化'}
{/* {isErrorStatus ? '不符合规范' : '需优化'} */}
{value ? '' : '缺失'}
</span>
</div>
<p className="text-xs text-left select-text">{value || (value === '' ? <span className="invisible"></span> : '')}</p>
@@ -770,14 +914,54 @@ export function ReviewPointsList({
// 找到被点击的评查点
const reviewPoint = reviewPoints.find(point => point.id === id);
// 如果评查点存在并且有contentPage数组,传递第一个页码
if (reviewPoint && reviewPoint.contentPage && reviewPoint.contentPage.length > 0) {
onReviewPointSelect(id, reviewPoint.contentPage[0]);
// 如果评查点存在
if (reviewPoint) {
// 使用checkContentPage方法获取页码和key
const { pageIndex, key } = checkContentPage(reviewPoint);
// 如果有有效页码,传递ID和页码
if (pageIndex > 0) {
console.log(`跳转到页面 ${pageIndex},对应内容 ${key || '未知'}`);
onReviewPointSelect(id, pageIndex);
return;
}
// 没有有效页码,只传递ID
onReviewPointSelect(id);
} else {
// 没有找到评查点,只传递ID
onReviewPointSelect(id);
}
};
// 检查评查点的contentPage
const checkContentPage = (reviewPoint: ReviewPoint): { pageIndex: number, key?: string, id: string } => {
// 返回对象初始化
const result = { pageIndex: 0, id: reviewPoint.id };
// 如果contentPage不存在或是空对象,返回默认值
if (!reviewPoint.contentPage || Object.keys(reviewPoint.contentPage).length === 0) {
return result;
}
// 遍历contentPage中的每个key
for (const key of Object.keys(reviewPoint.contentPage)) {
const pageArr = reviewPoint.contentPage[key];
// 如果数组存在且长度大于0
if (pageArr && pageArr.length > 0) {
// 返回第一个找到的有效页码,以及对应的key
return {
pageIndex: pageArr[0],
key,
id: reviewPoint.id
};
}
}
// 如果遍历完所有key都没找到有效页码,返回默认值
return result;
};
// 组件主渲染函数
return (
<div className="review-points-panel select-text">
@@ -805,6 +989,8 @@ export function ReviewPointsList({
style={{ userSelect: 'text' }}
>
{/* 评查点标题和状态 */}
{/* 评查点名称 pointName*/}
<div className="review-point-title flex-1 text-left text-blue-500">{'评查点名称:'+reviewPoint.pointName}</div>
<div className="review-point-header flex justify-between items-start">
<div className="review-point-title flex-1 text-left">{reviewPoint.title}</div>
{/* 评查点所属分组 */}