diff --git a/app/api/base.ts b/app/api/base.ts deleted file mode 100644 index cbbe7dd..0000000 --- a/app/api/base.ts +++ /dev/null @@ -1,131 +0,0 @@ -/** - * API基础服务 - */ - -// 默认API基础URL -const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:8000'; - -/** - * 通用API响应格式 - */ -export interface ApiResponse { - status: 'success' | 'error'; - data?: T; - message?: string; - error?: { - code: string; - details?: any; - }; -} - -/** - * 分页请求参数 - */ -export interface PaginationParams { - page: number; - pageSize: number; -} - -/** - * 分页响应数据 - */ -export interface PaginatedResponse { - items: T[]; - totalItems: number; - totalPages: number; - currentPage: number; - pageSize: number; -} - -/** - * 统一错误处理 - */ -export class ApiError extends Error { - statusCode: number; - errorCode: string; - details?: any; - - constructor(message: string, statusCode: number, errorCode: string, details?: any) { - super(message); - this.name = 'ApiError'; - this.statusCode = statusCode; - this.errorCode = errorCode; - this.details = details; - } -} - -/** - * 构建请求URL - */ -export function buildUrl(path: string, params?: Record): string { - // 确保API_BASE_URL末尾没有斜杠,而path开头有斜杠 - const baseUrl = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL; - const normalizedPath = path.startsWith('/') ? path : `/${path}`; - - try { - const url = new URL(`${baseUrl}${normalizedPath}`); - - if (params) { - Object.entries(params).forEach(([key, value]) => { - if (value !== undefined && value !== null) { - url.searchParams.append(key, String(value)); - } - }); - } - - return url.toString(); - } catch (error) { - console.error('URL构建错误:', error); - throw new Error(`无法构建URL: ${baseUrl}${normalizedPath}, 错误: ${error}`); - } -} - -/** - * 通用API请求函数 - */ -export async function apiRequest( - url: string, - method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET', - body?: any, - headers?: Record -): Promise { - const requestInit: RequestInit = { - method, - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - ...headers - } - }; - - if (body) { - requestInit.body = JSON.stringify(body); - } - - const response = await fetch(url, requestInit); - - // 获取响应数据 - const data = await response.json() as ApiResponse; - - // 检查状态码 - if (!response.ok) { - throw new ApiError( - data.message || '请求失败', - response.status, - data.error?.code || 'UNKNOWN_ERROR', - data.error?.details - ); - } - - // 检查业务状态 - if (data.status === 'error') { - throw new ApiError( - data.message || '请求失败', - response.status, - data.error?.code || 'BUSINESS_ERROR', - data.error?.details - ); - } - - return data.data as T; -} \ No newline at end of file diff --git a/app/api/evaluation_points/reviews.ts b/app/api/evaluation_points/reviews.ts index b196a4d..ba135b0 100644 --- a/app/api/evaluation_points/reviews.ts +++ b/app/api/evaluation_points/reviews.ts @@ -57,6 +57,7 @@ interface EvaluationPoint { suggestion_message_type?: string; suggestion_message?: string; score?: number; + updated_at?: string; [key: string]: unknown; } @@ -92,7 +93,7 @@ interface StatsData { * 获取当前评查文件的所有评查点结果 * @param fileId 评查文件ID * @returns 评查点结果列表和统计数据 - */ + */ export async function getReviewPoints(fileId: string) { // 步骤1:根据fileId查询evaluation_results表 const evaluationResultsParams: PostgrestParams = { @@ -197,12 +198,24 @@ export async function getReviewPoints(fileId: string) { return { id: result.id, title: message, + pointName: point.name || '', groupName: group.name || '', status: point.suggestion_message_type || '', content: data, suggestion: point.suggestion_message || '', result: result.evaluated_results?.result, // 记录评查结果,用于统计 - score: point.score || 0 + score: point.score || 0, + legalBasis: point.references_laws || {} + // legalBasis: { + // name: '中华人民共和国食品安全法', + // content: '中华人民共和国食品安全法', + // article: [ + // { + // name: '中华人民共和国食品安全法', + // content: '中华人民共和国食品安全法' + // } + // ] + // } }; }); @@ -233,5 +246,30 @@ export async function getReviewPoints(fileId: string) { stats.score += item.score || 0; }); - return { data: resultData, stats }; + // 构建文件信息-评查信息的数据 + // 找出最新的评查时间 + let latestUpdatedAt = ''; + evaluationResultsData.forEach(result => { + if (result.updated_at && (!latestUpdatedAt || result.updated_at > latestUpdatedAt)) { + latestUpdatedAt = result.updated_at.toString(); + } + }); + + // 提取不重复的规则组名称 + const uniqueGroups = Array.from(new Set(resultData.map(item => item.groupName))).filter(Boolean); + + // 计算问题数量 + const issueCount = stats.warning + stats.error; + + // 构建评查信息对象 + const reviewInfo = { + reviewTime: formatDate(latestUpdatedAt), + reviewModel: 'DeepSeek', + ruleGroup: uniqueGroups.join('、'), + result: issueCount > 0 ? 'warning' : 'success', + issueCount: issueCount + }; + // console.log("reviewInfo-------",JSON.stringify(reviewInfo,null,2)); + + return { data: resultData, stats, reviewInfo }; } diff --git a/app/api/evaluation_points/rules-files.ts b/app/api/evaluation_points/rules-files.ts index 78df771..53a32ef 100644 --- a/app/api/evaluation_points/rules-files.ts +++ b/app/api/evaluation_points/rules-files.ts @@ -85,7 +85,7 @@ export interface DocumentSearchParams { function formatDate(dateString: string): string { if (!dateString) return ''; try { - return dayjs(dateString).format('YYYY-MM-DD HH:mm:ss'); + return dayjs(dateString).format('YYYY-MM-DD'); } catch (error) { console.error('日期格式化失败:', error); return dateString; @@ -215,7 +215,12 @@ export async function getReviewFiles(searchParams: DocumentSearchParams = {}): P case 'upload_time_asc': params.order = 'created_at.asc'; break; - // 其他排序方式可以在这里添加 + // case 'issue_count_desc': + // params.order = 'issue_count.desc'; + // break; + // case 'issue_count_asc': + // params.order = 'issue_count.asc'; + // break; } } diff --git a/app/api/files/documents.ts b/app/api/files/documents.ts index 4b7a02f..2cc0fa1 100644 --- a/app/api/files/documents.ts +++ b/app/api/files/documents.ts @@ -114,6 +114,25 @@ function getFileExtension(filename: string): string { return parts.length > 1 ? parts.pop()?.toLowerCase() || '' : ''; } +/** + * 获取评查结果 + * @param id 评查结果ID + * @returns 评查结果 + */ +async function getEvaluationResults(id: number) { + const response = await postgrestGet<[]>('evaluation_results', { + filter: { + 'document_id': `eq.${id}` + } + }); + if (response.error) { + return { error: response.error, status: response.status }; + } + const evaluationResult = extractApiData<[]>(response.data); + return evaluationResult; +} + + /** * 将API文档转换为UI文档 */ @@ -122,6 +141,24 @@ async function convertToUIDocument(doc: Document): Promise { const typeResponse = await getDocumentTypes(); const documentTypes = typeResponse.data?.types || []; const docType = documentTypes.find(type => type.id.toString() === doc.type_id.toString()); + const evaluationResult = await getEvaluationResults(doc.id); + let issues = 0; + + interface EvaluationResultItem { + evaluated_results?: { + result?: string; + [key: string]: unknown; + }; + [key: string]: unknown; + } + + if (evaluationResult && Array.isArray(evaluationResult)) { + evaluationResult.forEach((result: EvaluationResultItem) => { + if(result && result.evaluated_results && !result.evaluated_results.result){ + issues++; + } + }); + } return { id: doc.id, @@ -132,7 +169,7 @@ async function convertToUIDocument(doc: Document): Promise { size: doc.file_size, auditStatus: doc.audit_status || 0, fileStatus: doc.status || '', // 默认为'' - issues: 0, // 固定为0 + issues: issues, // 使用计算得到的issues uploadTime: formatDate(doc.updated_at), fileType: getFileExtension(doc.name), path: doc.path, diff --git a/app/api/files/files-upload.ts b/app/api/files/files-upload.ts index 8960ac5..7b54623 100644 --- a/app/api/files/files-upload.ts +++ b/app/api/files/files-upload.ts @@ -40,6 +40,7 @@ function extractApiData(responseData: unknown): T | null { // 文档状态枚举 export enum DocumentStatus { + waiting = 'waiting', WAITING = "Waiting", CUTTING = "Cutting", EXTRACTIONING = "Extractioning", @@ -175,7 +176,8 @@ export async function uploadDocumentToServer( // 发送请求 // const response = await fetch(`${API_BASE_URL}/admin/documents/upload`, { - const response = await fetch('http://172.16.0.119:8000/admin/documents/upload', { + const response = await fetch('http://172.16.0.55:8000/admin/documents/upload', { + // const response = await fetch('http://172.16.0.119:8000/admin/documents/upload', { method: 'POST', headers: { 'X-File-Name': encodeURIComponent(fileName) diff --git a/app/components/reviews/FileDetails.tsx b/app/components/reviews/FileDetails.tsx index 2ab4392..949c878 100644 --- a/app/components/reviews/FileDetails.tsx +++ b/app/components/reviews/FileDetails.tsx @@ -44,6 +44,7 @@ interface FileDetailsProps { export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsProps) { // 情况状态对应的标签 + const controlContractShow = false const renderResultBadge = (result: string) => { switch (result) { case 'success': @@ -75,11 +76,39 @@ export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsP // 渲染信息区块 const renderInfoSection = (title: string, icon: string, color: string, children: ReactNode) => { + // 根据color参数返回对应的具体类名 + const getBgClass = (colorName: string) => { + switch(colorName) { + case 'blue': return 'bg-blue-50'; + case 'green': return 'bg-green-50'; + case 'purple': return 'bg-purple-50'; + default: return 'bg-gray-50'; + } + }; + + const getTextClass = (colorName: string) => { + switch(colorName) { + case 'blue': return 'text-blue-500'; + case 'green': return 'text-green-500'; + case 'purple': return 'text-purple-500'; + default: return 'text-gray-500'; + } + }; + + const getTextTitleClass = (colorName: string) => { + switch(colorName) { + case 'blue': return 'text-blue-700'; + case 'green': return 'text-green-700'; + case 'purple': return 'text-purple-700'; + default: return 'text-gray-700'; + } + }; + return (
-
- -

