添加评查意见的悬浮按钮
This commit is contained in:
@@ -32,6 +32,7 @@ export interface CrossCheckingTask {
|
|||||||
status: CrossCheckingTaskStatus;
|
status: CrossCheckingTaskStatus;
|
||||||
score: number;
|
score: number;
|
||||||
operation: string;
|
operation: string;
|
||||||
|
documentIds: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// API响应格式
|
// API响应格式
|
||||||
@@ -79,7 +80,8 @@ const mockTasks: CrossCheckingTask[] = [
|
|||||||
progress: 0,
|
progress: 0,
|
||||||
status: CrossCheckingTaskStatus.PENDING,
|
status: CrossCheckingTaskStatus.PENDING,
|
||||||
score: 0,
|
score: 0,
|
||||||
operation: '去评查'
|
operation: '去评查',
|
||||||
|
documentIds: [1, 2, 3, 4, 5]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
@@ -92,7 +94,8 @@ const mockTasks: CrossCheckingTask[] = [
|
|||||||
progress: 72,
|
progress: 72,
|
||||||
status: CrossCheckingTaskStatus.IN_PROGRESS,
|
status: CrossCheckingTaskStatus.IN_PROGRESS,
|
||||||
score: 0,
|
score: 0,
|
||||||
operation: '进行中'
|
operation: '进行中',
|
||||||
|
documentIds: [1, 2, 3, 4, 5]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
@@ -105,7 +108,8 @@ const mockTasks: CrossCheckingTask[] = [
|
|||||||
progress: 100,
|
progress: 100,
|
||||||
status: CrossCheckingTaskStatus.COMPLETED,
|
status: CrossCheckingTaskStatus.COMPLETED,
|
||||||
score: 95,
|
score: 95,
|
||||||
operation: '查看结果'
|
operation: '查看结果',
|
||||||
|
documentIds: [1, 2, 3, 4, 5]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
@@ -118,7 +122,8 @@ const mockTasks: CrossCheckingTask[] = [
|
|||||||
progress: 100,
|
progress: 100,
|
||||||
status: CrossCheckingTaskStatus.COMPLETED,
|
status: CrossCheckingTaskStatus.COMPLETED,
|
||||||
score: 85,
|
score: 85,
|
||||||
operation: '查看结果'
|
operation: '查看结果',
|
||||||
|
documentIds: [1, 2, 3, 4, 5]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
@@ -131,7 +136,8 @@ const mockTasks: CrossCheckingTask[] = [
|
|||||||
progress: 100,
|
progress: 100,
|
||||||
status: CrossCheckingTaskStatus.COMPLETED,
|
status: CrossCheckingTaskStatus.COMPLETED,
|
||||||
score: 92,
|
score: 92,
|
||||||
operation: '查看结果'
|
operation: '查看结果',
|
||||||
|
documentIds: [1, 2, 3, 4, 5]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ export async function getReviewPoints(fileId: string) {
|
|||||||
'document_id': `eq.${fileId}`
|
'document_id': `eq.${fileId}`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const scoringProposalsResponse = await postgrestGet('scoring_proposals', scoringProposalsParams);
|
const scoringProposalsResponse = await postgrestGet('cross_scoring_proposals', scoringProposalsParams);
|
||||||
|
|
||||||
if (scoringProposalsResponse.error) {
|
if (scoringProposalsResponse.error) {
|
||||||
return { error: scoringProposalsResponse.error, status: scoringProposalsResponse.status };
|
return { error: scoringProposalsResponse.error, status: scoringProposalsResponse.status };
|
||||||
@@ -404,8 +404,12 @@ export async function getReviewPoints(fileId: string) {
|
|||||||
// suggestion: '只是给建议的修改内容',
|
// suggestion: '只是给建议的修改内容',
|
||||||
|
|
||||||
result: result.evaluated_results?.result, // 记录评查结果,用于统计
|
result: result.evaluated_results?.result, // 记录评查结果,用于统计
|
||||||
|
// score是评查点设置的满分的分数
|
||||||
score: point.score || 0,
|
score: point.score || 0,
|
||||||
|
|
||||||
|
// finalScore是评查点对应评查结果最终所得的分数 用户交叉评查时使用
|
||||||
|
finalScore: result.final_score || 0,
|
||||||
|
|
||||||
postAction: point.post_action || '',
|
postAction: point.post_action || '',
|
||||||
// postAction: 'manual',
|
// postAction: 'manual',
|
||||||
|
|
||||||
|
|||||||
@@ -416,12 +416,17 @@ export function ReviewPointsList({
|
|||||||
// 状态管理
|
// 状态管理
|
||||||
const [searchText, setSearchText] = useState(''); // 搜索文本
|
const [searchText, setSearchText] = useState(''); // 搜索文本
|
||||||
const [statusFilter, setStatusFilter] = useState<string | null>(null); // 状态过滤
|
const [statusFilter, setStatusFilter] = useState<string | null>(null); // 状态过滤
|
||||||
|
const [evaluationResultIds, setEvaluationResultIds] = useState<number[]>([]); // 评分提案的evaluation_result_id
|
||||||
|
|
||||||
// 在组件中使用scoringProposals(这里只是简单使用以避免linter警告)
|
// 在组件中使用scoringProposals(这里只是简单使用以避免linter警告)
|
||||||
// 将来可以用于显示相关的评分提案信息
|
// 将来可以用于显示相关的评分提案信息
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (scoringProposals && scoringProposals.length > 0) {
|
if (scoringProposals && scoringProposals.length > 0) {
|
||||||
console.log('收到评分提案数据:', scoringProposals.length, '个提案');
|
console.log('收到评分提案数据:', scoringProposals.length, '个提案');
|
||||||
|
// 获取提案的evaluation_result_id
|
||||||
|
const evaluationResultIds = scoringProposals.map(proposal => Number(proposal.evaluation_result_id));
|
||||||
|
setEvaluationResultIds(evaluationResultIds);
|
||||||
|
// console.log('提案的evaluation_result_id:', evaluationResultIds);
|
||||||
}
|
}
|
||||||
}, [scoringProposals]);
|
}, [scoringProposals]);
|
||||||
|
|
||||||
@@ -447,6 +452,11 @@ export function ReviewPointsList({
|
|||||||
* 打开提出意见模态框
|
* 打开提出意见模态框
|
||||||
*/
|
*/
|
||||||
const handleOpenOpinionModal = (reviewPoint: ReviewPoint) => {
|
const handleOpenOpinionModal = (reviewPoint: ReviewPoint) => {
|
||||||
|
// 如果评分提案的evaluation_result_id包含当前评查点的id,则不打开模态框
|
||||||
|
if (evaluationResultIds.includes(Number(reviewPoint.id))) {
|
||||||
|
toastService.error('当前评查点已有意见提出项,可前往意见列表查看');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setSelectedReviewPoint(reviewPoint);
|
setSelectedReviewPoint(reviewPoint);
|
||||||
setOpinionForm({
|
setOpinionForm({
|
||||||
auditPoint: reviewPoint.pointName,
|
auditPoint: reviewPoint.pointName,
|
||||||
@@ -2098,8 +2108,34 @@ export function ReviewPointsList({
|
|||||||
// 组件主渲染函数
|
// 组件主渲染函数
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="review-points-panel select-text">
|
<div className="relative">
|
||||||
<TooltipPortal />
|
{/* 悬浮的意见数量显示 - 固定在左侧 */}
|
||||||
|
<div className="absolute left-[-35px] top-16 z-10 group cursor-pointer">
|
||||||
|
{/* 默认状态:竖向排列,窄宽度 */}
|
||||||
|
<div className="flex flex-col items-center bg-blue-50 px-2 py-2 rounded-lg border border-blue-200 shadow-md transition-all duration-300 group-hover:scale-0 group-hover:opacity-0 origin-top-right">
|
||||||
|
<i className="ri-chat-1-line text-blue-600 text-base"></i>
|
||||||
|
<span className="text-base text-blue-600 font-bold leading-tight whitespace-nowrap">{scoringProposals.length}</span>
|
||||||
|
<span className="text-xs text-blue-500 leading-tight whitespace-wrap">条</span>
|
||||||
|
<span className="text-xs text-blue-500 leading-tight whitespace-wrap">意</span>
|
||||||
|
<span className="text-xs text-blue-500 leading-tight whitespace-wrap">见</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 悬浮状态:横向排列,显示图标,数字放大 */}
|
||||||
|
<div className="absolute top-0 right-0 opacity-0 scale-0 group-hover:opacity-100 group-hover:scale-100 flex items-center bg-blue-50 px-3 py-2 rounded-lg border border-blue-200 shadow-lg transition-all duration-300 origin-top-right">
|
||||||
|
<button className="flex items-center" aria-label="点击查看详情">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<i className="ri-chat-1-line text-blue-600 text-base"></i>
|
||||||
|
<span className="text-xl text-blue-600 font-bold">{scoringProposals.length}</span>
|
||||||
|
<span className="text-xs text-blue-500 leading-tight whitespace-wrap">条</span>
|
||||||
|
<span className="text-xs text-blue-500 leading-tight whitespace-wrap">意</span>
|
||||||
|
<span className="text-xs text-blue-500 leading-tight whitespace-wrap">见</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="review-points-panel select-text">
|
||||||
|
<TooltipPortal />
|
||||||
{/* 面板头部 */}
|
{/* 面板头部 */}
|
||||||
<div className="review-panel-header py-2 px-4 flex items-center">
|
<div className="review-panel-header py-2 px-4 flex items-center">
|
||||||
<i className="ri-file-list-check-line text-primary mr-2"></i>
|
<i className="ri-file-list-check-line text-primary mr-2"></i>
|
||||||
@@ -2126,7 +2162,7 @@ export function ReviewPointsList({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
style={{ userSelect: 'text' }}
|
style={{ userSelect: 'text' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// console.log('reviewPoint', reviewPoint);
|
console.log('reviewPoint', reviewPoint);
|
||||||
handleReviewPointClick(reviewPoint.id);
|
handleReviewPointClick(reviewPoint.id);
|
||||||
}}
|
}}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
@@ -2182,104 +2218,105 @@ export function ReviewPointsList({
|
|||||||
renderEmptyState()
|
renderEmptyState()
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 提出意见模态框 */}
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpinionModalOpen}
|
|
||||||
onClose={handleCloseOpinionModal}
|
|
||||||
title="提出意见"
|
|
||||||
size="medium"
|
|
||||||
footer={
|
|
||||||
<div className="flex justify-end space-x-3">
|
|
||||||
<button
|
|
||||||
className="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50"
|
|
||||||
onClick={handleCloseOpinionModal}
|
|
||||||
disabled={isSubmittingOpinion}
|
|
||||||
>
|
|
||||||
取消
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="px-4 py-2 bg-green-700 border border-transparent rounded-md text-sm font-medium text-white hover:bg-green-600 disabled:opacity-50"
|
|
||||||
onClick={handleSubmitOpinion}
|
|
||||||
disabled={isSubmittingOpinion}
|
|
||||||
>
|
|
||||||
{isSubmittingOpinion ? '提交中...' : '发起投票'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="space-y-4">
|
|
||||||
{/* 审查点 */}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="audit-point" className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
审查点
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="audit-point"
|
|
||||||
type="text"
|
|
||||||
value={opinionForm.auditPoint}
|
|
||||||
readOnly
|
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-sm focus:outline-none"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 发现问题 */}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="found-issue" className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
发现问题
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="found-issue"
|
|
||||||
type="text"
|
|
||||||
value={opinionForm.foundIssue}
|
|
||||||
readOnly
|
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-sm focus:outline-none"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 审查意见 */}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="audit-opinion" className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
审查意见 <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="audit-opinion"
|
|
||||||
value={opinionForm.auditOpinion}
|
|
||||||
onChange={(e) => handleOpinionFormChange('auditOpinion', e.target.value)}
|
|
||||||
placeholder="请输入审查意见..."
|
|
||||||
rows={4}
|
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-green-700 focus:border-green-700 focus:outline-none"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 扣分 */}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="deduction-score" className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
扣分 <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="deduction-score"
|
|
||||||
type="number"
|
|
||||||
value={opinionForm.deductionScore}
|
|
||||||
onChange={(e) => {
|
|
||||||
const value = parseFloat(e.target.value);
|
|
||||||
if (!isNaN(value) && value >= 0) {
|
|
||||||
// 限制到1位小数
|
|
||||||
const roundedValue = Math.round(value * 10) / 10;
|
|
||||||
handleOpinionFormChange('deductionScore', roundedValue);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
step="0.1"
|
|
||||||
min="0"
|
|
||||||
max="100"
|
|
||||||
placeholder="0.0"
|
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-green-700 focus:border-green-700 focus:outline-none"
|
|
||||||
/>
|
|
||||||
<p className="mt-1 text-xs text-gray-500">分数最多保留1位小数</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
|
||||||
|
{/* 提出意见模态框 */}
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpinionModalOpen}
|
||||||
|
onClose={handleCloseOpinionModal}
|
||||||
|
title="提出意见"
|
||||||
|
size="medium"
|
||||||
|
footer={
|
||||||
|
<div className="flex justify-end space-x-3">
|
||||||
|
<button
|
||||||
|
className="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50"
|
||||||
|
onClick={handleCloseOpinionModal}
|
||||||
|
disabled={isSubmittingOpinion}
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="px-4 py-2 bg-green-700 border border-transparent rounded-md text-sm font-medium text-white hover:bg-green-600 disabled:opacity-50"
|
||||||
|
onClick={handleSubmitOpinion}
|
||||||
|
disabled={isSubmittingOpinion}
|
||||||
|
>
|
||||||
|
{isSubmittingOpinion ? '提交中...' : '发起投票'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* 审查点 */}
|
||||||
|
<div>
|
||||||
|
<label htmlFor="audit-point" className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
审查点
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="audit-point"
|
||||||
|
type="text"
|
||||||
|
value={opinionForm.auditPoint}
|
||||||
|
readOnly
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-sm focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 发现问题 */}
|
||||||
|
<div>
|
||||||
|
<label htmlFor="found-issue" className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
发现问题
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="found-issue"
|
||||||
|
type="text"
|
||||||
|
value={opinionForm.foundIssue}
|
||||||
|
readOnly
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-sm focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 审查意见 */}
|
||||||
|
<div>
|
||||||
|
<label htmlFor="audit-opinion" className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
审查意见 <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="audit-opinion"
|
||||||
|
value={opinionForm.auditOpinion}
|
||||||
|
onChange={(e) => handleOpinionFormChange('auditOpinion', e.target.value)}
|
||||||
|
placeholder="请输入审查意见..."
|
||||||
|
rows={4}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-green-700 focus:border-green-700 focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 扣分 */}
|
||||||
|
<div>
|
||||||
|
<label htmlFor="deduction-score" className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
扣分 <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="deduction-score"
|
||||||
|
type="number"
|
||||||
|
value={opinionForm.deductionScore}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = parseFloat(e.target.value);
|
||||||
|
if (!isNaN(value) && value >= 0) {
|
||||||
|
// 限制到1位小数
|
||||||
|
const roundedValue = Math.round(value * 10) / 10;
|
||||||
|
handleOpinionFormChange('deductionScore', roundedValue);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
step="0.1"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
placeholder="0.0"
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-1 focus:ring-green-700 focus:border-green-700 focus:outline-none"
|
||||||
|
/>
|
||||||
|
<p className="mt-1 text-xs text-gray-500">分数最多保留1位小数</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -176,6 +176,11 @@ export default function CrossCheckingIndex() {
|
|||||||
return 'high';
|
return 'high';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理查看结果
|
||||||
|
// const handleViewResult = (taskId: number, docIds: number[]) => {
|
||||||
|
// // 根据taskId获取关联的
|
||||||
|
// };
|
||||||
|
|
||||||
// 渲染进度条
|
// 渲染进度条
|
||||||
const renderProgress = (progress: number) => (
|
const renderProgress = (progress: number) => (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
@@ -198,7 +203,7 @@ export default function CrossCheckingIndex() {
|
|||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
className="operation-btn primary"
|
className="operation-btn primary"
|
||||||
onClick={() => navigate(`/cross-checking/${task.id}`)}
|
onClick={() => handleViewResult(task.id, task.documentIds)}
|
||||||
>
|
>
|
||||||
<i className="ri-play-line"></i>
|
<i className="ri-play-line"></i>
|
||||||
去评查
|
去评查
|
||||||
@@ -210,7 +215,7 @@ export default function CrossCheckingIndex() {
|
|||||||
type="default"
|
type="default"
|
||||||
size="small"
|
size="small"
|
||||||
className="operation-btn secondary"
|
className="operation-btn secondary"
|
||||||
onClick={() => navigate(`/cross-checking/${task.id}`)}
|
onClick={() => handleViewResult(task.id, task.documentIds)}
|
||||||
>
|
>
|
||||||
<i className="ri-eye-line"></i>
|
<i className="ri-eye-line"></i>
|
||||||
进行中
|
进行中
|
||||||
@@ -222,7 +227,7 @@ export default function CrossCheckingIndex() {
|
|||||||
type="default"
|
type="default"
|
||||||
size="small"
|
size="small"
|
||||||
className="operation-btn secondary"
|
className="operation-btn secondary"
|
||||||
onClick={() => navigate(`/cross-checking/result?id=1355&previousRoute=cross-checking`)}
|
onClick={() => handleViewResult(task.id, task.documentIds)}
|
||||||
>
|
>
|
||||||
<i className="ri-file-text-line"></i>
|
<i className="ri-file-text-line"></i>
|
||||||
查看结果
|
查看结果
|
||||||
|
|||||||
Reference in New Issue
Block a user