完善文档预览的效果修改
This commit is contained in:
@@ -1,21 +1,6 @@
|
|||||||
import { postgrestGet, type PostgrestParams, postgrestPut } from "../postgrest-client";
|
import { postgrestGet, type PostgrestParams, postgrestPut, postgrestPost } from "../postgrest-client";
|
||||||
import {getDocument} from "~/api/files/documents";
|
import {getDocument} from "~/api/files/documents";
|
||||||
import dayjs from 'dayjs';
|
import { formatDate } from "~/utils";
|
||||||
|
|
||||||
/**
|
|
||||||
* 格式化日期
|
|
||||||
* @param dateString 日期字符串
|
|
||||||
* @returns 格式化后的日期字符串
|
|
||||||
*/
|
|
||||||
function formatDate(dateString: string): string {
|
|
||||||
if (!dateString) return '';
|
|
||||||
try {
|
|
||||||
return dayjs(dateString).format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('日期格式化失败:', error);
|
|
||||||
return dateString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从不同格式的 API 响应中提取数据
|
* 从不同格式的 API 响应中提取数据
|
||||||
@@ -62,6 +47,15 @@ interface EvaluationPoint {
|
|||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 定义审核状态类型
|
||||||
|
interface AuditStatus {
|
||||||
|
id: string | number;
|
||||||
|
document_id: string | number;
|
||||||
|
evaluation_point_id: string | number;
|
||||||
|
edit_audit_status: number;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
// 定义评查点组类型
|
// 定义评查点组类型
|
||||||
interface EvaluationPointGroup {
|
interface EvaluationPointGroup {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
@@ -185,6 +179,47 @@ export async function getReviewPoints(fileId: string) {
|
|||||||
return { data: [], stats: { total: 0, success: 0, warning: 0, error: 0, score: 0 } };
|
return { data: [], stats: { total: 0, success: 0, warning: 0, error: 0, score: 0 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从audit_status表中 获取 需人工审核 的那些评查点的数据
|
||||||
|
// console.log('evaluationPointsData1112------', evaluationPointsData.find(point => point.post_action === 'manual'));
|
||||||
|
const manualReviewPoints = evaluationPointsData.filter(point => point.post_action === 'manual');
|
||||||
|
const manualReviewPointsIds = manualReviewPoints.map(point => point.id);
|
||||||
|
const manualReviewPointsParams: PostgrestParams = {
|
||||||
|
select: '*',
|
||||||
|
filter: {
|
||||||
|
'document_id': `eq.${fileId}`,
|
||||||
|
'evaluation_point_id': `in.(${manualReviewPointsIds.join(',')})`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const manualReviewPointsResponse = await postgrestGet('audit_status', manualReviewPointsParams);
|
||||||
|
if (manualReviewPointsResponse.error) {
|
||||||
|
return { error: manualReviewPointsResponse.error, status: manualReviewPointsResponse.status };
|
||||||
|
}
|
||||||
|
const manualReviewPointsData = extractApiData<AuditStatus[]>(manualReviewPointsResponse.data);
|
||||||
|
|
||||||
|
// 构建评查点ID到editAuditStatus的映射
|
||||||
|
const editAuditStatusMap = new Map<string | number, {id: string | number, status: number}>();
|
||||||
|
|
||||||
|
// 如果有查询结果,则根据evaluation_point_id索引到对应数据
|
||||||
|
if (manualReviewPointsData && Array.isArray(manualReviewPointsData)) {
|
||||||
|
manualReviewPointsData.forEach(auditStatus => {
|
||||||
|
if (auditStatus.evaluation_point_id && auditStatus.edit_audit_status !== undefined) {
|
||||||
|
editAuditStatusMap.set(auditStatus.evaluation_point_id, {id: auditStatus.id, status: auditStatus.edit_audit_status});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为没有对应audit_status记录的manual类型评查点设置默认值0
|
||||||
|
if (manualReviewPointsIds.length > 0) {
|
||||||
|
manualReviewPointsIds.forEach(pointId => {
|
||||||
|
if (!editAuditStatusMap.has(pointId)) {
|
||||||
|
editAuditStatusMap.set(pointId, {id: '', status: 0});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('manualReviewPoints-------', manualReviewPoints);
|
||||||
|
|
||||||
|
|
||||||
// 创建映射关系以便快速查找
|
// 创建映射关系以便快速查找
|
||||||
const pointsMap = new Map<string | number, EvaluationPoint>();
|
const pointsMap = new Map<string | number, EvaluationPoint>();
|
||||||
evaluationPointsData.forEach(point => {
|
evaluationPointsData.forEach(point => {
|
||||||
@@ -204,6 +239,7 @@ export async function getReviewPoints(fileId: string) {
|
|||||||
const resultData: ReviewPointResult[] = evaluationResultsData.map(result => {
|
const resultData: ReviewPointResult[] = evaluationResultsData.map(result => {
|
||||||
const point = pointsMap.get(result.evaluation_point_id) || {} as EvaluationPoint;
|
const point = pointsMap.get(result.evaluation_point_id) || {} as EvaluationPoint;
|
||||||
const group = groupsMap.get(point.evaluation_point_groups_id || 0) || {} as EvaluationPointGroup;
|
const group = groupsMap.get(point.evaluation_point_groups_id || 0) || {} as EvaluationPointGroup;
|
||||||
|
const editAuditStatus = editAuditStatusMap.get(result.evaluation_point_id) || {id: '', status: 0};
|
||||||
|
|
||||||
// 从 evaluated_results 中提取数据
|
// 从 evaluated_results 中提取数据
|
||||||
let message = '';
|
let message = '';
|
||||||
@@ -268,6 +304,10 @@ export async function getReviewPoints(fileId: string) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: result.id,
|
id: result.id,
|
||||||
|
documentId: fileId,
|
||||||
|
pointId: point.id,
|
||||||
|
editAuditStatusId: editAuditStatus.id,
|
||||||
|
editAuditStatus: editAuditStatus.status,
|
||||||
title: message,
|
title: message,
|
||||||
pointName: point.name || '',
|
pointName: point.name || '',
|
||||||
groupName: group.name || '',
|
groupName: group.name || '',
|
||||||
@@ -356,18 +396,19 @@ export async function getReviewPoints(fileId: string) {
|
|||||||
issueCount: issueCount
|
issueCount: issueCount
|
||||||
};
|
};
|
||||||
// console.log("reviewInfo-------",JSON.stringify(reviewInfo,null,2));
|
// console.log("reviewInfo-------",JSON.stringify(reviewInfo,null,2));
|
||||||
|
// data->reviewPoints stats->statistics reviewInfo->reviewInfo document->document
|
||||||
return { data: resultData, stats, reviewInfo, document: documentData.data };
|
return { data: resultData, stats, reviewInfo, document: documentData.data };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新评查结果
|
* 更新评查结果
|
||||||
* @param resultId 评查结果ID
|
* @param resultId 评查结果ID
|
||||||
|
* @param editAuditStatusId 审核状态ID
|
||||||
* @param result 评查结果 (true/false)
|
* @param result 评查结果 (true/false)
|
||||||
* @param message 评查意见
|
* @param message 评查意见
|
||||||
* @returns 更新后的评查结果
|
* @returns 更新后的评查结果
|
||||||
*/
|
*/
|
||||||
export async function updateReviewResult(resultId: string, result: boolean, message: string): Promise<{
|
export async function updateReviewResult(resultId: string, editAuditStatusId: string | number, result: string, message: string): Promise<{
|
||||||
data?: unknown;
|
data?: unknown;
|
||||||
error?: string;
|
error?: string;
|
||||||
status?: number;
|
status?: number;
|
||||||
@@ -396,29 +437,72 @@ export async function updateReviewResult(resultId: string, result: boolean, mess
|
|||||||
const currentResult = currentResultData[0];
|
const currentResult = currentResultData[0];
|
||||||
const currentEvaluatedResults = currentResult.evaluated_results || {};
|
const currentEvaluatedResults = currentResult.evaluated_results || {};
|
||||||
|
|
||||||
// 构建要更新的数据,保留原有字段,只更新result和message
|
// 判断是否是重新审核操作
|
||||||
|
const isReview = result === 'review';
|
||||||
|
console.log('isReview-------', result);
|
||||||
|
|
||||||
|
// 构建要更新的数据,保留原有字段
|
||||||
const updatedEvaluatedResults = {
|
const updatedEvaluatedResults = {
|
||||||
...currentEvaluatedResults,
|
...currentEvaluatedResults,
|
||||||
result: result,
|
// 如果是重新审核操作,不更新result,只更新message
|
||||||
message: message
|
...(isReview ? { message } : { result: result === 'true' ? true : false, message }),
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatedData = {
|
const updatedData = {
|
||||||
evaluated_results: updatedEvaluatedResults
|
evaluated_results: updatedEvaluatedResults
|
||||||
};
|
};
|
||||||
|
|
||||||
// 调用 API 更新数据
|
// 调用 API 更新评查点结果数据
|
||||||
const response = await postgrestPut<unknown, typeof updatedData>(
|
const resultResponse = await postgrestPut<unknown, typeof updatedData>(
|
||||||
'evaluation_results',
|
'evaluation_results',
|
||||||
updatedData,
|
updatedData,
|
||||||
{ id: resultId }
|
{ id: resultId }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.error) {
|
if (resultResponse.error) {
|
||||||
return { error: response.error, status: response.status };
|
return { error: resultResponse.error, status: resultResponse.status };
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractedData = extractApiData<unknown>(response.data);
|
// 处理audit_status表的更新或新增
|
||||||
|
// 确定edit_audit_status的值:
|
||||||
|
// 如果是重新审核操作,值为0;否则值为1
|
||||||
|
const editAuditStatusValue = isReview ? 0 : 1;
|
||||||
|
console.log('editAuditStatusValue-------', editAuditStatusValue);
|
||||||
|
console.log('editAuditStatusId-------', editAuditStatusId);
|
||||||
|
if (editAuditStatusId && editAuditStatusId !== '') {
|
||||||
|
// 更新现有审核状态记录
|
||||||
|
const auditStatusResponse = await postgrestPut(
|
||||||
|
'audit_status',
|
||||||
|
{ edit_audit_status: editAuditStatusValue },
|
||||||
|
{ id: editAuditStatusId }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (auditStatusResponse.error) {
|
||||||
|
return { error: auditStatusResponse.error, status: auditStatusResponse.status || 500 };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没有editAuditStatusId,则创建新记录
|
||||||
|
// 首先获取文档ID和评查点ID
|
||||||
|
const documentId = currentResult.document_id;
|
||||||
|
const evaluationPointId = currentResult.evaluation_point_id;
|
||||||
|
|
||||||
|
// 创建新的审核状态记录
|
||||||
|
const newAuditStatus = {
|
||||||
|
document_id: documentId,
|
||||||
|
evaluation_point_id: evaluationPointId,
|
||||||
|
evaluation_result_id: resultId,
|
||||||
|
edit_audit_status: editAuditStatusValue
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用postgrestPost创建新记录
|
||||||
|
const postResponse = await postgrestPost('audit_status', newAuditStatus);
|
||||||
|
|
||||||
|
if (postResponse.error) {
|
||||||
|
return { error: postResponse.error, status: postResponse.status || 500 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const extractedData = extractApiData<unknown>(resultResponse.data);
|
||||||
|
|
||||||
if (!extractedData) {
|
if (!extractedData) {
|
||||||
return { error: '更新评查结果失败', status: 500 };
|
return { error: '更新评查结果失败', status: 500 };
|
||||||
@@ -435,12 +519,12 @@ export async function updateReviewResult(resultId: string, result: boolean, mess
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 确认评查结果并更新文档审核状态
|
* 确认评查结果并更新文档审核状态 只更新文档的审核状态为通过
|
||||||
* @param documentId 文档ID
|
* @param documentId 文档ID
|
||||||
* @returns 更新结果
|
* @returns 更新结果
|
||||||
*/
|
*/
|
||||||
export async function confirmReviewResults(documentId: string): Promise<{
|
export async function confirmReviewResults(documentId: string): Promise<{
|
||||||
data?: { auditStatus: number; score: number };
|
data?: { auditStatus: number; };
|
||||||
error?: string;
|
error?: string;
|
||||||
status?: number;
|
status?: number;
|
||||||
}> {
|
}> {
|
||||||
@@ -450,26 +534,26 @@ export async function confirmReviewResults(documentId: string): Promise<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取该文档的所有评查点结果
|
// 获取该文档的所有评查点结果
|
||||||
const reviewPointsResponse = await getReviewPoints(documentId);
|
// const reviewPointsResponse = await getReviewPoints(documentId);
|
||||||
|
|
||||||
if ('error' in reviewPointsResponse && reviewPointsResponse.error) {
|
// if ('error' in reviewPointsResponse && reviewPointsResponse.error) {
|
||||||
return { error: reviewPointsResponse.error, status: reviewPointsResponse.status };
|
// return { error: reviewPointsResponse.error, status: reviewPointsResponse.status };
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (!('data' in reviewPointsResponse) || !reviewPointsResponse.data || !Array.isArray(reviewPointsResponse.data)) {
|
// if (!('data' in reviewPointsResponse) || !reviewPointsResponse.data || !Array.isArray(reviewPointsResponse.data)) {
|
||||||
return { error: '获取评查点数据失败', status: 500 };
|
// return { error: '获取评查点数据失败', status: 500 };
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 计算总分数
|
// // 计算总分数
|
||||||
const totalScore = reviewPointsResponse.stats?.score || 0;
|
// const totalScore = reviewPointsResponse.stats?.score || 0;
|
||||||
|
|
||||||
// 根据总分确定审核状态
|
// // 根据总分确定审核状态
|
||||||
// <80分:不通过(-1),>=80分:通过(1)
|
// // <80分:不通过(-1),>=80分:通过(1)
|
||||||
const auditStatus = totalScore < 80 ? -1 : 1;
|
// const auditStatus = totalScore < 80 ? -1 : 1;
|
||||||
|
|
||||||
// 更新文档的审核状态
|
// 更新文档的审核状态
|
||||||
const updateDocumentParams = {
|
const updateDocumentParams = {
|
||||||
audit_status: auditStatus
|
audit_status: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
// 调用API更新文档审核状态
|
// 调用API更新文档审核状态
|
||||||
@@ -483,7 +567,7 @@ export async function confirmReviewResults(documentId: string): Promise<{
|
|||||||
return { error: response.error, status: response.status };
|
return { error: response.error, status: response.status };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { data: { auditStatus, score: totalScore } };
|
return { data: { auditStatus: 1} };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('确认评查结果失败:', error);
|
console.error('确认评查结果失败:', error);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ export interface ReviewFileUI {
|
|||||||
message: string;
|
message: string;
|
||||||
}>;
|
}>;
|
||||||
createdBy: string;
|
createdBy: string;
|
||||||
|
passCount: number;
|
||||||
|
warningCount: number;
|
||||||
|
failCount: number;
|
||||||
|
manualCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文件列表搜索参数
|
// 文件列表搜索参数
|
||||||
@@ -109,6 +113,10 @@ interface EvaluationPoint {
|
|||||||
interface DocumentReviewResult {
|
interface DocumentReviewResult {
|
||||||
status: number;
|
status: number;
|
||||||
issueCount: number;
|
issueCount: number;
|
||||||
|
passCount: number;
|
||||||
|
warningCount: number;
|
||||||
|
failCount: number;
|
||||||
|
manualCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,6 +185,7 @@ export function getFileExtension(fileName: string): string {
|
|||||||
* @returns UI文件对象
|
* @returns UI文件对象
|
||||||
*/
|
*/
|
||||||
export function convertToReviewFileUI(document: Document, documentTypeName: string): ReviewFileUI {
|
export function convertToReviewFileUI(document: Document, documentTypeName: string): ReviewFileUI {
|
||||||
|
// 将评查状态转换为UI状态(这个评查状态后续可能不需要,这里先预留)
|
||||||
const reviewStatus = mapReviewStatusToUI(document.evaluations_status);
|
const reviewStatus = mapReviewStatusToUI(document.evaluations_status);
|
||||||
|
|
||||||
const reviewFileUI: ReviewFileUI = {
|
const reviewFileUI: ReviewFileUI = {
|
||||||
@@ -195,7 +204,11 @@ export function convertToReviewFileUI(document: Document, documentTypeName: stri
|
|||||||
score: 0,
|
score: 0,
|
||||||
auditStatus: document.audit_status,
|
auditStatus: document.audit_status,
|
||||||
issues: [],
|
issues: [],
|
||||||
createdBy: document.user_id?.toString() || '系统'
|
createdBy: document.user_id?.toString() || '系统',
|
||||||
|
passCount: 0,
|
||||||
|
warningCount: 0,
|
||||||
|
failCount: 0,
|
||||||
|
manualCount: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// console.log('reviewFileUI-----',reviewFileUI);
|
// console.log('reviewFileUI-----',reviewFileUI);
|
||||||
@@ -396,6 +409,9 @@ export async function getReviewFiles(searchParams: DocumentSearchParams = {}): P
|
|||||||
let totalScore = 0;
|
let totalScore = 0;
|
||||||
let totalPoints = 0;
|
let totalPoints = 0;
|
||||||
let totalPassPoints = 0;
|
let totalPassPoints = 0;
|
||||||
|
let totalWarningPoints = 0;
|
||||||
|
let totalFailPoints = 0;
|
||||||
|
let totalManualPoints = 0;
|
||||||
|
|
||||||
// 存储该文档的问题消息
|
// 存储该文档的问题消息
|
||||||
const issuesList: Array<{severity: 'info' | 'warning' | 'error' | 'critical', message: string}> = [];
|
const issuesList: Array<{severity: 'info' | 'warning' | 'error' | 'critical', message: string}> = [];
|
||||||
@@ -406,9 +422,10 @@ export async function getReviewFiles(searchParams: DocumentSearchParams = {}): P
|
|||||||
const pointId = result.evaluation_point_id;
|
const pointId = result.evaluation_point_id;
|
||||||
const point = pointsMap.get(pointId);
|
const point = pointsMap.get(pointId);
|
||||||
|
|
||||||
// 检查是否需要人工审核
|
// 统计需要人工审核的评查点
|
||||||
if (resultValue === false && point && point.post_action === 'manual') {
|
if (point && point.post_action === 'manual') {
|
||||||
hasManualReviewPoint = true;
|
hasManualReviewPoint = true;
|
||||||
|
totalManualPoints++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有不通过的结果
|
// 检查是否有不通过的结果
|
||||||
@@ -422,6 +439,14 @@ export async function getReviewFiles(searchParams: DocumentSearchParams = {}): P
|
|||||||
message: evaluatedResults.message as string
|
message: evaluatedResults.message as string
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 统计不通过而且评查点是警告的评查点
|
||||||
|
if (point && point.suggestion_message_type === 'warning') {
|
||||||
|
totalWarningPoints++;
|
||||||
|
}else if (point && point.suggestion_message_type === 'error') {
|
||||||
|
totalFailPoints++;
|
||||||
|
}
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
totalPassPoints++;
|
totalPassPoints++;
|
||||||
}
|
}
|
||||||
@@ -464,14 +489,20 @@ export async function getReviewFiles(searchParams: DocumentSearchParams = {}): P
|
|||||||
|
|
||||||
documentStatusMap.set(docId, {
|
documentStatusMap.set(docId, {
|
||||||
status,
|
status,
|
||||||
|
passCount: totalPassPoints,
|
||||||
|
warningCount: totalWarningPoints,
|
||||||
|
failCount: totalFailPoints,
|
||||||
|
manualCount: totalManualPoints,
|
||||||
issueCount: results.filter(r => r.evaluated_results?.result === false).length
|
issueCount: results.filter(r => r.evaluated_results?.result === false).length
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// console.log("documentStatusMap-----",documentStatusMap);
|
||||||
|
|
||||||
// 将文档数据转换为UI文件对象,同时应用评查状态
|
// 将文档数据转换为UI文件对象,同时应用评查状态
|
||||||
const reviewFiles = extractedDocuments.map(doc => {
|
const reviewFiles = extractedDocuments.map(doc => {
|
||||||
const typeName = typeNameMap[doc.type_id] || '未知类型';
|
const typeName = typeNameMap[doc.type_id] || '未知类型';
|
||||||
const reviewResult = documentStatusMap.get(doc.id) || { status: doc.evaluations_status || 0, issueCount: 0 };
|
const reviewResult = documentStatusMap.get(doc.id) || { status: doc.evaluations_status || 0, issueCount: 0, passCount: 0, warningCount: 0, failCount: 0, manualCount: 0 };
|
||||||
const issues = documentIssuesMap.get(doc.id) || [];
|
const issues = documentIssuesMap.get(doc.id) || [];
|
||||||
const score = documentScoreMap.get(doc.id) || 100; // 获取计算后的分数,默认为100
|
const score = documentScoreMap.get(doc.id) || 100; // 获取计算后的分数,默认为100
|
||||||
|
|
||||||
@@ -490,14 +521,20 @@ export async function getReviewFiles(searchParams: DocumentSearchParams = {}): P
|
|||||||
reviewFile.reviewStatusCode = reviewResult.status;
|
reviewFile.reviewStatusCode = reviewResult.status;
|
||||||
reviewFile.reviewStatus = mapReviewStatusToUI(reviewResult.status);
|
reviewFile.reviewStatus = mapReviewStatusToUI(reviewResult.status);
|
||||||
reviewFile.issueCount = reviewResult.issueCount;
|
reviewFile.issueCount = reviewResult.issueCount;
|
||||||
|
|
||||||
|
reviewFile.passCount = reviewResult.passCount;
|
||||||
|
reviewFile.warningCount = reviewResult.warningCount;
|
||||||
|
reviewFile.failCount = reviewResult.failCount;
|
||||||
|
reviewFile.manualCount = reviewResult.manualCount;
|
||||||
|
|
||||||
reviewFile.score = score; // 添加分数
|
reviewFile.score = score; // 添加分数
|
||||||
// 添加问题列表
|
// 添加问题列表
|
||||||
reviewFile.issues = issues;
|
reviewFile.issues = issues;
|
||||||
|
|
||||||
return reviewFile;
|
return reviewFile;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('reviewFiles-----',reviewFiles);
|
// console.log('reviewFiles-----',reviewFiles);
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
files: reviewFiles,
|
files: reviewFiles,
|
||||||
@@ -519,51 +556,51 @@ export async function getReviewFiles(searchParams: DocumentSearchParams = {}): P
|
|||||||
* @param status 评查状态
|
* @param status 评查状态
|
||||||
* @returns 更新后的文件信息
|
* @returns 更新后的文件信息
|
||||||
*/
|
*/
|
||||||
export async function updateReviewStatus(id: string, status: string): Promise<{
|
// export async function updateReviewStatus(id: string, status: string): Promise<{
|
||||||
data?: ReviewFileUI;
|
// data?: ReviewFileUI;
|
||||||
error?: string;
|
// error?: string;
|
||||||
status?: number;
|
// status?: number;
|
||||||
}> {
|
// }> {
|
||||||
try {
|
// try {
|
||||||
if (!id) {
|
// if (!id) {
|
||||||
return { error: '文件ID不能为空', status: 400 };
|
// return { error: '文件ID不能为空', status: 400 };
|
||||||
}
|
// }
|
||||||
|
|
||||||
const statusValue = mapUIToReviewStatus(status);
|
// const statusValue = mapUIToReviewStatus(status);
|
||||||
|
|
||||||
const response = await postgrestPut<Document, Partial<Document>>(
|
// const response = await postgrestPut<Document, Partial<Document>>(
|
||||||
'documents',
|
// 'documents',
|
||||||
{ evaluations_status: statusValue },
|
// { evaluations_status: statusValue },
|
||||||
{ id: parseInt(id) }
|
// { id: parseInt(id) }
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (response.error) {
|
// if (response.error) {
|
||||||
return { error: response.error, status: response.status };
|
// return { error: response.error, status: response.status };
|
||||||
}
|
// }
|
||||||
|
|
||||||
const extractedData = extractApiData<Document>(response.data);
|
// const extractedData = extractApiData<Document>(response.data);
|
||||||
|
|
||||||
if (!extractedData) {
|
// if (!extractedData) {
|
||||||
return { error: '更新评查状态失败', status: 500 };
|
// return { error: '更新评查状态失败', status: 500 };
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 获取文档类型,用于查找文档类型名称
|
// // 获取文档类型,用于查找文档类型名称
|
||||||
const documentTypesResponse = await getDocumentTypes({pageSize: 500});
|
// const documentTypesResponse = await getDocumentTypes({pageSize: 500});
|
||||||
const documentTypes = documentTypesResponse.data?.types || [];
|
// const documentTypes = documentTypesResponse.data?.types || [];
|
||||||
|
|
||||||
// 查找文档类型名称
|
// // 查找文档类型名称
|
||||||
const docType = documentTypes.find((type: DocumentTypeUI) => type.id === extractedData.type_id);
|
// const docType = documentTypes.find((type: DocumentTypeUI) => type.id === extractedData.type_id);
|
||||||
const typeName = docType ? docType.name : '未知类型';
|
// const typeName = docType ? docType.name : '未知类型';
|
||||||
|
|
||||||
return { data: convertToReviewFileUI(extractedData, typeName) };
|
// return { data: convertToReviewFileUI(extractedData, typeName) };
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.error('更新评查状态失败:', error);
|
// console.error('更新评查状态失败:', error);
|
||||||
return {
|
// return {
|
||||||
error: error instanceof Error ? error.message : '更新评查状态失败',
|
// error: error instanceof Error ? error.message : '更新评查状态失败',
|
||||||
status: 500
|
// status: 500
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新文件的审核状态
|
* 更新文件的审核状态
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { postgrestGet, postgrestDelete, postgrestPut, type PostgrestParams } from '../postgrest-client';
|
import { postgrestGet, postgrestDelete, postgrestPut, type PostgrestParams } from '../postgrest-client';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { getDocumentTypes } from '../document-types/document-types';
|
import { getDocumentTypes } from '../document-types/document-types';
|
||||||
import { formatDate } from '../../utils';
|
import { formatDate } from '../../utils';
|
||||||
|
|
||||||
@@ -433,9 +432,9 @@ export async function updateDocument(id: string, document: Partial<DocumentUI> &
|
|||||||
apiDocument.document_number = document.documentNumber;
|
apiDocument.document_number = document.documentNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.type !== undefined) {
|
// if (document.type !== undefined) {
|
||||||
apiDocument.type_id = parseInt(document.type);
|
// apiDocument.type_id = parseInt(document.type);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (document.auditStatus !== undefined) {
|
if (document.auditStatus !== undefined) {
|
||||||
apiDocument.audit_status = document.auditStatus;
|
apiDocument.audit_status = document.auditStatus;
|
||||||
|
|||||||
+3
-114
@@ -42,123 +42,12 @@ function logPostgrestQuery(endpoint: string, params?: QueryParams, method: strin
|
|||||||
// 以可读格式单独打印每个参数
|
// 以可读格式单独打印每个参数
|
||||||
Object.entries(params).forEach(([key, value]) => {
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
if (key === 'select' && typeof value === 'string') {
|
console.log(` - ${key}: ${JSON.stringify(value)}`);
|
||||||
// 美化 select 参数,使其看起来像 SQL 查询
|
|
||||||
console.log(` - ${key}:`);
|
|
||||||
const fields = value.replace(/\s+/g, ' ').trim().split(',');
|
|
||||||
fields.forEach(field => {
|
|
||||||
console.log(` ${field.trim()}`);
|
|
||||||
});
|
|
||||||
} else if (key === 'order' && typeof value === 'string') {
|
|
||||||
// 格式化排序参数
|
|
||||||
console.log(` - ${key}: ${value.replace(/\./g, ' ')}`); // 例如:created_at desc
|
|
||||||
} else if (key === 'or' && typeof value === 'string') {
|
|
||||||
// 格式化OR条件
|
|
||||||
console.log(` - ${key}: ${value.replace(/\./g, ' -> ').replace(/,/g, ' 或 ')}`);
|
|
||||||
} else {
|
|
||||||
console.log(` - ${key}: ${JSON.stringify(value)}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 构建人类可读的简化URL
|
|
||||||
const readableQueryString = Object.entries(params)
|
|
||||||
.filter(([, value]) => value !== undefined)
|
|
||||||
.map(([key, value]) => {
|
|
||||||
if (key === 'select' && typeof value === 'string') {
|
|
||||||
// 简化select查询
|
|
||||||
return `${key}=...`;
|
|
||||||
}
|
|
||||||
return `${key}=${value}`;
|
|
||||||
})
|
|
||||||
.join('&');
|
|
||||||
|
|
||||||
console.log(`\n📦 可读URL: ${baseUrl}/${normalizedEndpoint}${readableQueryString ? '?' + readableQueryString : ''}`);
|
|
||||||
|
|
||||||
// 格式化查询为 PostgreSQL 风格的查询
|
|
||||||
let postgrestQuery = `${method.toUpperCase()} `;
|
|
||||||
|
|
||||||
if (params.select && typeof params.select === 'string') {
|
|
||||||
postgrestQuery += params.select.replace(/\s+/g, ' ').trim();
|
|
||||||
} else {
|
|
||||||
postgrestQuery += '*';
|
|
||||||
}
|
|
||||||
|
|
||||||
postgrestQuery += ` FROM ${normalizedEndpoint}`;
|
|
||||||
|
|
||||||
const conditions: string[] = [];
|
|
||||||
|
|
||||||
// 添加过滤条件
|
|
||||||
if (params.filter) {
|
|
||||||
Object.entries(params.filter).forEach(([key, value]) => {
|
|
||||||
if (value !== undefined && typeof value === 'string') {
|
|
||||||
// 解析 eq.X, neq.X, gt.X 等
|
|
||||||
const parts = value.toString().split('.');
|
|
||||||
if (parts.length >= 2) {
|
|
||||||
const operator = parts[0];
|
|
||||||
const operatorMap: Record<string, string> = {
|
|
||||||
'eq': '=',
|
|
||||||
'neq': '!=',
|
|
||||||
'gt': '>',
|
|
||||||
'gte': '>=',
|
|
||||||
'lt': '<',
|
|
||||||
'lte': '<=',
|
|
||||||
'like': 'LIKE',
|
|
||||||
'ilike': 'ILIKE'
|
|
||||||
};
|
|
||||||
|
|
||||||
const sqlOperator = operatorMap[operator] || operator;
|
|
||||||
const value = parts.slice(1).join('.');
|
|
||||||
conditions.push(`${key} ${sqlOperator} '${value}'`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加 OR 条件
|
|
||||||
if (params.or) {
|
|
||||||
if (typeof params.or === 'string') {
|
|
||||||
if (params.or.startsWith('(') && params.or.endsWith(')')) {
|
|
||||||
// 处理 or=(cond1,cond2) 格式
|
|
||||||
const orConditions = params.or.slice(1, -1).split(',').map(cond => {
|
|
||||||
const [field, operator] = cond.split('.');
|
|
||||||
return `${field} ${operator.replace(/ilike/i, 'ILIKE')}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (orConditions.length > 0) {
|
|
||||||
conditions.push(`(${orConditions.join(' OR ')})`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 简单 or 条件
|
|
||||||
conditions.push(params.or);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加WHERE子句
|
|
||||||
if (conditions.length > 0) {
|
|
||||||
postgrestQuery += ` WHERE ${conditions.join(' AND ')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加ORDER BY
|
|
||||||
if (params.order && typeof params.order === 'string') {
|
|
||||||
const [field, direction] = params.order.split('.');
|
|
||||||
postgrestQuery += ` ORDER BY ${field} ${direction.toUpperCase()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加LIMIT和OFFSET
|
|
||||||
if (params.limit !== undefined) {
|
|
||||||
postgrestQuery += ` LIMIT ${params.limit}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.offset !== undefined) {
|
|
||||||
postgrestQuery += ` OFFSET ${params.offset}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('\n📦 等效SQL查询:');
|
|
||||||
console.log(postgrestQuery);
|
|
||||||
console.log('=========================================\n');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('=========================================\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
|
import { useNavigate } from "@remix-run/react";
|
||||||
interface FileInfoProps {
|
interface FileInfoProps {
|
||||||
fileInfo: {
|
fileInfo: {
|
||||||
fileName: string;
|
fileName: string;
|
||||||
@@ -15,6 +15,7 @@ interface FileInfoProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function FileInfo({ fileInfo, onConfirmResults }: FileInfoProps) {
|
export function FileInfo({ fileInfo, onConfirmResults }: FileInfoProps) {
|
||||||
|
const navigate = useNavigate();
|
||||||
const handleDownloadFile = async () => {
|
const handleDownloadFile = async () => {
|
||||||
try {
|
try {
|
||||||
const urlBefore = 'http://172.18.0.100:9000/docauditai/';
|
const urlBefore = 'http://172.18.0.100:9000/docauditai/';
|
||||||
@@ -79,20 +80,27 @@ export function FileInfo({ fileInfo, onConfirmResults }: FileInfoProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-3">
|
<div className="flex space-x-3">
|
||||||
|
{/* 返回上一级 */}
|
||||||
|
<button
|
||||||
|
className="ant-btn ant-btn-default flex items-center"
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
>
|
||||||
|
<i className="ri-arrow-left-line mr-1"></i> 返回
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className="ant-btn ant-btn-default flex items-center"
|
className="ant-btn ant-btn-default flex items-center"
|
||||||
onClick={handleDownloadFile}
|
onClick={handleDownloadFile}
|
||||||
>
|
>
|
||||||
<i className="ri-file-download-line mr-1"></i> 下载原文件
|
<i className="ri-file-download-line mr-1"></i> 下载原文件
|
||||||
</button>
|
</button>
|
||||||
<button
|
{/* <button
|
||||||
className="ant-btn ant-btn-default flex items-center"
|
className="ant-btn ant-btn-default flex items-center"
|
||||||
onClick={handleExportReport}
|
onClick={handleExportReport}
|
||||||
>
|
>
|
||||||
<i className="ri-file-copy-line mr-1"></i> 导出评查报告
|
<i className="ri-file-copy-line mr-1"></i> 导出评查报告
|
||||||
</button>
|
</button> */}
|
||||||
<button
|
<button
|
||||||
className="ant-btn ant-btn-primary flex items-center"
|
className={`ant-btn ant-btn-primary flex items-center ${fileInfo.auditStatus === 1 ? 'hidden' : ''}`}
|
||||||
onClick={onConfirmResults}
|
onClick={onConfirmResults}
|
||||||
>
|
>
|
||||||
<i className="ri-check-double-line mr-1"></i> 确认评查结果
|
<i className="ri-check-double-line mr-1"></i> 确认评查结果
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* 文件预览组件
|
* 文件预览组件
|
||||||
* 显示文档内容和评查点高亮
|
* 显示文档内容和评查点高亮
|
||||||
*/
|
*/
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef, ChangeEvent } from 'react';
|
||||||
import { Document, Page, pdfjs } from 'react-pdf';
|
import { Document, Page, pdfjs } from 'react-pdf';
|
||||||
|
|
||||||
// 设置worker路径为public目录下的worker文件
|
// 设置worker路径为public目录下的worker文件
|
||||||
@@ -11,6 +11,7 @@ pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js';
|
|||||||
|
|
||||||
// 导入统一的ReviewPoint类型
|
// 导入统一的ReviewPoint类型
|
||||||
import { type ReviewPoint } from './';
|
import { type ReviewPoint } from './';
|
||||||
|
import { toastService } from '../ui/Toast';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义样式
|
* 自定义样式
|
||||||
@@ -72,10 +73,11 @@ interface FilePreviewProps {
|
|||||||
|
|
||||||
export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, targetPage }: FilePreviewProps) {
|
export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, targetPage }: FilePreviewProps) {
|
||||||
const [zoomLevel, setZoomLevel] = useState(100);
|
const [zoomLevel, setZoomLevel] = useState(100);
|
||||||
const [highlightsVisible, setHighlightsVisible] = useState(true);
|
// const [highlightsVisible, setHighlightsVisible] = useState(true);
|
||||||
const contentRef = useRef<HTMLDivElement>(null);
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
const [numPages, setNumPages] = useState<number | null>(null);
|
const [numPages, setNumPages] = useState<number | null>(null);
|
||||||
const [loadError, setLoadError] = useState<string | null>(null);
|
const [loadError, setLoadError] = useState<string | null>(null);
|
||||||
|
const [pageInputValue, setPageInputValue] = useState<string>('');
|
||||||
|
|
||||||
// 放大文档
|
// 放大文档
|
||||||
const handleZoomIn = () => {
|
const handleZoomIn = () => {
|
||||||
@@ -92,9 +94,9 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 切换高亮显示
|
// 切换高亮显示
|
||||||
const toggleHighlights = () => {
|
// const toggleHighlights = () => {
|
||||||
setHighlightsVisible(!highlightsVisible);
|
// setHighlightsVisible(!highlightsVisible);
|
||||||
};
|
// };
|
||||||
|
|
||||||
// 当选中的评查点变化时,滚动到对应位置
|
// 当选中的评查点变化时,滚动到对应位置
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
@@ -132,7 +134,7 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
|||||||
|
|
||||||
const pageElement = document.getElementById(`page-${newTargetPage}`);
|
const pageElement = document.getElementById(`page-${newTargetPage}`);
|
||||||
if (pageElement) {
|
if (pageElement) {
|
||||||
console.log(`跳转到第${newTargetPage}页,对应评查点ID: ${activeReviewPointId}`);
|
console.log(`跳转到第${newTargetPage}页,对应评查点结果ID: ${activeReviewPointId}`);
|
||||||
pageElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
pageElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,6 +154,40 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理页码输入变化
|
||||||
|
const handlePageInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
// 只允许输入数字
|
||||||
|
const value = e.target.value.replace(/\D/g, '');
|
||||||
|
setPageInputValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理页码跳转
|
||||||
|
const handlePageJump = () => {
|
||||||
|
if (!pageInputValue || !numPages) return;
|
||||||
|
|
||||||
|
const targetPageNum = parseInt(pageInputValue, 10);
|
||||||
|
|
||||||
|
// 验证页码是否在有效范围内
|
||||||
|
if (targetPageNum > 0 && targetPageNum <= numPages) {
|
||||||
|
// 找到目标页面元素并滚动到该位置
|
||||||
|
const pageElement = document.getElementById(`page-${targetPageNum}`);
|
||||||
|
if (pageElement) {
|
||||||
|
pageElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 页码超出范围,显示错误信息或重置输入
|
||||||
|
toastService.warning(`请输入有效页码 (1-${numPages})`);
|
||||||
|
setPageInputValue('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理回车键跳转
|
||||||
|
const handlePageInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
handlePageJump();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// PDF文档加载成功回调函数
|
// PDF文档加载成功回调函数
|
||||||
function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
|
function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
|
||||||
setNumPages(numPages);
|
setNumPages(numPages);
|
||||||
@@ -166,6 +202,13 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
|||||||
const additionalMargin = Math.max(0, (zoomFactor - 1) * 800); // 800是估计的页面高度
|
const additionalMargin = Math.max(0, (zoomFactor - 1) * 800); // 800是估计的页面高度
|
||||||
return baseMargin + additionalMargin;
|
return baseMargin + additionalMargin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 滚动到顶部
|
||||||
|
const handleScrollToTop = () => {
|
||||||
|
if (contentRef.current) {
|
||||||
|
contentRef.current.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 渲染PDF文档的所有页面
|
* 渲染PDF文档的所有页面
|
||||||
@@ -224,7 +267,7 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 渲染评查点高亮区域 */}
|
{/* 渲染评查点高亮区域 */}
|
||||||
{highlightsVisible && pageReviewPoints.map(point => {
|
{/* {highlightsVisible && pageReviewPoints.map(point => {
|
||||||
// 判断当前评查点是否为激活状态(被选中)
|
// 判断当前评查点是否为激活状态(被选中)
|
||||||
const isActive = point.id === activeReviewPointId;
|
const isActive = point.id === activeReviewPointId;
|
||||||
|
|
||||||
@@ -249,7 +292,7 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})} */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -288,7 +331,14 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
|||||||
<i className="ri-file-text-line text-primary mr-2"></i>
|
<i className="ri-file-text-line text-primary mr-2"></i>
|
||||||
<span className="font-medium text-primary">文件预览</span>
|
<span className="font-medium text-primary">文件预览</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="file-preview-actions">
|
<div className="file-preview-actions flex items-center">
|
||||||
|
<button
|
||||||
|
className="ant-btn ant-btn-sm ant-btn-default py-0 px-1 text-xs h-5 leading-5 "
|
||||||
|
onClick={handleScrollToTop}
|
||||||
|
>
|
||||||
|
<i className="ri-arrow-up-double-line "></i>
|
||||||
|
<span className="ml-1">返回顶部</span>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className="ant-btn ant-btn-sm ant-btn-default py-0 px-1 text-xs h-5 leading-5"
|
className="ant-btn ant-btn-sm ant-btn-default py-0 px-1 text-xs h-5 leading-5"
|
||||||
onClick={handleZoomIn}
|
onClick={handleZoomIn}
|
||||||
@@ -301,13 +351,33 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
|||||||
>
|
>
|
||||||
<i className="ri-zoom-out-line"></i>
|
<i className="ri-zoom-out-line"></i>
|
||||||
</button>
|
</button>
|
||||||
|
{/* 页码跳转控件 */}
|
||||||
|
<div className="inline-flex items-center ml-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="ant-input ant-input-sm py-0 px-1 text-xs h-5 leading-5 w-10 text-center
|
||||||
|
focus:outline-none focus:ring-1 focus:ring-green-900"
|
||||||
|
placeholder="页码"
|
||||||
|
value={pageInputValue}
|
||||||
|
onChange={handlePageInputChange}
|
||||||
|
onKeyDown={handlePageInputKeyDown}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="ant-btn ant-btn-sm ant-btn-default py-0 px-1 text-xs h-5 leading-5 ml-1"
|
||||||
|
onClick={handlePageJump}
|
||||||
|
disabled={!numPages}
|
||||||
|
>
|
||||||
|
<i className="ri-arrow-right-line"></i>
|
||||||
|
</button>
|
||||||
|
{numPages && <span className="ml-1 text-xs text-gray-500">/ {numPages}</span>}
|
||||||
|
</div>
|
||||||
{/* <button
|
{/* <button
|
||||||
className="ant-btn ant-btn-sm ant-btn-default py-0 px-1 text-xs h-5 leading-5"
|
className="ant-btn ant-btn-sm ant-btn-default py-0 px-1 text-xs h-5 leading-5"
|
||||||
onClick={toggleHighlights}
|
onClick={toggleHighlights}
|
||||||
>
|
>
|
||||||
<i className="ri-mark-pen-line"></i> {highlightsVisible ? '隐藏问题' : '显示问题'}
|
<i className="ri-mark-pen-line"></i> {highlightsVisible ? '隐藏问题' : '显示问题'}
|
||||||
</button> */}
|
</button> */}
|
||||||
<span className="ml-2 text-xs text-gray-500">{"比例:"+zoomLevel+"%"}</span>
|
<span className="ml-2 text-xs text-gray-500 inline-block">{"比例:"+zoomLevel+"%"}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
* - 操作按钮: 提供一键替换和人工审核功能
|
* - 操作按钮: 提供一键替换和人工审核功能
|
||||||
*/
|
*/
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
// import { toastService } from '../ui/Toast';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 评查点类型定义
|
* 评查点类型定义
|
||||||
@@ -25,11 +26,15 @@ import { useState, useEffect } from 'react';
|
|||||||
*/
|
*/
|
||||||
export interface ReviewPoint {
|
export interface ReviewPoint {
|
||||||
id: string;
|
id: string;
|
||||||
|
documentId?: string;
|
||||||
|
pointId?: string;
|
||||||
|
editAuditStatusId?: string | number;
|
||||||
|
editAuditStatus: number;
|
||||||
pointName: string;
|
pointName: string;
|
||||||
title: string;
|
title: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
status: string;
|
status: string;
|
||||||
content: string | Record<string, string>;
|
content: Record<string, string>;
|
||||||
suggestion: string;
|
suggestion: string;
|
||||||
needsHumanReview?: boolean;
|
needsHumanReview?: boolean;
|
||||||
humanReviewNote?: string;
|
humanReviewNote?: string;
|
||||||
@@ -65,7 +70,7 @@ interface ReviewPointsListProps {
|
|||||||
statistics: Statistics;
|
statistics: Statistics;
|
||||||
activeReviewPointId: string | null;
|
activeReviewPointId: string | null;
|
||||||
onReviewPointSelect: (id: string, page?: number) => void;
|
onReviewPointSelect: (id: string, page?: number) => void;
|
||||||
onStatusChange: (id: string, status: string, message: string) => void;
|
onStatusChange: (id: string, editAuditStatusId: string | number, status: string, message: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReviewPointsList({
|
export function ReviewPointsList({
|
||||||
@@ -79,7 +84,7 @@ export function ReviewPointsList({
|
|||||||
const [editingReviewPoint, setEditingReviewPoint] = useState<string | null>(null); // 当前正在编辑的评查点ID
|
const [editingReviewPoint, setEditingReviewPoint] = useState<string | null>(null); // 当前正在编辑的评查点ID
|
||||||
const [searchText, setSearchText] = useState(''); // 搜索文本
|
const [searchText, setSearchText] = useState(''); // 搜索文本
|
||||||
const [statusFilter, setStatusFilter] = useState<string | null>(null); // 状态过滤
|
const [statusFilter, setStatusFilter] = useState<string | null>(null); // 状态过滤
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const [suggestionTexts, setSuggestionTexts] = useState<Record<string, string>>({}); // 存储每个评查点的建议文本
|
const [suggestionTexts, setSuggestionTexts] = useState<Record<string, string>>({}); // 存储每个评查点的建议文本
|
||||||
|
|
||||||
// 添加重新审核意见的状态/ 用户输入的修改内容 / 用户提前写好的修改内容
|
// 添加重新审核意见的状态/ 用户输入的修改内容 / 用户提前写好的修改内容
|
||||||
@@ -116,25 +121,34 @@ export function ReviewPointsList({
|
|||||||
/**
|
/**
|
||||||
* 处理评查点审核操作
|
* 处理评查点审核操作
|
||||||
* @param reviewPointResultId 评查点结果ID
|
* @param reviewPointResultId 评查点结果ID
|
||||||
* @param action 操作类型: 'approve' 通过 / 'reject' 不通过
|
* @param editAuditStatusId 审核状态记录ID
|
||||||
|
* @param action 操作类型: 'approve' 通过 / 'reject' 不通过 / 'review' 重新审核
|
||||||
* @param message 用户输入的审核内容
|
* @param message 用户输入的审核内容
|
||||||
*/
|
*/
|
||||||
const handleReviewAction = (reviewPointResultId: string, action: 'approve' | 'reject', message: string) => {
|
const handleReviewAction = (reviewPointResultId: string, editAuditStatusId: string | number | undefined, action: 'approve' | 'reject' | 'review', message: string) => {
|
||||||
// 更新评查点状态
|
// 更新评查点状态
|
||||||
onStatusChange(reviewPointResultId, action === 'approve' ? 'true' : 'false', message);
|
// console.log('handleReviewAction-------', reviewPointResultId, editAuditStatusId, action, message);
|
||||||
|
if (action === 'review') {
|
||||||
|
// 重新审核时,不更新结果状态,只更新审核意见和审核状态
|
||||||
|
// console.log('重新审核-------', reviewPointResultId, editAuditStatusId || '', 'review', message);
|
||||||
|
onStatusChange(reviewPointResultId, editAuditStatusId || '', 'review', message);
|
||||||
|
} else {
|
||||||
|
// 通过/不通过时,更新结果状态和审核意见
|
||||||
|
// console.log('通过/不通过-------', reviewPointResultId, editAuditStatusId || '', action === 'approve' ? 'true' : 'false', message);
|
||||||
|
onStatusChange(reviewPointResultId, editAuditStatusId || '', action === 'approve' ? 'true' : 'false', message);
|
||||||
|
}
|
||||||
|
|
||||||
// 将参数输出到控制台
|
// 将参数输出到控制台
|
||||||
console.log('评查点审核通过不通过操作', {
|
console.log('评查点审核操作', {
|
||||||
id: reviewPointResultId,
|
id: reviewPointResultId,
|
||||||
|
editAuditStatusId: editAuditStatusId,
|
||||||
action: action,
|
action: action,
|
||||||
content: message,
|
content: message,
|
||||||
status: action === 'approve' ? 'true' : 'false'
|
status: action === 'approve' ? 'true' : (action === 'reject' ? 'false' : 'review')
|
||||||
});
|
});
|
||||||
|
|
||||||
// 清除编辑状态
|
// 清除编辑状态
|
||||||
setEditingReviewPoint(null);
|
setEditingReviewPoint(null);
|
||||||
|
|
||||||
// alert(`${action === 'approve' ? '通过' : '不通过'}了评查点 ${reviewPointResultId},审核内容: ${message}`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -147,7 +161,6 @@ export function ReviewPointsList({
|
|||||||
point.pointName.toLowerCase().includes(searchText.toLowerCase()) ||
|
point.pointName.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||||
point.title.toLowerCase().includes(searchText.toLowerCase()) ||
|
point.title.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||||
point.groupName.toLowerCase().includes(searchText.toLowerCase()) ||
|
point.groupName.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||||
(typeof point.content === 'string' && point.content.toLowerCase().includes(searchText.toLowerCase())) ||
|
|
||||||
(typeof point.content === 'object' && point.content !== null &&
|
(typeof point.content === 'object' && point.content !== null &&
|
||||||
Object.values(point.content).some(value =>
|
Object.values(point.content).some(value =>
|
||||||
typeof value === 'string' && value.toLowerCase().includes(searchText.toLowerCase())
|
typeof value === 'string' && value.toLowerCase().includes(searchText.toLowerCase())
|
||||||
@@ -291,7 +304,8 @@ export function ReviewPointsList({
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="w-full border border-gray-200 rounded-md pl-8 pr-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-primary"
|
className="w-full border border-gray-200 rounded-md pl-8 pr-2 py-1 text-xs
|
||||||
|
focus:outline-none focus:ring-1 focus:ring-green-800"
|
||||||
placeholder="搜索评查点..."
|
placeholder="搜索评查点..."
|
||||||
value={searchText}
|
value={searchText}
|
||||||
onChange={(e) => setSearchText(e.target.value)}
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
@@ -434,35 +448,24 @@ export function ReviewPointsList({
|
|||||||
// 根据result和status决定渲染哪种样式
|
// 根据result和status决定渲染哪种样式
|
||||||
if (reviewPoint.result === true ){
|
if (reviewPoint.result === true ){
|
||||||
// 已通过的评查点只显示基本信息和人工审核注释 delete
|
// 已通过的评查点只显示基本信息和人工审核注释 delete
|
||||||
if (reviewPoint.needsHumanReview && reviewPoint.humanReviewNote) {
|
// if (reviewPoint.needsHumanReview && reviewPoint.humanReviewNote) {
|
||||||
return (
|
// return (
|
||||||
<div className="mt-2">
|
// <div className="mt-2">
|
||||||
<div className="p-2 bg-green-50 rounded border border-green-200 text-xs">
|
// <div className="p-2 bg-green-50 rounded border border-green-200 text-xs">
|
||||||
<p className="text-xs text-success"><i className="ri-check-line mr-1"></i>已处理</p>
|
// <p className="text-xs text-success"><i className="ri-check-line mr-1"></i>已处理</p>
|
||||||
{reviewPoint.suggestion && (
|
// {reviewPoint.suggestion && (
|
||||||
<div className="border-t border-green-200 mt-1 pt-1">
|
// <div className="border-t border-green-200 mt-1 pt-1">
|
||||||
<p className="text-xs text-gray-600 select-text">{reviewPoint.suggestion}</p>
|
// <p className="text-xs text-gray-600 select-text">{reviewPoint.suggestion}</p>
|
||||||
</div>
|
// </div>
|
||||||
)}
|
// )}
|
||||||
</div>
|
// </div>
|
||||||
</div>
|
// </div>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 处理 result=true 且 postAction=manual 的情况
|
// 处理 result=true 且 postAction=manual 的情况
|
||||||
if (reviewPoint.postAction === 'manual') {
|
if (reviewPoint.postAction === 'manual') {
|
||||||
// 处理重新审核意见的提交
|
const note = manualReviewNotes[reviewPoint.id] || '';
|
||||||
const handleReReview = (reviewPointId: string, status: string) => {
|
|
||||||
const note = manualReviewNotes[reviewPointId] || '';
|
|
||||||
if (!note.trim()) {
|
|
||||||
alert('请输入审核意见');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 在实际应用中,这里应该调用API提交审核意见
|
|
||||||
onStatusChange(reviewPointId, status, note);
|
|
||||||
alert(`提交重新审核意见: ${note}`);
|
|
||||||
// 可以添加提交成功后的状态更新等操作
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理重新审核意见的输入
|
// 处理重新审核意见的输入
|
||||||
const handleNoteChange = (reviewPointId: string, text: string) => {
|
const handleNoteChange = (reviewPointId: string, text: string) => {
|
||||||
@@ -483,7 +486,6 @@ export function ReviewPointsList({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
{/* 额外的文本输入框区域 */}
|
{/* 额外的文本输入框区域 */}
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<textarea
|
<textarea
|
||||||
@@ -496,12 +498,29 @@ export function ReviewPointsList({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<button
|
{reviewPoint.editAuditStatus === 0 ? (
|
||||||
className="bg-purple-600 hover:bg-purple-700 text-xs text-white py-1 px-2 rounded-md flex items-center justify-center"
|
<div className="w-full flex justify-end gap-2">
|
||||||
onClick={() => handleReReview(reviewPoint.id, 'false')}
|
<button
|
||||||
>
|
className="bg-[#1890ff] hover:bg-blue-600 text-xs text-white py-1 px-2 rounded-md flex items-center justify-center"
|
||||||
<i className="ri-refresh-line mr-1"></i> 重新审核
|
onClick={() => handleReviewAction(reviewPoint.id, reviewPoint.editAuditStatusId, 'approve', note)}
|
||||||
</button>
|
>
|
||||||
|
<i className="ri-check-line mr-1"></i> 通过
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="bg-[#f5222d] hover:bg-red-600 text-xs text-white py-1 px-2 rounded-md flex items-center justify-center"
|
||||||
|
onClick={() => handleReviewAction(reviewPoint.id, reviewPoint.editAuditStatusId, 'reject', note)}
|
||||||
|
>
|
||||||
|
<i className="ri-close-line mr-1"></i> 不通过
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
className="bg-purple-600 hover:bg-purple-700 text-xs text-white py-1 px-2 rounded-md flex items-center justify-center"
|
||||||
|
onClick={() => handleReviewAction(reviewPoint.id, reviewPoint.editAuditStatusId, 'review', note)}
|
||||||
|
>
|
||||||
|
<i className="ri-refresh-line mr-1"></i> 重新审核
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -514,60 +533,51 @@ export function ReviewPointsList({
|
|||||||
<p className="text-xs text-red-500 select-text text-left">该评查点无法找到索引内容,无法自动定位到对应页面。</p>
|
<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">
|
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
|
||||||
{typeof reviewPoint.content === 'object' && reviewPoint.content !== null ? (
|
<div>
|
||||||
// 当 content 是对象时的渲染方式
|
{Object.entries(reviewPoint.content).map(([key, value], index) => (
|
||||||
<div>
|
<div
|
||||||
{Object.entries(reviewPoint.content).map(([key, value], index) => (
|
key={index}
|
||||||
<div
|
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"
|
||||||
key={index}
|
onClick={(e) => {
|
||||||
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();
|
||||||
// 阻止事件冒泡,防止触发父元素的点击事件
|
|
||||||
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]);
|
||||||
|
|
||||||
console.log(`非通过:单独点击${key}----`,reviewPoint);
|
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||||
// 检查评查点是否有 contentPage 以及当前 key 对应的页码数组
|
} else {
|
||||||
|
console.log(`通过:单独点击${key}--------没有对应页码`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
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]);
|
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||||
} else {
|
} else {
|
||||||
// 如果没有对应页码,弹出提示
|
console.log(`通过:单独点击${key}--------没有对应页码`);
|
||||||
// alert(`无法找到"${key}"对应的内容页面`);
|
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
onKeyDown={(e) => {
|
}}
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
role="button"
|
||||||
e.preventDefault();
|
tabIndex={0}
|
||||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
aria-label={`查看${key}内容详情`}
|
||||||
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
>
|
||||||
} else {
|
{/* 使用flex布局使key和状态标签左右对齐 */}
|
||||||
// alert(`无法找到"${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 ? '' : '缺失'}
|
||||||
role="button"
|
</span>
|
||||||
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>
|
||||||
))}
|
<p className="text-xs text-left select-text">{value || (value === '' ? <span className="invisible">占位符</span> : '')}</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
))}
|
||||||
// 当 content 是字符串时的渲染方式
|
</div>
|
||||||
<>
|
|
||||||
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -634,76 +644,58 @@ export function ReviewPointsList({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
{reviewPoint.content !== null && (
|
{reviewPoint.content !== null && Object.keys(reviewPoint.content).length > 0 && (
|
||||||
(typeof reviewPoint.content === 'string' && reviewPoint.content !== '') ||
|
|
||||||
(typeof reviewPoint.content === 'object' && Object.keys(reviewPoint.content).length > 0)
|
|
||||||
) && (
|
|
||||||
<>
|
<>
|
||||||
{/* 内容显示区域 */}
|
{/* 内容显示区域 */}
|
||||||
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
|
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
|
||||||
{typeof reviewPoint.content === 'object' && reviewPoint.content !== null ? (
|
<div>
|
||||||
// 当 content 是对象时的渲染方式
|
{Object.entries(reviewPoint.content).map(([key, value], index) => (
|
||||||
<div>
|
<div
|
||||||
{Object.entries(reviewPoint.content).map(([key, value], index) => (
|
key={index}
|
||||||
<div
|
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"
|
||||||
key={index}
|
onClick={(e) => {
|
||||||
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();
|
||||||
// 阻止事件冒泡,防止触发父元素的点击事件
|
|
||||||
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]);
|
||||||
|
|
||||||
console.log(`非通过:单独点击${key}----`,reviewPoint);
|
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||||
// 检查评查点是否有 contentPage 以及当前 key 对应的页码数组
|
} else {
|
||||||
|
// 如果没有对应页码,弹出提示
|
||||||
|
// alert(`无法找到"${key}"对应的内容页面`);
|
||||||
|
console.log(`非通过:单独点击${key}--------没有对应页码`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
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]);
|
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||||
} else {
|
} else {
|
||||||
// 如果没有对应页码,弹出提示
|
|
||||||
// alert(`无法找到"${key}"对应的内容页面`);
|
// alert(`无法找到"${key}"对应的内容页面`);
|
||||||
|
console.log(`非通过:单独点击${key}--------没有对应页码`);
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
onKeyDown={(e) => {
|
}}
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
role="button"
|
||||||
e.preventDefault();
|
tabIndex={0}
|
||||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
aria-label={`查看${key}内容详情`}
|
||||||
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
>
|
||||||
} else {
|
{/* 使用flex布局使key和状态标签左右对齐 */}
|
||||||
// alert(`无法找到"${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'}`}>
|
||||||
}}
|
{value ? '' : '缺失'}
|
||||||
role="button"
|
</span>
|
||||||
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'}`}>
|
|
||||||
{value ? '' : '缺失'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-left select-text">{value || (value === '' ? <span className="invisible">占位符</span> : '')}</p>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
<p className="text-xs text-left select-text">{value || (value === '' ? <span className="invisible">占位符</span> : '')}</p>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
) : (
|
|
||||||
// 当 content 是字符串时的渲染方式
|
|
||||||
<>
|
|
||||||
{/* 为字符串内容也添加标题和状态 */}
|
|
||||||
<div className="flex justify-between items-center mb-1">
|
|
||||||
<span className="text-xs">当前值</span>
|
|
||||||
<span className={`text-xs ${isErrorStatus ? 'text-error' : 'text-warning'}`}>
|
|
||||||
{isErrorStatus ? '不符合规范' : '需优化'}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-left select-text">{reviewPoint.content || '(内容为空)'}</p>
|
))}
|
||||||
</>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -740,13 +732,13 @@ export function ReviewPointsList({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 人工审核按钮 */}
|
{/* 人工审核按钮 */}
|
||||||
{reviewPoint.postAction === 'manual' && (
|
{reviewPoint.editAuditStatus === 0 ? (
|
||||||
<div className="w-full flex justify-end gap-2">
|
<div className="w-full flex justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
className="bg-[#1890ff] hover:bg-blue-600 text-white py-1 px-2 rounded-md text-sm"
|
className="bg-[#1890ff] hover:bg-blue-600 text-white py-1 px-2 rounded-md text-sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const note = manualReviewNotes[reviewPoint.id] || '';
|
const note = manualReviewNotes[reviewPoint.id] || '';
|
||||||
handleReviewAction(reviewPoint.id, 'approve', note);
|
handleReviewAction(reviewPoint.id, reviewPoint.editAuditStatusId, 'approve', note);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className="ri-check-line mr-1"></i> 通过
|
<i className="ri-check-line mr-1"></i> 通过
|
||||||
@@ -755,12 +747,24 @@ export function ReviewPointsList({
|
|||||||
className="bg-[#f5222d] hover:bg-red-600 text-white py-1 px-2 rounded-md text-sm"
|
className="bg-[#f5222d] hover:bg-red-600 text-white py-1 px-2 rounded-md text-sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const note = manualReviewNotes[reviewPoint.id] || '';
|
const note = manualReviewNotes[reviewPoint.id] || '';
|
||||||
handleReviewAction(reviewPoint.id, 'reject', note);
|
handleReviewAction(reviewPoint.id, reviewPoint.editAuditStatusId, 'reject', note);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i className="ri-close-line mr-1"></i> 不通过
|
<i className="ri-close-line mr-1"></i> 不通过
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="w-full flex justify-end">
|
||||||
|
<button
|
||||||
|
className="bg-purple-600 hover:bg-purple-700 text-white py-1 px-2 rounded-md text-sm"
|
||||||
|
onClick={() => {
|
||||||
|
const note = manualReviewNotes[reviewPoint.id] || '';
|
||||||
|
handleReviewAction(reviewPoint.id, reviewPoint.editAuditStatusId, 'review', note);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="ri-refresh-line mr-1"></i> 重新审核
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -768,120 +772,6 @@ export function ReviewPointsList({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处于编辑状态时显示编辑界面
|
|
||||||
// 根据result和status决定显示不同的标记
|
|
||||||
const isErrorStatus = reviewPoint.result === false && reviewPoint.status === 'error';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mt-2">
|
|
||||||
{/* 内容显示区域 */}
|
|
||||||
<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-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 ? '不符合规范' : '需优化'} */}
|
|
||||||
{value ? '' : '缺失'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-left select-text">{value || (value === '' ? <span className="invisible">占位符</span> : '')}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
// 当 content 是字符串时的渲染方式
|
|
||||||
<>
|
|
||||||
{/* 为字符串内容也添加标题和状态 */}
|
|
||||||
<div className="flex justify-between items-center mb-1">
|
|
||||||
<span className="text-xs">当前值</span>
|
|
||||||
<span className={`text-xs ${isErrorStatus ? 'text-error' : 'text-warning'}`}>
|
|
||||||
{isErrorStatus ? '不符合规范' : '需优化'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs select-text">{reviewPoint.content || '(内容为空)'}</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 建议修改区域 */}
|
|
||||||
<div className="mb-2">
|
|
||||||
<div className="flex justify-between items-center mb-2">
|
|
||||||
<span className="text-gray-700 text-xs">建议修改为:</span>
|
|
||||||
{/* <span className="text-green-500 text-xs">符合规范</span> */}
|
|
||||||
</div>
|
|
||||||
<textarea
|
|
||||||
value={suggestionTexts[reviewPoint.id] || ''}
|
|
||||||
onChange={(e) => handleSuggestionChange(reviewPoint.id, e.target.value)}
|
|
||||||
className="w-full p-2 border rounded bg-gray-50 min-h-[100px] focus:outline-none focus:border-[#00684a] focus:shadow-[0_0_0_2px_rgba(0,104,74,0.2)]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 审核意见区域 */}
|
|
||||||
<div className="bg-blue-50 rounded border border-blue-200 p-2">
|
|
||||||
<label htmlFor="reviewNote" className="block text-xs text-gray-700 mb-1">审核意见</label>
|
|
||||||
<textarea
|
|
||||||
id="reviewNote"
|
|
||||||
className="w-full border border-gray-200 rounded-md text-xs p-2 mb-2"
|
|
||||||
placeholder="请输入审核意见(可选)..."
|
|
||||||
rows={2}
|
|
||||||
value={manualReviewNotes[reviewPoint.id] || ''}
|
|
||||||
onChange={(e) => handleManualReviewNotesChange(reviewPoint.id, e.target.value)}
|
|
||||||
></textarea>
|
|
||||||
<div className="flex justify-end mt-2 space-x-2">
|
|
||||||
<button
|
|
||||||
className="replace-action"
|
|
||||||
onClick={() => handleReviewAction(reviewPoint.id, 'approve', manualReviewNotes[reviewPoint.id] || '')}
|
|
||||||
>
|
|
||||||
<i className="ri-check-line"></i> 通过
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="replace-action status-error"
|
|
||||||
onClick={() => handleReviewAction(reviewPoint.id, 'reject', manualReviewNotes[reviewPoint.id] || '')}
|
|
||||||
>
|
|
||||||
<i className="ri-close-line"></i> 不通过
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -912,7 +802,7 @@ export function ReviewPointsList({
|
|||||||
// 处理评查点点击事件
|
// 处理评查点点击事件
|
||||||
const handleReviewPointClick = (id: string) => {
|
const handleReviewPointClick = (id: string) => {
|
||||||
// 找到被点击的评查点
|
// 找到被点击的评查点
|
||||||
const reviewPoint = reviewPoints.find(point => point.id === id);
|
const reviewPoint = reviewPoints.find(result => result.id === id);
|
||||||
|
|
||||||
// 如果评查点存在
|
// 如果评查点存在
|
||||||
if (reviewPoint) {
|
if (reviewPoint) {
|
||||||
@@ -928,9 +818,11 @@ export function ReviewPointsList({
|
|||||||
|
|
||||||
// 没有有效页码,只传递ID
|
// 没有有效页码,只传递ID
|
||||||
onReviewPointSelect(id);
|
onReviewPointSelect(id);
|
||||||
|
console.log(`没有有效页码---评查点ID:${reviewPoint.pointId},评查点结果ID:${id}`);
|
||||||
} else {
|
} else {
|
||||||
// 没有找到评查点,只传递ID
|
// 没有找到评查点,只传递ID
|
||||||
onReviewPointSelect(id);
|
onReviewPointSelect(id);
|
||||||
|
console.log(`没有找到评查点---评查点结果ID:${id}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -981,11 +873,17 @@ export function ReviewPointsList({
|
|||||||
<div className="review-points-list">
|
<div className="review-points-list">
|
||||||
{filteredReviewPoints.length > 0 ? (
|
{filteredReviewPoints.length > 0 ? (
|
||||||
filteredReviewPoints.map(reviewPoint => (
|
filteredReviewPoints.map(reviewPoint => (
|
||||||
<button
|
<div
|
||||||
key={reviewPoint.id}
|
key={reviewPoint.id}
|
||||||
className={`review-point-item ${activeReviewPointId === reviewPoint.id ? 'active' : ''}`}
|
className={`review-point-item ${activeReviewPointId === reviewPoint.id ? 'active' : ''}`}
|
||||||
onClick={() => handleReviewPointClick(reviewPoint.id)}
|
onClick={() => handleReviewPointClick(reviewPoint.id)}
|
||||||
type="button"
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
handleReviewPointClick(reviewPoint.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
style={{ userSelect: 'text' }}
|
style={{ userSelect: 'text' }}
|
||||||
>
|
>
|
||||||
{/* 评查点标题和状态 */}
|
{/* 评查点标题和状态 */}
|
||||||
@@ -1009,7 +907,7 @@ export function ReviewPointsList({
|
|||||||
|
|
||||||
{/* 评查点内容和操作 */}
|
{/* 评查点内容和操作 */}
|
||||||
{renderReviewPointContent(reviewPoint)}
|
{renderReviewPointContent(reviewPoint)}
|
||||||
</button>
|
</div>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
renderEmptyState()
|
renderEmptyState()
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function ActionButtons({ onSave, onSaveDraft, isEditMode }: ActionButtons
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="ant-btn ant-btn-default min-w-[120px]"
|
className="ant-btn ant-btn-default min-w-[120px] !hidden"
|
||||||
onClick={onSaveDraft}
|
onClick={onSaveDraft}
|
||||||
>
|
>
|
||||||
<i className="ri-draft-line mr-1"></i> {isEditMode ? '另存为草稿' : '保存草稿'}
|
<i className="ri-draft-line mr-1"></i> {isEditMode ? '另存为草稿' : '保存草稿'}
|
||||||
|
|||||||
+130
-37
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useLoaderData, useActionData, useNavigate, Form } from "@remix-run/react";
|
import { useLoaderData, useActionData, useNavigate, Form } from "@remix-run/react";
|
||||||
import { redirect, type ActionFunctionArgs, type LoaderFunctionArgs, type MetaFunction } from "@remix-run/node";
|
import { redirect, type ActionFunctionArgs, type LoaderFunctionArgs, type MetaFunction } from "@remix-run/node";
|
||||||
import { Card } from "~/components/ui/Card";
|
import { Card } from "~/components/ui/Card";
|
||||||
@@ -7,6 +7,10 @@ import documentEditStyles from "~/styles/pages/documents_edit.css?url";
|
|||||||
import { getDocument, updateDocument } from "~/api/files/documents";
|
import { getDocument, updateDocument } from "~/api/files/documents";
|
||||||
import { getDocumentTypes } from "~/api/document-types/document-types";
|
import { getDocumentTypes } from "~/api/document-types/document-types";
|
||||||
import { FileTag } from "~/components/ui/FileTag";
|
import { FileTag } from "~/components/ui/FileTag";
|
||||||
|
import { toastService } from "~/components/ui/Toast";
|
||||||
|
import { Document, Page , pdfjs } from "react-pdf";
|
||||||
|
|
||||||
|
pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js';
|
||||||
|
|
||||||
export function links() {
|
export function links() {
|
||||||
return [{ rel: "stylesheet", href: documentEditStyles }];
|
return [{ rel: "stylesheet", href: documentEditStyles }];
|
||||||
@@ -19,6 +23,10 @@ export const meta: MetaFunction = () => {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const handle = {
|
||||||
|
breadcrumb: "文档编辑"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 文档审核状态定义
|
// 文档审核状态定义
|
||||||
@@ -106,31 +114,31 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
|
|
||||||
// 从表单数据中提取字段
|
// 从表单数据中提取字段
|
||||||
const type = formData.get("type_id") as string;
|
// const type = formData.get("type_id") as string;
|
||||||
const documentNumber = formData.get("document_number") as string;
|
const documentNumber = formData.get("document_number") as string;
|
||||||
const auditStatus = parseInt(formData.get("audit_status") as string);
|
const auditStatus = parseInt(formData.get("audit_status") as string);
|
||||||
const isTest = formData.get("is_test_document") === "on";
|
const isTest = formData.get("is_test_document") === "on";
|
||||||
const remark = formData.get("remark") as string;
|
const remark = formData.get("remark") as string;
|
||||||
|
|
||||||
// 验证必填字段
|
// 验证必填字段
|
||||||
if (!type || auditStatus === undefined || isNaN(auditStatus)) {
|
// if (!type || auditStatus === undefined || isNaN(auditStatus)) {
|
||||||
return Response.json(
|
// return Response.json(
|
||||||
{
|
// {
|
||||||
error: "缺少必填字段",
|
// error: "缺少必填字段",
|
||||||
fieldErrors: {
|
// fieldErrors: {
|
||||||
type_id: !type ? "文档类型不能为空" : null,
|
// type_id: !type ? "文档类型不能为空" : null,
|
||||||
audit_status: (auditStatus === undefined || isNaN(auditStatus)) ? "审核状态不能为空" : null
|
// audit_status: (auditStatus === undefined || isNaN(auditStatus)) ? "审核状态不能为空" : null
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
{ status: 400 }
|
// { status: 400 }
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
console.log('提交更新:', { type, documentNumber, auditStatus, isTest, remark });
|
// console.log('提交更新:', { type, documentNumber, auditStatus, isTest, remark });
|
||||||
|
|
||||||
// 更新文档
|
// 更新文档
|
||||||
const updateResponse = await updateDocument(id, {
|
const updateResponse = await updateDocument(id, {
|
||||||
type,
|
// type,
|
||||||
documentNumber,
|
documentNumber,
|
||||||
auditStatus,
|
auditStatus,
|
||||||
isTest,
|
isTest,
|
||||||
@@ -138,17 +146,23 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (updateResponse.error) {
|
if (updateResponse.error) {
|
||||||
console.error('更新文档失败:', updateResponse.error);
|
console.error('更新文档失败1:', updateResponse.error);
|
||||||
return Response.json({
|
return Response.json({
|
||||||
error: updateResponse.error,
|
error: updateResponse.error,
|
||||||
message: "更新文档失败,请检查提交的数据是否正确"
|
message: "更新文档失败,请检查提交的数据是否正确"
|
||||||
}, { status: updateResponse.status || 500 });
|
}, { status: updateResponse.status || 500 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toastService.success('更新文档成功');
|
||||||
|
|
||||||
// 重定向回文档列表
|
// 重定向回文档列表
|
||||||
return redirect("/documents");
|
// return redirect("/documents");
|
||||||
|
return Response.json({
|
||||||
|
success: true,
|
||||||
|
message: "更新文档成功"
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("更新文档失败:", error);
|
console.error("更新文档失败2:", error);
|
||||||
return Response.json({
|
return Response.json({
|
||||||
error: "更新文档失败",
|
error: "更新文档失败",
|
||||||
message: error instanceof Error ? error.message : "发生未知错误"
|
message: error instanceof Error ? error.message : "发生未知错误"
|
||||||
@@ -160,8 +174,96 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
export default function DocumentEdit() {
|
export default function DocumentEdit() {
|
||||||
const { document, documentTypes } = useLoaderData<typeof loader>();
|
const { document, documentTypes } = useLoaderData<typeof loader>();
|
||||||
const actionData = useActionData<typeof action>();
|
const actionData = useActionData<typeof action>();
|
||||||
|
const [numPages, setNumPages] = useState(0);
|
||||||
|
const [loadError, setLoadError] = useState<string | null>(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// console.log('actionData', actionData);
|
||||||
|
if (actionData?.error) {
|
||||||
|
toastService.error('更新文档失败:' + actionData.error);
|
||||||
|
}
|
||||||
|
if (actionData?.success) {
|
||||||
|
toastService.success('更新文档成功');
|
||||||
|
}
|
||||||
|
}, [actionData]);
|
||||||
|
|
||||||
|
const onDocumentLoadSuccess = ({numPages}: {numPages: number}) => {
|
||||||
|
setNumPages(numPages);
|
||||||
|
console.log('文档加载成功', numPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderDocumentContent = () => {
|
||||||
|
return (
|
||||||
|
<div className="preview-content relative overflow-y-auto max-h-[1000px]">
|
||||||
|
<Document
|
||||||
|
file={'http://172.18.0.100:9000/docauditai/'+document.path}
|
||||||
|
onLoadSuccess={onDocumentLoadSuccess}
|
||||||
|
onLoadError={(error) => {
|
||||||
|
console.error("PDF加载错误:", error);
|
||||||
|
setLoadError("PDF文档加载失败:" + (error.message || "未知错误"));
|
||||||
|
}}
|
||||||
|
className="flex flex-col items-center w-full"
|
||||||
|
error={<div className="text-red-500">PDF文档加载失败,请检查链接或网络连接。</div>}
|
||||||
|
noData={<div>无数据</div>}
|
||||||
|
loading={<div className="text-center py-10">PDF加载中...</div>}
|
||||||
|
>
|
||||||
|
{renderAllPages()}
|
||||||
|
</Document>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderAllPages = () => {
|
||||||
|
// 如果还没有获取到PDF总页数,返回null
|
||||||
|
if (!numPages) return null;
|
||||||
|
|
||||||
|
// 用于存储所有页面组件的数组
|
||||||
|
const pages = [];
|
||||||
|
const styles = {
|
||||||
|
pageContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column' as const,
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
position: 'relative' as const,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 遍历每一页,生成对应的页面组件
|
||||||
|
for (let i = 1; i <= numPages; i++) {
|
||||||
|
// 为每一页创建组件
|
||||||
|
pages.push(
|
||||||
|
<div key={i} id={`page-${i}`} style={styles.pageContainer}>
|
||||||
|
{/* 页码标识,显示在页面上方 */}
|
||||||
|
<div className="text-center text-gray-500 text-sm mb-2">第 {i} 页</div>
|
||||||
|
|
||||||
|
{/* 页面容器,应用缩放变换,设置相对定位用于放置评查点高亮 */}
|
||||||
|
<div
|
||||||
|
className="page-wrapper flex justify-center"
|
||||||
|
style={{
|
||||||
|
// transform: `scale(${zoomFactor})`, // 根据zoomLevel应用缩放
|
||||||
|
transformOrigin: 'top center', // 缩放原点设置为顶部中心
|
||||||
|
position: 'relative', // 相对定位,作为评查点高亮的定位参考
|
||||||
|
maxWidth: '100%', // 限制最大宽度
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 渲染PDF页面组件 */}
|
||||||
|
<Page
|
||||||
|
pageNumber={i} // 当前页码
|
||||||
|
renderTextLayer={true} // 启用文本层,使文本可选择
|
||||||
|
renderAnnotationLayer={true} // 启用注释层,显示PDF内置注释
|
||||||
|
className="border border-gray-300 shadow-md" // 添加边框和阴影样式
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回所有页面组件数组
|
||||||
|
return pages;
|
||||||
|
};
|
||||||
|
|
||||||
// 定义类型
|
// 定义类型
|
||||||
interface DocType {
|
interface DocType {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
@@ -275,7 +377,8 @@ export default function DocumentEdit() {
|
|||||||
name="type_id"
|
name="type_id"
|
||||||
className="form-select"
|
className="form-select"
|
||||||
defaultValue={document.type}
|
defaultValue={document.type}
|
||||||
disabled={document.fileStatus !== 'Processed'}
|
// disabled={document.fileStatus !== 'Processed'}
|
||||||
|
disabled={true}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
{documentTypes.map((type: DocType) => (
|
{documentTypes.map((type: DocType) => (
|
||||||
@@ -384,22 +487,12 @@ export default function DocumentEdit() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="preview-content">
|
{/* 预览窗口 */}
|
||||||
<div className="preview-placeholder">
|
{loadError ?(<div className="text-red-500">
|
||||||
<i className={`ri-file-${document.fileType}-line`}></i>
|
{loadError}
|
||||||
<p>预览功能暂不可用</p>
|
</div>):(
|
||||||
<p className="text-xs mt-2">点击"在新窗口打开"查看完整文档</p>
|
renderDocumentContent()
|
||||||
<Button
|
)}
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
icon="ri-external-link-line"
|
|
||||||
className="mt-4"
|
|
||||||
onClick={openPreview}
|
|
||||||
>
|
|
||||||
在新窗口打开
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { UploadArea, UploadAreaRef } from "~/components/ui/UploadArea";
|
|||||||
import { FileProgress} from "~/components/ui/FileProgress";
|
import { FileProgress} from "~/components/ui/FileProgress";
|
||||||
import { ProcessingSteps, Step } from "~/components/ui/ProcessingSteps";
|
import { ProcessingSteps, Step } from "~/components/ui/ProcessingSteps";
|
||||||
import uploadStyles from "~/styles/pages/files_upload.css?url";
|
import uploadStyles from "~/styles/pages/files_upload.css?url";
|
||||||
|
import { messageService } from "~/components/ui/MessageModal";
|
||||||
import {
|
import {
|
||||||
getTodayDocuments,
|
getTodayDocuments,
|
||||||
getDocumentTypes,
|
getDocumentTypes,
|
||||||
@@ -323,6 +324,18 @@ export default function FilesUpload() {
|
|||||||
// 获取action返回的数据
|
// 获取action返回的数据
|
||||||
const actionData = useActionData<ActionData>();
|
const actionData = useActionData<ActionData>();
|
||||||
|
|
||||||
|
// 添加一个本地状态来跟踪文件类型错误
|
||||||
|
const [fileTypeError, setFileTypeError] = useState<string | null>(
|
||||||
|
actionData?.errors?.fileType || null
|
||||||
|
);
|
||||||
|
|
||||||
|
// 监听actionData变化,当有fileType错误时更新fileTypeError状态
|
||||||
|
useEffect(() => {
|
||||||
|
if (actionData?.errors?.fileType) {
|
||||||
|
setFileTypeError(actionData.errors.fileType);
|
||||||
|
}
|
||||||
|
}, [actionData]);
|
||||||
|
|
||||||
// 状态检查定时器引用
|
// 状态检查定时器引用
|
||||||
const statusCheckIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
const statusCheckIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
@@ -402,6 +415,8 @@ export default function FilesUpload() {
|
|||||||
// 确保只有选择了有效的文件类型才进行设置
|
// 确保只有选择了有效的文件类型才进行设置
|
||||||
if (value) {
|
if (value) {
|
||||||
setFileType(value as FileType);
|
setFileType(value as FileType);
|
||||||
|
// 立即清除错误状态
|
||||||
|
setFileTypeError(null);
|
||||||
|
|
||||||
// 如果已经有选中的文件,且选择了文件类型,则开始上传
|
// 如果已经有选中的文件,且选择了文件类型,则开始上传
|
||||||
if (currentFiles.length > 0) {
|
if (currentFiles.length > 0) {
|
||||||
@@ -409,6 +424,8 @@ export default function FilesUpload() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setFileType("");
|
setFileType("");
|
||||||
|
// 如果用户选择了空选项,显示错误信息
|
||||||
|
setFileTypeError("上传文件之前请选择文件类型");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -535,6 +552,7 @@ export default function FilesUpload() {
|
|||||||
const errorSteps = [...processingSteps];
|
const errorSteps = [...processingSteps];
|
||||||
errorSteps[0].status = "error";
|
errorSteps[0].status = "error";
|
||||||
errorSteps[0].description = `上传文件失败: ${error instanceof Error ? error.message : '未知错误'}`;
|
errorSteps[0].description = `上传文件失败: ${error instanceof Error ? error.message : '未知错误'}`;
|
||||||
|
|
||||||
setProcessingSteps(errorSteps);
|
setProcessingSteps(errorSteps);
|
||||||
|
|
||||||
// 清除进度定时器
|
// 清除进度定时器
|
||||||
@@ -542,7 +560,13 @@ export default function FilesUpload() {
|
|||||||
clearInterval(progressIntervalRef.current);
|
clearInterval(progressIntervalRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
alert(`文件上传失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
// 显示错误提示
|
||||||
|
messageService.error('文件上传失败:只能上传pdf文件。', {
|
||||||
|
title: '文件上传失败',
|
||||||
|
onConfirm: () => {
|
||||||
|
resetUpload();
|
||||||
|
}
|
||||||
|
});
|
||||||
resetUpload();
|
resetUpload();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -924,7 +948,7 @@ export default function FilesUpload() {
|
|||||||
<select
|
<select
|
||||||
id="file-type-select"
|
id="file-type-select"
|
||||||
name="fileType"
|
name="fileType"
|
||||||
className={`form-select ${actionData?.errors?.fileType ? 'border-red-500' : ''}`}
|
className={`form-select ${fileTypeError ? 'border-red-500' : ''}`}
|
||||||
value={fileType}
|
value={fileType}
|
||||||
onChange={handleFileTypeChange}
|
onChange={handleFileTypeChange}
|
||||||
disabled={uploadStage !== "idle"}
|
disabled={uploadStage !== "idle"}
|
||||||
@@ -935,8 +959,8 @@ export default function FilesUpload() {
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{actionData?.errors?.fileType && (
|
{fileTypeError && (
|
||||||
<div className="text-red-500 text-sm mt-1">{actionData.errors.fileType}</div>
|
<div className="text-red-500 text-sm mt-1">{fileTypeError}</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="form-tip">不同类型的文档将应用不同的审核规则</div>
|
<div className="form-tip">不同类型的文档将应用不同的审核规则</div>
|
||||||
@@ -1000,8 +1024,9 @@ export default function FilesUpload() {
|
|||||||
ref={uploadAreaRef}
|
ref={uploadAreaRef}
|
||||||
onFilesSelected={handleFilesSelected}
|
onFilesSelected={handleFilesSelected}
|
||||||
multiple={true}
|
multiple={true}
|
||||||
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png"
|
// accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png"
|
||||||
tipText="支持单个或批量上传,文件格式:PDF、Word、Excel、图片"
|
accept=".pdf"
|
||||||
|
tipText="支持单个或多个pdf文件上传,文件格式:PDF"
|
||||||
shouldPreventFileSelect={!fileType}
|
shouldPreventFileSelect={!fileType}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -1021,17 +1046,28 @@ export default function FilesUpload() {
|
|||||||
{/* 高级上传设置 */}
|
{/* 高级上传设置 */}
|
||||||
{ showAdvancedOptions && (
|
{ showAdvancedOptions && (
|
||||||
<div className="advanced-options">
|
<div className="advanced-options">
|
||||||
<div
|
<button
|
||||||
className={`advanced-options-toggle ${showAdvancedOptions ? 'open' : ''}`}
|
className={`advanced-options-toggle ${showAdvancedOptions ? 'open' : ''}`}
|
||||||
onClick={() => setShowAdvancedOptions(!showAdvancedOptions)}
|
onClick={() => setShowAdvancedOptions(!showAdvancedOptions)}
|
||||||
|
aria-expanded={showAdvancedOptions}
|
||||||
|
aria-controls="advanced-options-content"
|
||||||
|
type="button"
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
setShowAdvancedOptions(!showAdvancedOptions);
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span>高级上传设置</span>
|
<span>高级上传设置</span>
|
||||||
<i className="ri-arrow-down-s-line"></i>
|
<i className="ri-arrow-down-s-line" aria-hidden="true"></i>
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
id="advanced-options-content"
|
||||||
className="advanced-options-content"
|
className="advanced-options-content"
|
||||||
style={{ display: showAdvancedOptions ? 'block' : 'none' }}
|
style={{ display: showAdvancedOptions ? 'block' : 'none' }}
|
||||||
|
aria-hidden={!showAdvancedOptions}
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
|
|||||||
+18
-15
@@ -30,6 +30,7 @@ import { useState, useEffect } from "react";
|
|||||||
import { useNavigate, useLoaderData } from "@remix-run/react";
|
import { useNavigate, useLoaderData } from "@remix-run/react";
|
||||||
import reviewsStyles from "~/styles/reviews.css?url";
|
import reviewsStyles from "~/styles/reviews.css?url";
|
||||||
import { getReviewPoints, updateReviewResult, confirmReviewResults } from "~/api/evaluation_points/reviews";
|
import { getReviewPoints, updateReviewResult, confirmReviewResults } from "~/api/evaluation_points/reviews";
|
||||||
|
import { toastService } from "~/components/ui/Toast";
|
||||||
|
|
||||||
// 导入评查详情页面组件
|
// 导入评查详情页面组件
|
||||||
import {
|
import {
|
||||||
@@ -208,7 +209,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
const reviewData = await getReviewPoints(id);
|
const reviewData = await getReviewPoints(id);
|
||||||
|
|
||||||
// console.log("documentData-------",JSON.stringify(documentData.data,null,2));
|
// console.log("documentData-------",JSON.stringify(documentData.data,null,2));
|
||||||
console.log("reviewData-------",JSON.stringify('data' in reviewData ? reviewData.data : '',null,2));
|
// console.log("reviewData-------",JSON.stringify('data' in reviewData ? reviewData.data : '',null,2));
|
||||||
if ('error' in reviewData && reviewData.error) {
|
if ('error' in reviewData && reviewData.error) {
|
||||||
console.error("获取评查点数据错误:", reviewData.error);
|
console.error("获取评查点数据错误:", reviewData.error);
|
||||||
return Response.json({ error: reviewData.error }, { status: reviewData.status || 500 });
|
return Response.json({ error: reviewData.error }, { status: reviewData.status || 500 });
|
||||||
@@ -357,31 +358,34 @@ export default function ReviewDetails() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
alert('评审数据已更新');
|
toastService.success('评审数据已更新');
|
||||||
} else {
|
} else {
|
||||||
console.error('返回的数据格式不正确');
|
console.error('返回的数据格式不正确');
|
||||||
alert('刷新评审数据失败: 返回的数据格式不正确');
|
toastService.error('刷新评审数据失败: 返回的数据格式不正确');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('刷新评审数据失败:', error);
|
console.error('刷新评审数据失败:', error);
|
||||||
alert(`刷新评审数据失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
toastService.error(`刷新评审数据失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理评审点状态变更
|
// 处理评审点状态变更
|
||||||
const handleReviewPointStatusChange = async (reviewPointResultId: string, newStatus: string, message: string) => {
|
const handleReviewPointStatusChange = async (reviewPointResultId: string, editAuditStatusId: string | number, newStatus: string, message: string) => {
|
||||||
// 将字符串的布尔值转换为布尔类型
|
// 将字符串的布尔值转换为布尔类型
|
||||||
const boolResult = newStatus === 'true';
|
let boolResult = 'review';
|
||||||
|
if(newStatus !== 'review'){
|
||||||
|
boolResult = newStatus === 'true' ? 'true' : 'false';
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 调用 API 更新评查结果
|
// 调用 API 更新评查结果
|
||||||
const response = await updateReviewResult(reviewPointResultId, boolResult, message);
|
const response = await updateReviewResult(reviewPointResultId, editAuditStatusId, boolResult, message);
|
||||||
|
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
console.error('更新评查结果失败:', response.error);
|
console.error('更新评查结果失败:', response.error);
|
||||||
alert(`更新评查结果失败: ${response.error}`);
|
toastService.error(`更新评查结果失败: ${response.error}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,8 +400,7 @@ export default function ReviewDetails() {
|
|||||||
const updatedReviewPoints = reviewData.reviewPoints.map(point =>
|
const updatedReviewPoints = reviewData.reviewPoints.map(point =>
|
||||||
point.id === reviewPointResultId ? {
|
point.id === reviewPointResultId ? {
|
||||||
...point,
|
...point,
|
||||||
result: boolResult,
|
result: newStatus === 'true' ? true : (newStatus === 'false' ? false : point.result),
|
||||||
status: boolResult ? 'success' : 'error',
|
|
||||||
message: message
|
message: message
|
||||||
} : point
|
} : point
|
||||||
);
|
);
|
||||||
@@ -416,13 +419,13 @@ export default function ReviewDetails() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新评查结果出错:', error);
|
console.error('更新评查结果出错:', error);
|
||||||
alert('更新评查结果失败,请稍后重试');
|
toastService.error('更新评查结果失败,请稍后重试');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirmResults = async () => {
|
const handleConfirmResults = async () => {
|
||||||
if (!document || !document.id) {
|
if (!document || !document.id) {
|
||||||
alert('文档数据不完整,无法确认评查结果');
|
toastService.error('文档数据不完整,无法确认评查结果');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,18 +438,18 @@ export default function ReviewDetails() {
|
|||||||
|
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
console.error('确认评查结果失败:', response.error);
|
console.error('确认评查结果失败:', response.error);
|
||||||
alert(`确认评查结果失败: ${response.error}`);
|
toastService.error(`确认评查结果失败: ${response.error}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示成功消息
|
// 显示成功消息
|
||||||
alert('评查结果已确认,文档审核状态已更新');
|
toastService.success('评查结果已确认,文档审核状态已更新');
|
||||||
|
|
||||||
// 导航到文档列表页
|
// 导航到文档列表页
|
||||||
navigate('/documents');
|
navigate('/documents');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('确认评查结果出错:', error);
|
console.error('确认评查结果出错:', error);
|
||||||
alert(`确认评查结果失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
toastService.error(`确认评查结果失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
pageSize,
|
pageSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('rules-filessearchParams-----',searchParams);
|
// console.log('rules-filessearchParams-----',searchParams);
|
||||||
|
|
||||||
const filesResponse = await getReviewFiles(searchParams);
|
const filesResponse = await getReviewFiles(searchParams);
|
||||||
if (filesResponse.error) {
|
if (filesResponse.error) {
|
||||||
@@ -343,16 +343,47 @@ export default function RulesFiles() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "评查状态",
|
title: "评查统计",
|
||||||
key: "reviewStatus",
|
key: "reviewStatus",
|
||||||
width: "12%",
|
width: "12%",
|
||||||
render: (_: unknown, file: ReviewFileUI) =>
|
render: (_: unknown, file: ReviewFileUI) =>
|
||||||
|
// 要文件切分处理完之后,再显示评查统计
|
||||||
file.status === 'Processed' ? (
|
file.status === 'Processed' ? (
|
||||||
<StatusBadge
|
<div>
|
||||||
status={file.reviewStatus}
|
{file.passCount > 0 && (
|
||||||
text={`${REVIEW_STATUS_LABELS[file.reviewStatus]}${file.issueCount>0?'('+file.issueCount+')':''}`}
|
<StatusBadge
|
||||||
showIcon={true}
|
status="pass"
|
||||||
/>
|
text={`通过(${file.passCount})`}
|
||||||
|
showIcon={true}
|
||||||
|
className="my-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{file.warningCount > 0 && (
|
||||||
|
<StatusBadge
|
||||||
|
status="warning"
|
||||||
|
text={`警告(${file.warningCount})`}
|
||||||
|
showIcon={true}
|
||||||
|
className="my-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{file.failCount > 0 && (
|
||||||
|
<StatusBadge
|
||||||
|
status="fail"
|
||||||
|
text={`不通过(${file.failCount})`}
|
||||||
|
showIcon={true}
|
||||||
|
className="my-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{file.manualCount > 0 && (
|
||||||
|
<StatusBadge
|
||||||
|
status="pending"
|
||||||
|
text={`需人工(${file.manualCount})`}
|
||||||
|
showIcon={true}
|
||||||
|
className="my-2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
) : (
|
) : (
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
-
|
-
|
||||||
@@ -451,10 +482,10 @@ export default function RulesFiles() {
|
|||||||
/>
|
/>
|
||||||
<SearchFilter
|
<SearchFilter
|
||||||
label="搜索"
|
label="搜索"
|
||||||
placeholder="搜索文件名、合同编号或关键词"
|
placeholder="搜索文件名、合同编号"
|
||||||
value={searchParams.get('keyword') || ''}
|
value={searchParams.get('keyword') || ''}
|
||||||
onSearch={handleSearch}
|
onSearch={handleSearch}
|
||||||
buttonText=""
|
buttonText="搜索"
|
||||||
className="mr-2 flex-1"
|
className="mr-2 flex-1"
|
||||||
/>
|
/>
|
||||||
</FilterPanel>
|
</FilterPanel>
|
||||||
|
|||||||
@@ -706,6 +706,11 @@ export default function RuleNew() {
|
|||||||
const id = searchParams.get('id');
|
const id = searchParams.get('id');
|
||||||
const mode = searchParams.get('mode');
|
const mode = searchParams.get('mode');
|
||||||
|
|
||||||
|
// 编辑或复制模式下设置加载状态
|
||||||
|
if (id || mode === 'copy') {
|
||||||
|
setIsLoading(true);
|
||||||
|
}
|
||||||
|
|
||||||
// 设置编辑模式
|
// 设置编辑模式
|
||||||
if (mode && mode === 'copy') {
|
if (mode && mode === 'copy') {
|
||||||
setIsEditMode(false);
|
setIsEditMode(false);
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ input:checked + .slider:before {
|
|||||||
|
|
||||||
/* 文档预览样式 */
|
/* 文档预览样式 */
|
||||||
.document-preview {
|
.document-preview {
|
||||||
@apply border border-gray-200 rounded-md overflow-hidden h-96;
|
@apply border border-gray-200 rounded-md overflow-hidden max-h-[1000px] ;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-toolbar {
|
.preview-toolbar {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
height: calc(100vh - 220px);
|
height: calc(100vh - 120px);
|
||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
height: calc(100vh - 220px);
|
height: calc(100vh - 120px);
|
||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in New Issue
Block a user