{title}

+
+ +

{title}

{children} @@ -114,20 +143,22 @@ export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsP ))} {/* 合同信息 */} - {renderInfoSection('合同信息', 'ri-file-paper-2-line', 'green', ( -
- {renderInfoRow('合同类型', contractInfo.contractType)} - {renderInfoRow('签约日期', contractInfo.signDate)} - {renderInfoRow('合同当事人', ( -
-
甲方:{contractInfo.parties.partyA}
-
乙方:{contractInfo.parties.partyB}
-
- ))} - {renderInfoRow('合同金额', contractInfo.amount)} - {renderInfoRow('履行期限', contractInfo.period)} -
- ))} + { controlContractShow && ( + renderInfoSection('合同信息', 'ri-file-paper-2-line', 'green', ( +
+ {renderInfoRow('合同类型', contractInfo.contractType)} + {renderInfoRow('签约日期', contractInfo.signDate)} + {renderInfoRow('合同当事人', ( +
+
甲方:{contractInfo.parties.partyA}
+
乙方:{contractInfo.parties.partyB}
+
+ ))} + {renderInfoRow('合同金额', contractInfo.amount)} + {renderInfoRow('履行期限', contractInfo.period)} +
+ )) + )} {/* 评查信息 */} {renderInfoSection('评查信息', 'ri-search-eye-line', 'purple', ( @@ -139,7 +170,7 @@ export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsP {renderInfoRow('评查结果', (
{renderResultBadge(reviewInfo.result)} - (共发现{reviewInfo.issueCount}个问题) + {reviewInfo.issueCount > 0 && (共发现{reviewInfo.issueCount}个问题)}
))}
diff --git a/app/components/reviews/FileInfo.tsx b/app/components/reviews/FileInfo.tsx index 55abbb2..f1368de 100644 --- a/app/components/reviews/FileInfo.tsx +++ b/app/components/reviews/FileInfo.tsx @@ -26,17 +26,17 @@ export function FileInfo({ fileInfo, onConfirmResults }: FileInfoProps) {
-

+

{fileInfo.fileName} - - 合同编号:{fileInfo.contractNumber} - - {fileInfo.fileSize && ( - - {fileInfo.fileSize} | {fileInfo.fileFormat} | {fileInfo.pageCount}页 - - )}

+ + 合同编号:{fileInfo.contractNumber} + + {fileInfo.fileSize && ( + + {fileInfo.fileSize} | {fileInfo.fileFormat} | {fileInfo.pageCount}页 + + )} {fileInfo.uploadTime && (
上传时间:{fileInfo.uploadTime} | 上传用户:{fileInfo.uploadUser} diff --git a/app/components/reviews/FilePreview.tsx b/app/components/reviews/FilePreview.tsx index 1009c06..1b89c64 100644 --- a/app/components/reviews/FilePreview.tsx +++ b/app/components/reviews/FilePreview.tsx @@ -4,18 +4,8 @@ */ import { useState, useEffect, useRef } from 'react'; -// 定义评查点类型 -interface ReviewPoint { - id: string; - title: string; - status: string; - content: string; - suggestion: string; - position?: { - section: string; - index: number; - }; -} +// 导入统一的ReviewPoint类型 +import { type ReviewPoint } from './'; // 定义文档内容类型 interface FileContent { diff --git a/app/components/reviews/ReviewPointsList.tsx b/app/components/reviews/ReviewPointsList.tsx index 335ee25..7f49d87 100644 --- a/app/components/reviews/ReviewPointsList.tsx +++ b/app/components/reviews/ReviewPointsList.tsx @@ -39,6 +39,12 @@ export interface ReviewPoint { index: number; }; result?: boolean; + legalBasis?: { + name?: string; + content?: string; + articles?: Array; + [key: string]: unknown; + }; } // 统计数据类型 @@ -70,6 +76,7 @@ export function ReviewPointsList({ const [userInputText, setUserInputText] = useState(''); // 用户输入的审核意见文本 const [searchText, setSearchText] = useState(''); // 搜索文本 const [statusFilter, setStatusFilter] = useState(null); // 状态过滤 + // eslint-disable-next-line no-unused-vars const [suggestionTexts, setSuggestionTexts] = useState>({}); // 存储每个评查点的建议文本 // 初始化建议文本 @@ -83,6 +90,7 @@ export function ReviewPointsList({ }, [reviewPoints]); // 处理建议文本变更 + // eslint-disable-next-line no-unused-vars const handleSuggestionChange = (reviewPointId: string, text: string) => { setSuggestionTexts(prev => ({ ...prev, @@ -135,7 +143,7 @@ export function ReviewPointsList({ alert(`将为评查点 ${reviewPointId} 执行一键替换操作`); // 更新评查点状态为成功 - onStatusChange(reviewPointId, 'success'); + // onStatusChange(reviewPointId, 'success'); }; /** @@ -206,9 +214,17 @@ export function ReviewPointsList({
{/* 总计数量 */}
-
+
+ 总计
@@ -422,75 +438,120 @@ export function ReviewPointsList({ return (
- {/* 内容显示区域 */} -
- {/* 移除顶部的"当前值"标题,在每个内容项中显示 */} - {typeof reviewPoint.content === 'object' && reviewPoint.content !== null ? ( - // 当 content 是对象时的渲染方式 -
- {Object.entries(reviewPoint.content).map(([key, value], index) => ( -
- {/* 使用flex布局使key和状态标签左右对齐 */} + {reviewPoint.content !== null && ( + (typeof reviewPoint.content === 'string' && reviewPoint.content !== '') || + (typeof reviewPoint.content === 'object' && Object.keys(reviewPoint.content).length > 0) + ) && ( + <> + {/* 内容显示区域 */} +
+ {/* 移除顶部的"当前值"标题,在每个内容项中显示 */} + {typeof reviewPoint.content === 'object' && reviewPoint.content !== null ? ( + // 当 content 是对象时的渲染方式 +
+ {Object.entries(reviewPoint.content).map(([key, value], index) => ( +
+ {/* 使用flex布局使key和状态标签左右对齐 */} +
+ {key} + {/* + {isErrorStatus ? '不符合规范' : '需优化'} + */} + + {value ? '' : '缺失'} + +
+

{value || ' '}

+
+ ))} +
+ ) : ( + // 当 content 是字符串时的渲染方式 + <> + {/* 为字符串内容也添加标题和状态 */}
- {key} + 当前值 {isErrorStatus ? '不符合规范' : '需优化'}
-

{value || '(内容为空)'}

-
- ))} +

{reviewPoint.content || '(内容为空)'}

+ + )}
- ) : ( - // 当 content 是字符串时的渲染方式 - <> - {/* 为字符串内容也添加标题和状态 */} -
- 当前值 - - {isErrorStatus ? '不符合规范' : '需优化'} - + + {/* 法律依据内容 */} + {reviewPoint.legalBasis && (typeof reviewPoint.legalBasis === 'object') && ( + (reviewPoint.legalBasis.name || reviewPoint.legalBasis.content || + (reviewPoint.legalBasis.articles && Array.isArray(reviewPoint.legalBasis.articles) && reviewPoint.legalBasis.articles.length > 0)) && ( +
+
+ 法律依据 +
+ {reviewPoint.legalBasis.name && ( +

{reviewPoint.legalBasis.name}

+ )} + {reviewPoint.legalBasis.content && ( +

条款内容:{reviewPoint.legalBasis.content}

+ )} + {reviewPoint.legalBasis.articles && Array.isArray(reviewPoint.legalBasis.articles) && reviewPoint.legalBasis.articles.length > 0 && ( +
+

相关条款:

+
    + {reviewPoint.legalBasis.articles.map((item, index) => ( +
  • + {typeof item === 'string' ? item : + typeof item === 'object' && item !== null ? + (item.name ? `${item.name}: ${item.content || ''}` : + item.content || JSON.stringify(item)) : + String(item)} +
  • + ))} +
+
+ )} +
+ ) + )} + + {/* 建议修改区域 */} +
+
+ 建议修改为: + {/* 符合规范 */}
-

{reviewPoint.content || '(内容为空)'}

- - )} -
+