删除所有console.log输出,优化评查结果的表格的显示,添加新的页码获取逻辑

This commit is contained in:
2025-06-02 18:55:00 +08:00
parent 820baa5b22
commit b02978508d
71 changed files with 862 additions and 572 deletions
+53
View File
@@ -1,3 +1,56 @@
# 智慧法务系统 - 权限认证系统
## 认证系统概述
本系统实现了一个简单的基于角色的权限认证系统,支持以下功能:
1. 用户可以以不同角色登录系统:
- `common`:普通用户,只能访问基本功能
- `developer`:开发者,可以访问所有功能,包括系统设置
2. 权限控制:
- 根据用户角色显示或隐藏菜单项
- 防止通过URL直接访问未授权页面
## 技术实现
权限系统基于以下核心技术实现:
1. **Cookie会话存储**:使用Remix的`createCookieSessionStorage`创建基于Cookie的会话存储
2. **角色权限控制**:将用户角色存储在会话中,并在全局loader中检查访问权限
3. **界面适配**:根据用户角色动态过滤显示菜单项
## 使用方法
### 登录系统
1. 访问登录页面(`/login`)
2. 输入用户名和密码
3. 选择用户角色:
- **普通用户**:只能看到基础功能菜单
- **开发者**:可以看到所有菜单,包括系统设置
### 权限验证
系统会自动执行以下权限验证:
1. 未登录用户会被重定向到登录页面
2. 普通用户试图访问系统设置等受限页面会被重定向到首页
3. 侧边栏菜单会根据用户角色动态显示
## 关键文件
- `app/root.tsx`:实现会话管理和全局权限检查
- `app/components/layout/Sidebar.tsx`:根据用户角色显示不同菜单
- `app/routes/login.tsx`:提供角色选择功能
## 进一步优化建议
1. 实现真实的用户认证系统,如接入数据库验证用户
2. 添加更细粒度的权限控制
3. 实现用户角色和权限的管理界面
4. 使用环境变量管理会话密钥等敏感信息
# Welcome to Remix!
- 📖 [Remix docs](https://remix.run/docs)
+7 -7
View File
@@ -98,7 +98,7 @@ function buildUrl(endpoint: string, params?: QueryParams): string {
* 获取模拟响应数据
*/
function getMockResponse<T>(endpoint: string): ApiResponse<T> {
console.log(`[开发模式] 使用模拟数据: ${endpoint}`);
// console.log(`[开发模式] 使用模拟数据: ${endpoint}`);
// 移除开头的斜杠以便于匹配
const path = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
@@ -176,7 +176,7 @@ export async function apiRequest<T>(
// 针对 PostgREST 的额外处理
if (endpoint.includes('evaluation_point_groups') && (options.method === 'POST' || options.method === 'PATCH')) {
console.log('使用 PostgREST 特定配置处理请求');
// console.log('使用 PostgREST 特定配置处理请求');
// 确保请求体是有效的 JSON 对象
if (options.body && typeof options.body === 'string') {
try {
@@ -188,15 +188,15 @@ export async function apiRequest<T>(
}
}
console.log(`📦 axios-client.ts->请求URL: ${decodeUrlForDisplay(url)}`);
console.log(`axios-client.ts->发送 ${options.method || 'GET'} 请求到: ${decodeUrlForDisplay(url)}`);
// console.log(`📦 axios-client.ts->请求URL: ${decodeUrlForDisplay(url)}`);
// console.log(`axios-client.ts->发送 ${options.method || 'GET'} 请求到: ${decodeUrlForDisplay(url)}`);
// 处理body参数,转换为data
if (options.body) {
console.log(`axios-client.ts->请求体: \n${options.body}`);
// console.log(`axios-client.ts->请求体: \n${options.body}`);
options.data = options.body;
} else if (options.data) {
console.log(`axios-client.ts->请求体: \n${typeof options.data === 'string' ? options.data : JSON.stringify(options.data)}`);
// console.log(`axios-client.ts->请求体: \n${typeof options.data === 'string' ? options.data : JSON.stringify(options.data)}`);
}
// 发送请求
@@ -344,7 +344,7 @@ export async function downloadFile(path: string): Promise<Blob> {
const downloadUrl = `${DOCUMENT_URL}${path}`;
try {
console.log(`📦 axios-client.ts->下载文件: ${decodeUrlForDisplay(downloadUrl)}`);
// console.log(`📦 axios-client.ts->下载文件: ${decodeUrlForDisplay(downloadUrl)}`);
const response = await axios.get(downloadUrl, {
responseType: 'blob'
});
+6 -6
View File
@@ -90,7 +90,7 @@ const fetchWithTimeout = async (url: string, options: RequestInit, timeout = 500
const id = setTimeout(() => controller.abort(), timeout);
try {
console.log(`📦 client.ts->请求URL: ${decodeUrlForDisplay(url)}`);
// console.log(`📦 client.ts->请求URL: ${decodeUrlForDisplay(url)}`);
const response = await fetch(url, {
...options,
signal: controller.signal
@@ -114,7 +114,7 @@ const fetchWithTimeout = async (url: string, options: RequestInit, timeout = 500
* 获取模拟响应数据
*/
function getMockResponse<T>(endpoint: string): ApiResponse<T> {
console.log(`[开发模式] 使用模拟数据: ${endpoint}`);
// console.log(`[开发模式] 使用模拟数据: ${endpoint}`);
// 移除开头的斜杠以便于匹配
const path = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
@@ -185,7 +185,7 @@ export async function apiRequest<T>(
// 针对 PostgREST 的额外处理
if (endpoint.includes('evaluation_point_groups') && (options.method === 'POST' || options.method === 'PATCH')) {
console.log('使用 PostgREST 特定配置处理请求');
// console.log('使用 PostgREST 特定配置处理请求');
// 确保请求体是有效的 JSON 对象
if (options.body && typeof options.body === 'string') {
try {
@@ -197,9 +197,9 @@ export async function apiRequest<T>(
}
}
console.log(`client.ts->发送 ${options.method || 'GET'} 请求到: ${decodeUrlForDisplay(url)}`);
// console.log(`client.ts->发送 ${options.method || 'GET'} 请求到: ${decodeUrlForDisplay(url)}`);
if (options.body) {
console.log(`client.ts->请求体: \n${options.body}`);
// console.log(`client.ts->请求体: \n${options.body}`);
}
// 发送请求,10秒超时
@@ -226,7 +226,7 @@ export async function apiRequest<T>(
}
} catch (e) {
console.error('解析响应失败:', e);
console.log('原始响应:', responseText);
// console.log('原始响应:', responseText);
}
// 收集响应头信息
+3 -3
View File
@@ -213,7 +213,7 @@ export async function getContractTemplates(searchParams: TemplateSearchParams =
if (categoryResponse.data) {
const categories = extractApiData<ContractCategory[]>(categoryResponse.data) || [];
matchingCategoryIds = categories.map(cat => cat.id);
console.log('匹配的分类ID:', matchingCategoryIds);
// console.log('匹配的分类ID:', matchingCategoryIds);
}
} catch (error) {
console.error('查询分类失败:', error);
@@ -229,8 +229,8 @@ export async function getContractTemplates(searchParams: TemplateSearchParams =
];
params.or = `(${searchConditions.join(',')})`;
console.log('搜索关键词:', cleanKeyword);
console.log('搜索条件:', params.or);
// console.log('搜索关键词:', cleanKeyword);
// console.log('搜索条件:', params.or);
}
// 如果有分类名称,需要先获取分类ID
+6 -6
View File
@@ -251,7 +251,7 @@ export async function getDocumentTypes(searchParams: DocumentTypeSearchParams =
params.filter = filter;
console.log('获取文档类型列表,参数:', params);
// console.log('获取文档类型列表,参数:', params);
const response = await postgrestGet<DocumentType[]>('document_types', params);
if (response.error) {
@@ -286,7 +286,7 @@ export async function getDocumentTypes(searchParams: DocumentTypeSearchParams =
groupIds = [];
}
console.log(`文档类型 ${type.id} 的分组IDs:`, groupIds);
// console.log(`文档类型 ${type.id} 的分组IDs:`, groupIds);
// 获取这些ID对应的分组信息
const groupsResponse = await getEvaluationPointGroupsByIds(groupIds);
@@ -486,7 +486,7 @@ export async function getDocumentType(id: string): Promise<{
groupIds = [];
}
console.log(`文档类型 ${id} 的分组IDs:`, groupIds);
// console.log(`文档类型 ${id} 的分组IDs:`, groupIds);
const groupsResponse = await getEvaluationPointGroupsByIds(groupIds);
@@ -606,7 +606,7 @@ export async function createDocumentType(documentType: DocumentTypeCreateDTO): P
return { error: response.error, status: response.status };
}
console.log('创建文档类型响应数据:', JSON.stringify(response.data, null, 2));
// console.log('创建文档类型响应数据:', JSON.stringify(response.data, null, 2));
// 处理响应数据
const newDocumentType = extractApiData<DocumentType>(response.data);
@@ -712,7 +712,7 @@ export async function updateDocumentType(id: string, documentType: DocumentTypeU
prompt_config: promptConfig
};
console.log('更新文档类型请求数据:', JSON.stringify(apiDocumentType, null, 2));
// console.log('更新文档类型请求数据:', JSON.stringify(apiDocumentType, null, 2));
// throw new Error('测试错误');
// 发送更新请求
const response = await postgrestPut<DocumentType, typeof apiDocumentType>(
@@ -726,7 +726,7 @@ export async function updateDocumentType(id: string, documentType: DocumentTypeU
return { error: response.error, status: response.status };
}
console.log('更新文档类型响应数据:', JSON.stringify(response.data, null, 2));
// console.log('更新文档类型响应数据:', JSON.stringify(response.data, null, 2));
// 处理响应数据
const updatedDocumentType = extractApiData<DocumentType>(response.data);
+21 -20
View File
@@ -1,5 +1,6 @@
import { postgrestGet, type PostgrestParams, postgrestPut, postgrestPost } from "../postgrest-client";
import {getDocument} from "~/api/files/documents";
import dayjs from "dayjs";
import { formatDate } from "~/utils";
/**
@@ -344,22 +345,22 @@ export async function getReviewPoints(fileId: string) {
evaluatedPointResultsLog: evaluatedPointResultsLog || {}
// evaluatedPointResultsLog: {
// rules:[
// {
// "id": "0",
// "type": "consistency",
// "res": true,
// "config": {
// "logic": "all",
// "pairs": [
// {
// "sourceField": {"证据先行登记保存批准书-负责人意见并签名-时间": {page: 1,value: ''}},
// "targetField": {"证据先行登记保存批准书-负责人意见并签名-签名": {page: 2,value: '有无判断类型'}},
// "compareMethod": "exact",
// "res": true
// }
// ]
// }
// },
// {
// "id": "0",
// "type": "consistency",
// "res": true,
// "config": {
// "logic": "all",
// "pairs": [
// {
// "sourceField": {"证据先行登记保存批准书-负责人意见并签名-时间": {page: 1,value: ''}},
// "targetField": {"证据先行登记保存批准书-负责人意见并签名-签名": {page: 2,value: '有无判断类型'}},
// "compareMethod": "exact",
// "res": true
// }
// ]
// }
// },
// {
// "id": "1",
// "type": "consistency",
@@ -611,7 +612,7 @@ export async function getReviewPoints(fileId: string) {
// 构建评查信息对象
const reviewInfo = {
reviewTime: formatDate(latestUpdatedAt),
reviewTime: dayjs.utc(latestUpdatedAt).format('YYYY-MM-DD HH:mm:ss'),
reviewModel: 'DeepSeek',
ruleGroup: uniqueGroups.join('、'),
result: issueCount > 0 ? 'warning' : 'success',
@@ -661,7 +662,7 @@ export async function updateReviewResult(resultId: string, editAuditStatusId: st
// 判断是否是重新审核操作
const isReview = result === 'review';
console.log('isReview-------', result);
// console.log('isReview-------', result);
// 构建要更新的数据,保留原有字段
const updatedEvaluatedResults = {
@@ -689,8 +690,8 @@ export async function updateReviewResult(resultId: string, editAuditStatusId: st
// 确定edit_audit_status的值:
// 如果是重新审核操作,值为0;否则值为1
const editAuditStatusValue = isReview ? 0 : 1;
console.log('editAuditStatusValue-------', editAuditStatusValue);
console.log('editAuditStatusId-------', editAuditStatusId);
// console.log('editAuditStatusValue-------', editAuditStatusValue);
// console.log('editAuditStatusId-------', editAuditStatusId);
if (editAuditStatusId && editAuditStatusId !== '') {
// 更新现有审核状态记录
const auditStatusResponse = await postgrestPut(
+2 -2
View File
@@ -442,7 +442,7 @@ export async function createRuleGroup(groupData: RuleGroupCreateUpdateDto): Prom
is_enabled: groupData.is_enabled
};
console.log('创建评查点分组请求数据:', JSON.stringify(apiGroup, null, 2));
// console.log('创建评查点分组请求数据:', JSON.stringify(apiGroup, null, 2));
// 直接发送到 PostgreSQL 表
const response = await postgrestPost<ApiResponse<ApiRuleGroup> | ApiRuleGroup, ApiRuleGroup>(
@@ -455,7 +455,7 @@ export async function createRuleGroup(groupData: RuleGroupCreateUpdateDto): Prom
return { error: response.error, status: response.status };
}
console.log('创建评查点分组响应数据:', JSON.stringify(response.data, null, 2));
// console.log('创建评查点分组响应数据:', JSON.stringify(response.data, null, 2));
// 处理响应数据 - 适配不同的API响应格式
const apiResponse = extractApiData<ApiRuleGroup>(response.data);
+1 -1
View File
@@ -313,7 +313,7 @@ export async function getReviewFiles(searchParams: DocumentSearchParams = {}): P
// }
params.filter = filter;
console.log('params-----',params);
// console.log('params-----',params);
// 发送API请求获取文档列表
const response = await postgrestGet<Document[]>('documents', params);
+10 -10
View File
@@ -141,7 +141,7 @@ function mapApiRuleToFrontendModel(apiRule: ApiRule): Rule {
priority: priorityMap[apiRule.risk] || 'medium',
description: apiRule.description || '',
isActive: apiRule.is_enabled === undefined ? false : apiRule.is_enabled,
createdAt: apiRule.created_at ,
createdAt: apiRule.created_at,
updatedAt: apiRule.updated_at
};
}
@@ -365,7 +365,7 @@ export async function getRulesList(params: RulesQueryParams): Promise<{data: Rul
// 将API返回的数据映射到前端模型,并附加分组名称
console.log("groupsMap",groupsMap);
// console.log("groupsMap",groupsMap);
const mappedRules = filteredRules.map(apiRule => {
// 创建修改版的apiRule,添加从分组映射获取的名称
const enhancedApiRule = {
@@ -620,7 +620,7 @@ export async function updateRule(id: string, ruleData: Partial<Omit<Rule, 'id' |
*/
export async function deleteRule(id: string): Promise<{data: Rule; error?: never} | {data?: never; error: string; status?: number}> {
try {
console.log(`开始删除评查点, ID: ${id}`);
// console.log(`开始删除评查点, ID: ${id}`);
// 使用 PostgREST 语法,通过查询参数指定要删除的行
const postgrestParams: PostgrestParams = {
@@ -635,7 +635,7 @@ export async function deleteRule(id: string): Promise<{data: Rule; error?: never
// 使用postgrestDelete删除评查点
const response = await postgrestDelete('evaluation_points', postgrestParams);
console.log('删除请求响应:', JSON.stringify(response, null, 2));
// console.log('删除请求响应:', JSON.stringify(response, null, 2));
// 检查是否有错误响应
if (response.error) {
@@ -842,7 +842,7 @@ export async function getRuleTypes(): Promise<{data: RuleType[]; error?: never}
return { error: '9000接口返回数据格式不正确', status: 500 };
}
}else if(Array.isArray(response.data) && response.data.length > 0){
console.log("评查点类型列表",response.data);
// console.log("评查点类型列表",response.data);
const ruleTypes = response.data.map(item => ({
id: item.id.toString(),
pid: item.pid.toString(),
@@ -924,7 +924,7 @@ export async function getRuleGroupsByType(typeId: string): Promise<{data: RuleGr
return { error: '9000接口返回数据格式不正确', status: 500 };
}
}else if(Array.isArray(response.data) && response.data.length > 0){
console.log("评查点类型列表",response.data);
// console.log("评查点类型列表",response.data);
const ruleGroups = response.data.map(item => ({
id: item.id.toString(),
name: item.name,
@@ -1080,7 +1080,7 @@ export async function getEvaluationPoint(id: number): Promise<{
status?: number;
}> {
try {
console.log(`获取评查点数据,ID: ${id}`);
// console.log(`获取评查点数据,ID: ${id}`);
// 使用 postgrestGet 替代直接调用 fetch
const postgrestParams: PostgrestParams = {
@@ -1140,7 +1140,7 @@ export async function getEvaluationPointGroups(): Promise<{
status?: number;
}> {
try {
console.log("获取评查点组数据");
// console.log("获取评查点组数据");
// 使用 postgrestGet 替代直接调用 fetch
const postgrestParams: PostgrestParams = {
@@ -1256,7 +1256,7 @@ export async function saveEvaluationPoint(evaluationPoint: EvaluationPointInput,
status?: number;
}> {
try {
console.log(`${isEditMode ? '更新' : '创建'}评查点数据`);
// console.log(`${isEditMode ? '更新' : '创建'}评查点数据`);
// 创建一个符合数据库模式的数据副本
const cleanedData = {
@@ -1393,7 +1393,7 @@ export async function saveEvaluationPoint(evaluationPoint: EvaluationPointInput,
});
}
console.log("准备发送到API的数据大小:", JSON.stringify(cleanedData).length, "字节");
// console.log("准备发送到API的数据大小:", JSON.stringify(cleanedData).length, "字节");
// 使用 postgrest-client 替代直接 fetch 调用
let response;
+1 -1
View File
@@ -448,7 +448,7 @@ export async function updateDocument(id: string, document: Partial<DocumentUI> &
apiDocument.remark = document.remark;
}
console.log('更新文档API数据:', apiDocument);
// console.log('更新文档API数据:', apiDocument);
const response = await postgrestPut<Document, Partial<Document>>(
'documents',
+11 -11
View File
@@ -131,7 +131,7 @@ export async function uploadDocumentToServer(
isTestDocument: boolean = false
): Promise<{data: FileUploadResponse; error?: never} | {data?: never; error: string; status?: number}> {
try {
console.log('【调试】开始上传文档:', { fileName, fileSize: binaryData.byteLength });
// console.log('【调试】开始上传文档:', { fileName, fileSize: binaryData.byteLength });
// 创建FormData对象
const formData = new FormData();
@@ -139,7 +139,7 @@ export async function uploadDocumentToServer(
// 将二进制数据转换为Blob并添加到FormData
const blob = new Blob([binaryData], { type: fileType });
formData.append('file', blob, fileName);
console.log('【调试】Blob已创建,文件大小:', blob.size);
// console.log('【调试】Blob已创建,文件大小:', blob.size);
// 将信息添加到一个JSON对象中
const uploadInfo = {
@@ -152,14 +152,14 @@ export async function uploadDocumentToServer(
// 添加JSON字符串到FormData
formData.append('upload_info', JSON.stringify(uploadInfo));
console.log('【调试】FormData准备完成:', JSON.stringify(uploadInfo));
// console.log('【调试】FormData准备完成:', JSON.stringify(uploadInfo));
console.log('【调试】准备发送请求到服务器:', 'http://172.16.0.58:8008/admin/documents/upload');
// console.log('【调试】准备发送请求到服务器:', 'http://172.16.0.58:8008/admin/documents/upload');
// 发送请求
// const response = await fetch(`${API_BASE_URL}/admin/documents/upload`, {
try {
console.log('【调试】开始fetch请求...');
// console.log('【调试】开始fetch请求...');
const response = await fetch('http://172.16.0.58:8008/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', {
@@ -170,7 +170,7 @@ export async function uploadDocumentToServer(
body: formData
});
console.log('【调试】收到服务器响应:', { status: response.status, statusText: response.statusText });
// console.log('【调试】收到服务器响应:', { status: response.status, statusText: response.statusText });
if (!response.ok) {
const errorText = await response.text();
@@ -181,11 +181,11 @@ export async function uploadDocumentToServer(
};
}
console.log('【调试】开始解析JSON响应');
// console.log('【调试】开始解析JSON响应');
let responseData;
try {
responseData = await response.json();
console.log('【调试】JSON响应解析成功:', responseData);
// console.log('【调试】JSON响应解析成功:', responseData);
} catch (jsonError) {
console.error('【调试】JSON解析失败:', jsonError);
return {
@@ -195,14 +195,14 @@ export async function uploadDocumentToServer(
}
const extractedData = extractApiData<FileUploadResponse>(responseData);
console.log('【调试】提取的数据:', extractedData);
// console.log('【调试】提取的数据:', extractedData);
if (!extractedData) {
console.error('【调试】无法提取数据');
return { error: '处理上传响应失败', status: 500 };
}
console.log('【调试】上传成功,返回数据');
// console.log('【调试】上传成功,返回数据');
return { data: extractedData };
} catch (fetchError) {
console.error('【调试】fetch请求失败:', fetchError);
@@ -227,7 +227,7 @@ export async function uploadDocumentToServer(
export async function getTodayDocuments(): Promise<{data: Document[]; error?: never} | {data?: never; error: string; status?: number}> {
try {
const today = dayjs().startOf('day').format('YYYY-MM-DD');
console.log('查询当天文档,日期范围:', today);
// console.log('查询当天文档,日期范围:', today);
const params: PostgrestParams = {
select: `
+3 -3
View File
@@ -225,7 +225,7 @@ export async function getHomeData(): Promise<HomeStatistics> {
? parseFloat(((lastMonthTotal / lastMonthReviewed) * 100).toFixed(1))
: 0;
console.log('上个月-------', lastMonthPassRate);
// console.log('上个月-------', lastMonthPassRate);
// 计算通过率同比增长
let passRateGrowthValue = 0;
@@ -242,8 +242,8 @@ export async function getHomeData(): Promise<HomeStatistics> {
passRateGrowthIsUp = true;
}
console.log('上月通过率-------', lastMonthPassRate);
console.log('本月通过率-------', monthlyPassRate);
// console.log('上月通过率-------', lastMonthPassRate);
// console.log('本月通过率-------', monthlyPassRate);
// 4. 检查出的问题总数(从评估结果表中统计)
const thisMonthIssuesParams: PostgrestParams = {
+16 -16
View File
@@ -55,22 +55,22 @@ function logPostgrestQuery(endpoint: string, params?: QueryParams, method: strin
// 确保 endpoint 格式正确
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
console.log('\n📦 PostgREST 查询日志 ======================start=============');
console.log(`📦 HTTP 方法: ${method}`);
console.log(`📦 API 端点: ${decodeUrlForDisplay(`${baseUrl}/${normalizedEndpoint}`)}`);
// console.log('\n📦 PostgREST 查询日志 ======================start=============');
// console.log(`📦 HTTP 方法: ${method}`);
// console.log(`📦 API 端点: ${decodeUrlForDisplay(`${baseUrl}/${normalizedEndpoint}`)}`);
if (params && Object.keys(params).length > 0) {
console.log('📦 查询参数:');
// if (params && Object.keys(params).length > 0) {
// console.log('📦 查询参数:');
// 以可读格式单独打印每个参数
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
console.log(` - ${key}: ${JSON.stringify(value)}`);
}
});
}
// // 以可读格式单独打印每个参数
// Object.entries(params).forEach(([key, value]) => {
// if (value !== undefined) {
// console.log(` - ${key}: ${JSON.stringify(value)}`);
// }
// });
// }
console.log('PostgREST 查询日志=============================end============\n');
// console.log('PostgREST 查询日志=============================end============\n');
}
}
@@ -305,8 +305,8 @@ export async function postgrestPost<T, D = Record<string, unknown>>(endpoint: st
// 确保数据是合法的JSON对象
const requestBody = JSON.stringify(processedData);
console.log(`准备发送 PostgreSQL 插入请求到: ${apiEndpoint}`);
console.log(`请求体: ${requestBody}`);
// console.log(`准备发送 PostgreSQL 插入请求到: ${apiEndpoint}`);
// console.log(`请求体: ${requestBody}`);
try {
const response = await apiRequest<T>(
@@ -327,7 +327,7 @@ export async function postgrestPost<T, D = Record<string, unknown>>(endpoint: st
throw new Error(response.error);
}
console.log(`POST请求成功,响应: `, response.data);
// console.log(`POST请求成功,响应: `, response.data);
return { data: response.data as T };
} catch (error) {
// 捕获并处理 API 请求错误
+3 -3
View File
@@ -121,7 +121,7 @@ export async function getPromptTemplates(searchParams: PromptSearchParams = {}):
status?: number;
}> {
try {
console.log('获取提示词模板列表,参数:', searchParams);
// console.log('获取提示词模板列表,参数:', searchParams);
const page = searchParams.page || 1;
const pageSize = searchParams.pageSize || 10;
@@ -177,7 +177,7 @@ export async function getPromptTemplates(searchParams: PromptSearchParams = {}):
params.filter = filter;
// 发送API请求
console.log('API请求参数:', params);
// console.log('API请求参数:', params);
const response = await postgrestGet<PromptTemplate[]>('prompt_templates', params);
if (response.error) {
@@ -320,7 +320,7 @@ export async function createPromptTemplate(template: Partial<PromptTemplateUI>):
};
if(apiTemplate){
console.log('apiTemplate', apiTemplate);
// console.log('apiTemplate', apiTemplate);
// throw new Error('测试错误');
}
@@ -60,7 +60,7 @@ export function TemplateCard({ template, onClick }: TemplateCardProps) {
link.click();
document.body.removeChild(link);
console.log('开始下载文件:', cleanFileName);
// console.log('开始下载文件:', cleanFileName);
} catch (error) {
console.error('下载文件失败:', error);
alert('下载失败,请稍后重试');
@@ -86,7 +86,7 @@ export function TemplateCard({ template, onClick }: TemplateCardProps) {
navigate(`/contract-template/detail/${template.id}`);
break;
default:
console.log(`执行操作: ${action}`, template.id);
// console.log(`执行操作: ${action}`, template.id);
}
};
+2 -2
View File
@@ -22,8 +22,8 @@ export function ErrorBoundary({ status, statusText, message }: ErrorBoundaryProp
</Link>
</div>
<div className="mt-12 text-center">
<p className="text-sm text-gray-500"></p>
<p className="text-sm text-gray-500 mt-1">support@tobacco-ai-system.com</p>
<p className="text-sm text-gray-500"></p>
{/* <p className="text-sm text-gray-500 mt-1">技术支持:support@tobacco-ai-system.com</p> */}
</div>
</div>
);
+4 -1
View File
@@ -3,9 +3,11 @@ import { Sidebar } from './Sidebar';
// import { Header } from './Header';
import { Breadcrumb } from './Breadcrumb';
import { useMatches, useLocation } from '@remix-run/react';
import type { UserRole } from '~/root';
interface LayoutProps {
children: React.ReactNode;
userRole?: UserRole;
}
// 添加一个接口表示路由handle可能包含的属性
@@ -20,7 +22,7 @@ interface Match {
data: unknown;
}
export function Layout({ children }: LayoutProps) {
export function Layout({ children, userRole = 'developer' }: LayoutProps) {
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const matches = useMatches() as Match[];
const location = useLocation();
@@ -58,6 +60,7 @@ export function Layout({ children }: LayoutProps) {
<Sidebar
collapsed={sidebarCollapsed}
onToggle={toggleSidebar}
userRole={userRole}
/>
<div className={`main-content ${sidebarCollapsed ? 'sidebar-collapsed' : ''}`}>
+32 -14
View File
@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react';
import { Link, useLocation } from '@remix-run/react';
import type { UserRole } from '~/root';
interface MenuItem {
id: string;
@@ -7,15 +8,17 @@ interface MenuItem {
path: string;
icon: string;
hideBreadcrumb?: boolean;
requiredRole?: UserRole;
children?: MenuItem[];
}
interface SidebarProps {
onToggle: () => void;
collapsed: boolean;
userRole: UserRole;
}
export function Sidebar({ onToggle, collapsed }: SidebarProps) {
export function Sidebar({ onToggle, collapsed, userRole }: SidebarProps) {
const location = useLocation();
const [expandedMenus, setExpandedMenus] = useState<Record<string, boolean>>({});
@@ -109,12 +112,14 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
title: '系统设置',
path: '/settings',
icon: 'ri-settings-4-line',
requiredRole: 'developer',
children: [
{
id: 'config-lists',
title: '配置列表',
path: '/config-lists',
icon: 'ri-list-check-3'
icon: 'ri-list-check-3',
requiredRole: 'developer'
},
// {
// id: 'basic-settings',
@@ -126,13 +131,15 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
id: 'document-types',
title: '文档类型',
path: '/document-types',
icon: 'ri-file-list-line'
icon: 'ri-file-list-line',
requiredRole: 'developer'
},
{
id: 'prompt-management',
title: '提示词管理',
path: '/prompts',
icon: 'ri-chat-1-line'
icon: 'ri-chat-1-line',
requiredRole: 'developer'
}
]
}
@@ -150,11 +157,10 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
}, []);
const toggleMenu = (id: string, e: React.MouseEvent) => {
// 防止事件冒泡默认行为
e.preventDefault();
// 我们只防止事件冒泡,不阻止默认行为
e.stopPropagation();
// console.log('%c父菜单展开/折叠 ===> ', 'background: #f5222d; color: white; padding: 2px 4px; border-radius: 2px;', id);
// console.log('父菜单展开/折叠:', id);
setExpandedMenus(prev => ({
...prev,
@@ -168,18 +174,27 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
// 处理侧边栏切换事件
const handleToggleSidebar = (e: React.MouseEvent) => {
// console.log('%c侧边栏折叠/展开 ===> ', 'background: #1890ff; color: white; padding: 2px 4px; border-radius: 2px;');
e.preventDefault();
// console.log('侧边栏折叠/展开');
// 只防止事件冒泡,不阻止默认行为
e.stopPropagation();
onToggle();
};
// 处理子菜单项点击事件
const handleSubMenuClick = (child: MenuItem, e: React.MouseEvent) => {
// 需要阻止冒泡,否则会触发父级菜单的展开/折叠事件
// 需要阻止冒泡,不阻止默认行为
e.stopPropagation();
// console.log('%c子菜单点击 ===> ', 'background: #00684a; color: white; padding: 2px 4px; border-radius: 2px;', child.title, '路径:', child.path);
// console.log('子菜单点击:', child.title, '路径:', child.path);
};
// 根据用户角色过滤菜单项
const filteredMenuItems = menuItems.filter(item => {
// 如果菜单项需要特定角色但用户没有
if (item.requiredRole && item.requiredRole !== userRole) {
return false;
}
return true;
});
return (
<div className={`sidebar ${collapsed ? 'collapsed' : ''}`}>
@@ -211,15 +226,16 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
)} */}
<div className="py-4 px-[10px]">
{menuItems.map((item) => (
{filteredMenuItems.map((item) => (
<div key={item.id} className={`${collapsed ? 'px-0' : ''}`}>
{!item.children ? (
<Link
to={item.path}
className={`sidebar-menu-item ${isActive(item.path) ? 'active' : ''} flex items-center ${collapsed ? 'justify-center' : ''}`}
onClick={(e) => {
// 只阻止冒泡,不阻止默认行为
e.stopPropagation();
// console.log('%c单级菜单点击 ===> ', 'background: #52c41a; color: white; padding: 2px 4px; border-radius: 2px;', item.title, '路径:', item.path);
// console.log('单级菜单点击:', item.title, '路径:', item.path);
}}
>
<i className={`${item.icon} ${collapsed ? 'text-xl' : 'mr-3'}`}></i>
@@ -258,7 +274,9 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) {
className={`submenu-container ${collapsed ? 'border-l-0 pl-0' : 'border-l border-gray-100 ml-4 pl-3'} z-20`}
id={`submenu-${item.id}`}
>
{item.children.map((child) => (
{item.children
.filter(child => !child.requiredRole || child.requiredRole === userRole)
.map((child) => (
<Link
key={child.id}
to={child.path}
+1 -1
View File
@@ -139,7 +139,7 @@ export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsP
{renderInfoRow('文件格式', fileInfo.fileFormat)}
{renderInfoRow('页数', `${fileInfo.pageCount}`)}
{renderInfoRow('上传时间', fileInfo.uploadTime)}
{renderInfoRow('上传用户', fileInfo.uploadUser)}
{/* {renderInfoRow('上传用户', fileInfo.uploadUser)} */}
</div>
))}
+2 -2
View File
@@ -203,7 +203,7 @@ export function FilePreview({ fileContent, activeReviewPointResultId, targetPage
const pageElement = document.getElementById(pageElementId);
if (pageElement) {
console.log(`跳转到第${newTargetPage}页,对应评查点结果ID: ${activeReviewPointResultId}`);
// console.log(`跳转到第${newTargetPage}页,对应评查点结果ID: ${activeReviewPointResultId}`);
pageElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
} else {
console.warn(`未找到页面元素: ${pageElementId}`);
@@ -262,7 +262,7 @@ export function FilePreview({ fileContent, activeReviewPointResultId, targetPage
// PDF文档加载成功回调函数
function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
setNumPages(numPages);
console.log("PDF加载成功,页数:", numPages);
// console.log("PDF加载成功,页数:", numPages);
}
// 计算页面在缩放后的实际间距
+119 -39
View File
@@ -131,13 +131,6 @@ interface Statistics {
score: number;
}
// 统一规则的类型
// interface pointRule {
// id: string;
// type: string;
// config: Record<string, unknown>;
// }
interface ReviewPointsListProps {
reviewPoints: ReviewPoint[];
statistics: Statistics;
@@ -240,11 +233,14 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
const [showTooltip, setShowTooltip] = useState(false);
const textRef = useRef<HTMLDivElement>(null);
const isTableLike = content.includes('\t') && content.includes('\n');
useEffect(() => {
const checkTextOverflow = () => {
const element = textRef.current;
if (element) {
setShowTooltip(element.scrollHeight > element.clientHeight);
// 如果是表格格式,总是显示tooltip;否则只在文本溢出时显示
setShowTooltip(isTableLike || element.scrollHeight > element.clientHeight);
}
};
@@ -253,7 +249,7 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
return () => {
window.removeEventListener('resize', checkTextOverflow);
};
}, [content]);
}, [content, isTableLike]);
// 解析表格数据
const parseTableData = (text: string) => {
@@ -268,7 +264,7 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
const hasHeader = tableData.length > 0;
return (
<div className="overflow-auto max-h-[400px]">
<div>
<table className="min-w-full border-collapse border border-gray-300">
{hasHeader && (
<thead>
@@ -307,8 +303,7 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
}
};
// 检测内容是否像表格
const isTableLike = content.includes('\t') && content.includes('\n');
return (
<div className="text-xs p-1 rounded cursor-text w-full text-left">
@@ -321,6 +316,8 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
showArrow={true}
className="tooltip-custom-offset"
// fixedPlacement={true}
// scrollable={true}
maxHeight={400}
>
<div className="text-gray-800 break-all overflow-hidden line-clamp-2" ref={textRef}>
{content}
@@ -352,8 +349,8 @@ export function ReviewPointsList({
// 添加重新审核意见的状态/ 用户输入的修改内容 / 用户提前写好的修改内容
const [manualReviewNotes, setManualReviewNotes] = useState<Record<string, string>>({});
// 存放属于有无判断,格式判断,逻辑判断,正则表达式这一类的评查点规则设置
// const [otherRule, setOtherRule] = useState<Record<string, unknown>[]>([]);
// 存放评查点ID与有效页码的映射
const [effectivePages, setEffectivePages] = useState<Record<string, number>>({});
// 初始化建议文本
useEffect(() => {
@@ -874,6 +871,35 @@ export function ReviewPointsList({
// 处理配对数据
const pairs = config.pairs;
// 获取第一个有效页码
if (reviewPoint.id && !effectivePages[reviewPoint.id]) {
for (const pair of pairs) {
// 检查sourceField中是否有有效页码
const sourceFieldKey = Object.keys(pair.sourceField)[0];
if (sourceFieldKey && pair.sourceField[sourceFieldKey].page &&
Number(pair.sourceField[sourceFieldKey].page) > 0) {
// 保存页码
setEffectivePages(prev => ({
...prev,
[reviewPoint.id || '']: Number(pair.sourceField[sourceFieldKey].page)
}));
break;
}
// 如果sourceField没有有效页码,检查targetField
const targetFieldKey = Object.keys(pair.targetField)[0];
if (targetFieldKey && pair.targetField[targetFieldKey].page &&
Number(pair.targetField[targetFieldKey].page) > 0) {
// 保存页码
setEffectivePages(prev => ({
...prev,
[reviewPoint.id || '']: Number(pair.targetField[targetFieldKey].page)
}));
break;
}
}
}
// 查找链条关系
const findChains = () => {
type ChainItem = {
@@ -1169,7 +1195,11 @@ export function ReviewPointsList({
if (reviewPointId && typeof onReviewPointSelect === 'function') {
onReviewPointSelect(reviewPointId, Number(item.data.page));
}
}else{
}
else if(reviewPoint.contentPage && reviewPoint.contentPage[item.field]){
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[item.field]));
}
else{
toastService.error(`没有找到${item.field}对应的索引内容`);
}
}}
@@ -1177,7 +1207,7 @@ export function ReviewPointsList({
>
<div className="flex justify-between w-full">
<ReactTableTooltip content={item.data.value?.toString() || ''} />
{!item.data.page && !item.data.value && (
{!item.data.page && (reviewPoint.contentPage && !reviewPoint.contentPage[item.field]) && (
<i className="ri-information-line text-red-500 text-xs" title="没有找到对应的文书内容"></i>
)}
</div>
@@ -1248,14 +1278,18 @@ export function ReviewPointsList({
if (reviewPointId && typeof onReviewPointSelect === 'function') {
onReviewPointSelect(reviewPointId, chain[0].data.page);
}
}else{
}
else if(reviewPoint.contentPage && reviewPoint.contentPage[chain[0].field]){
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[chain[0].field]));
}
else{
toastService.error(`没有找到${chain[0].field}对应的索引内容`);
}
}}
aria-label={`查看${chain[0].field}内容详情`}
>
<div className="value-source text-xs text-gray-500 mb-1">{chain[0].field}
{!chain[0].data.page && !chain[0].data.value && (
{!chain[0].data.page && (reviewPoint.contentPage && !reviewPoint.contentPage[chain[0].field]) && (
<i className="ri-information-line text-red-500 text-xs ml-1" title="没有找到对应的文书内容"></i>
)}
</div>
@@ -1270,14 +1304,18 @@ export function ReviewPointsList({
if (reviewPointId && typeof onReviewPointSelect === 'function') {
onReviewPointSelect(reviewPointId, chain[1].data.page);
}
}else{
}
else if(reviewPoint.contentPage && reviewPoint.contentPage[chain[1].field]){
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[chain[1].field]));
}
else{
toastService.error(`没有找到${chain[1].field}对应的索引内容`);
}
}}
aria-label={`查看${chain[1].field}内容详情`}
>
<div className="value-source text-xs text-gray-500 mb-1">{chain[1].field}
{!chain[1].data.page && !chain[1].data.value && (
{!chain[1].data.page && (reviewPoint.contentPage && !reviewPoint.contentPage[chain[1].field]) && (
<i className="ri-information-line text-red-500 text-xs ml-1" title="没有找到对应的文书内容"></i>
)}
</div>
@@ -1404,6 +1442,8 @@ export function ReviewPointsList({
e.stopPropagation();
if (mainTypeValue.page && typeof onReviewPointSelect === 'function') {
onReviewPointSelect(reviewPoint.id, Number(mainTypeValue.page));
}else if(reviewPoint.contentPage && reviewPoint.contentPage[fieldKey]){
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[fieldKey]));
}else{
toastService.error(`没有找到${fieldKey}对应的索引内容`);
}
@@ -1425,7 +1465,7 @@ export function ReviewPointsList({
{/* 字段名称 */}
<div className="text-xs text-gray-500 mb-1">
{fieldKey}
{!mainTypeValue.page && !mainTypeValue.value && (
{!mainTypeValue.page && (reviewPoint.contentPage && !reviewPoint.contentPage[fieldKey]) && (
<i className="ri-information-line text-red-500 text-xs ml-1" title="没有找到对应的文书内容"></i>
)}
{/* 缺失显示 */}
@@ -1492,6 +1532,19 @@ export function ReviewPointsList({
// 如果配置不存在,不渲染任何内容
if (!config) return null;
// 获取第一个有效页码
if (reviewPoint.id && !effectivePages[reviewPoint.id] && config.fields) {
for (const field of Object.values(config.fields || {})) {
if (field.page && Number(field.page) > 0) {
setEffectivePages(prev => ({
...prev,
[reviewPoint.id || '']: Number(field.page)
}));
break;
}
}
}
// 创建一个数组来存储需要渲染的JSX元素
const fieldElements: JSX.Element[] = [];
@@ -1510,6 +1563,8 @@ export function ReviewPointsList({
e.stopPropagation();
if (value.page && typeof onReviewPointSelect === 'function') {
onReviewPointSelect(reviewPoint.id, Number(value.page));
}else if(reviewPoint.contentPage && reviewPoint.contentPage[key]){
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[key]));
}else{
toastService.error(`没有找到${key}对应的索引内容`);
}
@@ -1520,6 +1575,8 @@ export function ReviewPointsList({
e.preventDefault();
if (value.page && typeof onReviewPointSelect === 'function') {
onReviewPointSelect(reviewPoint.id, Number(value.page));
}else if(reviewPoint.contentPage && reviewPoint.contentPage[key]){
onReviewPointSelect(reviewPoint.id, Number(reviewPoint.contentPage[key]));
}else{
toastService.error(`没有找到${key}对应的索引内容`);
}
@@ -1533,7 +1590,7 @@ export function ReviewPointsList({
<div className="text-xs text-left text-gray-500 mb-1">
{key}
{/* 没有抽取到目录内容,page和value都为空 */}
{!value.page && !value.value && (
{!value.page && (reviewPoint.contentPage && !reviewPoint.contentPage[key]) && (
<i className="ri-information-line text-red-500 text-xs ml-1" title="没有找到对应的文书内容"></i>
)}
{/* 缺失显示 */}
@@ -1851,6 +1908,34 @@ export function ReviewPointsList({
for (const key in fieldKeyMap) {
mergedRules.push(fieldKeyMap[key]);
}
// 获取第一个有效页码
if (reviewPoint.id && !effectivePages[reviewPoint.id]) {
// 遍历合并后的规则数组,查找第一个有效页码
for (const rule of mergedRules) {
// 遍历字段类型对象
const typeEntries = Object.entries(rule.fieldValue.type);
// 遍历每种类型规则
for (const [, typeValue] of typeEntries) {
// 检查是否有有效页码
if (typeValue.page && Number(typeValue.page) > 0) {
// 找到有效页码,设置状态并跳出循环
setEffectivePages(prev => ({
...prev,
[reviewPoint.id || '']: Number(typeValue.page)
}));
// 使用break跳出当前循环
break;
}
}
// 如果已经找到有效页码,跳出外层循环
if (reviewPoint.id && effectivePages[reviewPoint.id]) {
break;
}
}
}
// 返回合并后的规则数组
return mergedRules;
@@ -2148,24 +2233,19 @@ export function ReviewPointsList({
const reviewPoint = reviewPoints.find(result => result.id === id);
// 如果评查点存在
if (reviewPoint) {
// // 使用checkContentPage方法获取页码和key
// const { pageIndex, key } = checkContentPage(reviewPoint);
// // 如果有有效页码,传递ID和页码
// if (pageIndex > 0) {
// console.log(`跳转到页面 ${pageIndex},对应内容 ${key || '未知'}`);
// onReviewPointSelect(id, pageIndex);
// return;
// }
// // 没有有效页码,只传递ID
onReviewPointSelect(id);
// console.log(`没有有效页码---评查点ID${reviewPoint.pointId},评查点结果ID${id}`);
if (reviewPoint) {
// 如果effectivePages有值,使用它
if (reviewPoint.id && effectivePages[reviewPoint.id]) {
// console.log('effectivePages', effectivePages[reviewPoint.id]);
onReviewPointSelect(id, effectivePages[reviewPoint.id]);
// return;
} else {
// 没有有效页码,只传递ID
onReviewPointSelect(id);
}
} else {
// // 没有找到评查点,只传递ID
// 没有找到评查点,只传递ID
onReviewPointSelect(id);
// console.log(`没有找到评查点---评查点结果ID${id}`);
}
};
@@ -2225,7 +2305,7 @@ export function ReviewPointsList({
tabIndex={0}
style={{ userSelect: 'text' }}
onClick={() => {
console.log('reviewPoint', reviewPoint);
// console.log('reviewPoint', reviewPoint);
handleReviewPointClick(reviewPoint.id);
}}
onKeyDown={(e) => {
+20 -15
View File
@@ -3,25 +3,30 @@ interface ActionButtonsProps {
onSave?: () => void;
onSaveDraft?: () => void;
isEditMode?: boolean;
showButtons?: boolean;
}
export function ActionButtons({ onSave, onSaveDraft, isEditMode }: ActionButtonsProps) {
export function ActionButtons({ onSave, onSaveDraft, isEditMode, showButtons = true }: ActionButtonsProps) {
return (
<div className="flex justify-center space-x-4 mt-8 mb-4">
<button
type="button"
className="ant-btn ant-btn-primary min-w-[120px]"
onClick={onSave}
>
<i className="ri-save-line mr-1"></i> {isEditMode ? '保存修改' : '保存'}
</button>
<button
type="button"
className="ant-btn ant-btn-default min-w-[120px] !hidden"
onClick={onSaveDraft}
>
<i className="ri-draft-line mr-1"></i> {isEditMode ? '另存为草稿' : '保存草稿'}
</button>
{showButtons && (
<>
<button
type="button"
className="ant-btn ant-btn-primary min-w-[120px]"
onClick={onSave}
>
<i className="ri-save-line mr-1"></i> {isEditMode ? '保存修改' : '保存'}
</button>
<button
type="button"
className="ant-btn ant-btn-default min-w-[120px] !hidden"
onClick={onSaveDraft}
>
<i className="ri-draft-line mr-1"></i> {isEditMode ? '另存为草稿' : '保存草稿'}
</button>
</>
)}
<Button
type="default"
className="min-w-[120px] focus:!ring-gray-300"
+11 -8
View File
@@ -1,20 +1,23 @@
interface PageHeaderProps {
title: string;
onSave?: () => void;
showSaveButton?: boolean;
}
export function PageHeader({ title, onSave }: PageHeaderProps) {
export function PageHeader({ title, onSave, showSaveButton = true }: PageHeaderProps) {
return (
<div className="flex justify-between items-center pb-2 mb-4 border-b border-gray-200">
<h1 className="text-xl font-medium text-gray-800">{title}</h1>
<div>
<button
type="button"
className="ant-btn ant-btn-primary"
onClick={onSave}
>
<i className="ri-save-line mr-1"></i>
</button>
{showSaveButton && (
<button
type="button"
className="ant-btn ant-btn-primary"
onClick={onSave}
>
<i className="ri-save-line mr-1"></i>
</button>
)}
</div>
</div>
);
+8 -8
View File
@@ -243,7 +243,7 @@ export function ReviewSettings({
}
// 记录初始化处理
console.log("ReviewSettings开始初始化,数据:", initialData);
// console.log("ReviewSettings开始初始化,数据:", initialData);
// 保存初始数据引用,用于后续比较
initialDataRef.current = JSON.parse(JSON.stringify(initialData));
@@ -255,7 +255,7 @@ export function ReviewSettings({
if (initialData) {
// 处理初始规则数据
if (initialData.rules && Array.isArray(initialData.rules) && initialData.rules.length > 0) {
console.log("设置初始规则数据:", initialData.rules);
// console.log("设置初始规则数据:", initialData.rules);
const validRules = initialData.rules.map(rule => {
// 确保每个规则都有id
@@ -394,7 +394,7 @@ export function ReviewSettings({
const newFields = uniqueFields.filter((field: string) => !availableFields.includes(field));
if (newFields.length > 0 || deletedFields.length > 0) {
console.log('Updating fields in checkAndUpdateFields - deleted:', deletedFields, 'new:', newFields);
// console.log('Updating fields in checkAndUpdateFields - deleted:', deletedFields, 'new:', newFields);
// 设置最新的可用字段列表
setAvailableFields(uniqueFields);
@@ -420,7 +420,7 @@ export function ReviewSettings({
// 处理已删除字段的函数
const handleDeletedFields = (deletedFields: string[]) => {
console.log("处理已删除字段:", deletedFields);
// console.log("处理已删除字段:", deletedFields);
// 如果没有删除的字段,则直接返回
if (!deletedFields || deletedFields.length === 0) return;
@@ -513,7 +513,7 @@ export function ReviewSettings({
// 如果配置有实质性修改,记录日志
if (configModified) {
console.log(`规则(ID: ${rule.id}, 类型: ${rule.type})已清除对已删除字段的引用`);
// console.log(`规则(ID: ${rule.id}, 类型: ${rule.type})已清除对已删除字段的引用`);
}
return {
@@ -877,7 +877,7 @@ export function ReviewSettings({
!(config.availableFields as string[]).every((field) => availableFields.includes(field))))) {
// 延迟更新以避免在渲染过程中修改状态
setTimeout(() => {
console.log('Updating rule config with new available fields:', availableFields);
// console.log('Updating rule config with new available fields:', availableFields);
const updatedConfig = { ...config, availableFields: availableFields };
handleRuleConfigChange(id, updatedConfig);
}, 0);
@@ -915,7 +915,7 @@ export function ReviewSettings({
value="and"
checked={!config.logic || config.logic === 'and'}
onChange={(e) => {
console.log(`[调试] 选择判断逻辑 and,规则ID: ${id}, 当前值: ${config.logic}`);
// console.log(`[调试] 选择判断逻辑 and,规则ID: ${id}, 当前值: ${config.logic}`);
handleRuleConfigChange(id, { logic: e.target.value });
// 直接触发配置更新
generateEvaluationConfig();
@@ -932,7 +932,7 @@ export function ReviewSettings({
value="or"
checked={config.logic === 'or'}
onChange={(e) => {
console.log(`[调试] 选择判断逻辑 or,规则ID: ${id}, 当前值: ${config.logic}`);
// console.log(`[调试] 选择判断逻辑 or,规则ID: ${id}, 当前值: ${config.logic}`);
handleRuleConfigChange(id, { logic: e.target.value });
// 直接触发配置更新
generateEvaluationConfig();
+5 -5
View File
@@ -93,12 +93,12 @@ export function DateRangePicker({
try {
// 检查并记录showPicker是否可用
const hasShowPicker = typeof inputRef.current.showPicker === 'function';
console.log('showPicker API available:', hasShowPicker);
// console.log('showPicker API available:', hasShowPicker);
// 尝试使用showPicker API
if (hasShowPicker) {
inputRef.current.showPicker();
console.log('showPicker called successfully');
// console.log('showPicker called successfully');
}
} catch (error) {
console.error('Failed to show date picker:', error);
@@ -236,7 +236,7 @@ export function SimpleDateRangePicker({
// 处理日期输入框全局点击
const handleWrapperClick = (inputRef: React.RefObject<HTMLInputElement>) => {
if (inputRef.current) {
console.log('Wrapper clicked, triggering date input');
// console.log('Wrapper clicked, triggering date input');
// 点击整个包装器区域时,触发输入框点击
inputRef.current.focus();
inputRef.current.click();
@@ -244,12 +244,12 @@ export function SimpleDateRangePicker({
try {
// 检查并记录showPicker是否可用
const hasShowPicker = typeof inputRef.current.showPicker === 'function';
console.log('showPicker API available:', hasShowPicker);
// console.log('showPicker API available:', hasShowPicker);
// 尝试使用showPicker API
if (hasShowPicker) {
inputRef.current.showPicker();
console.log('showPicker called successfully');
// console.log('showPicker called successfully');
}
} catch (error) {
console.error('Failed to show date picker:', error);
+25 -6
View File
@@ -24,6 +24,8 @@ export interface TooltipProps {
className?: string; // 自定义类名
onVisibleChange?: (visible: boolean) => void; // 显示状态变化回调
fixedPlacement?: boolean; // 是否固定显示位置,不自动切换
maxHeight?: number | string; // 最大高度,超出将显示滚动条
scrollable?: boolean; // 是否可滚动
}
/**
@@ -45,7 +47,9 @@ export function Tooltip({
maxWidth = 320,
className = '',
onVisibleChange,
fixedPlacement = false // 默认不固定位置
fixedPlacement = false, // 默认不固定位置
maxHeight = 300, // 默认最大高度300px
scrollable = true // 默认允许滚动
}: TooltipProps) {
// 使用内部状态管理提示框显示状态(非受控模式)
const [visible, setVisible] = useState(false);
@@ -59,6 +63,7 @@ export function Tooltip({
// 引用DOM元素
const triggerRef = useRef<HTMLDivElement>(null);
const tooltipRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
// 保存当前实际显示位置
const [actualPlacement, setActualPlacement] = useState<TooltipPlacement>(placement);
@@ -99,6 +104,17 @@ export function Tooltip({
? `${maxWidth}px`
: String(maxWidth);
// 如果内容可滚动,设置最大高度,但仅应用于内容容器而不是整个tooltip
if (scrollable && contentRef.current) {
contentRef.current.style.maxHeight = typeof maxHeight === 'number'
? `${maxHeight}px`
: String(maxHeight);
// 确保只有内容区域显示滚动条,整个tooltip容器不滚动
contentRef.current.style.overflowY = 'auto';
tooltipRef.current.style.overflow = 'visible';
}
// 计算各个方向的位置
let top = 0, left = 0;
let arrowTop = 0, arrowLeft = 0;
@@ -154,7 +170,7 @@ export function Tooltip({
if (top < 0) {
if (currentPlacement === 'top') {
// 如果上方放不下,切换到下方
top = triggerRect.bottom + arrowSize;
top = triggerRect.bottom + arrowSize - 8;
arrowTop = -arrowSize;
// 更新实际位置为bottom
@@ -347,19 +363,22 @@ export function Tooltip({
// 渲染提示框内容
const renderTooltipContent = () => {
// 根据是否启用滚动来确定内容容器类名
const contentClassName = scrollable ? 'tooltip-content-inner tooltip-scrollable' : 'tooltip-content-inner';
if (rich) {
return (
<div className="tooltip-content">
<div className="tooltip-content" ref={contentRef}>
{header && <div className="tooltip-header">{header}</div>}
<div className="tooltip-body">{content}</div>
<div className={contentClassName}>{content}</div>
{footer && <div className="tooltip-footer">{footer}</div>}
</div>
);
}
return (
<div className="tooltip-content">
<div>{content}</div>
<div className="tooltip-content" ref={contentRef}>
<div className={contentClassName}>{content}</div>
</div>
);
};
+128 -134
View File
@@ -9,14 +9,14 @@ import {
isRouteErrorResponse,
useRouteError,
type MetaFunction,
// useLoaderData
useLoaderData
} from "@remix-run/react";
// import {
// LoaderFunctionArgs,
// redirect,
// createCookieSessionStorage,
// ActionFunctionArgs
// } from "@remix-run/node";
import {
LoaderFunctionArgs,
redirect,
createCookieSessionStorage,
ActionFunctionArgs
} from "@remix-run/node";
import { Layout } from "~/components/layout/Layout";
import { ErrorBoundary as AppErrorBoundary } from "~/components/error/ErrorBoundary";
import { MessageModalProvider } from "~/components/ui/MessageModal";
@@ -30,115 +30,127 @@ import LoadingBarContainer from "~/components/ui/LoadingBar";
import RouteChangeLoader from "~/components/ui/RouteChangeLoader";
// import { useState, useEffect } from "react";
// 定义用户角色类型
export type UserRole = 'common' | 'developer';
// 定义需要高级权限的路径
export const developerOnlyPaths = [
'/settings',
'/config-lists',
'/document-types',
'/prompts'
];
// 创建基于Cookie的会话存储
// 在实际应用中,应该使用环境变量来设置密钥
// const sessionStorage = createCookieSessionStorage({
// cookie: {
// name: "__session",
// httpOnly: true,
// path: "/",
// sameSite: "lax",
// secrets: ["s3cr3t"], // 应该从环境变量读取
// secure: process.env.NODE_ENV === "production",
// },
// });
const sessionStorage = createCookieSessionStorage({
cookie: {
name: "__session",
httpOnly: true,
path: "/",
sameSite: "lax",
secrets: ["s3cr3t"], // 应该从环境变量读取
secure: process.env.NODE_ENV === "production",
},
});
// // 获取会话对象
// export async function getSession(request: Request) {
// const cookie = request.headers.get("Cookie");
// return sessionStorage.getSession(cookie);
// }
// 获取会话对象
export async function getSession(request: Request) {
const cookie = request.headers.get("Cookie");
return sessionStorage.getSession(cookie);
}
// // 获取用户登录状态
// export async function getUserSession(request: Request) {
// const session = await getSession(request);
// return {
// isAuthenticated: session.get("isAuthenticated") === true,
// };
// }
// 获取用户登录状态
export async function getUserSession(request: Request) {
const session = await getSession(request);
return {
isAuthenticated: session.get("isAuthenticated") === true,
userRole: session.get("userRole") || 'common' as UserRole
};
}
// // 创建登录会话
// export async function createUserSession(isAuthenticated: boolean, redirectTo: string) {
// const session = await sessionStorage.getSession();
// session.set("isAuthenticated", isAuthenticated);
// 创建登录会话
export async function createUserSession(isAuthenticated: boolean, userRole: UserRole, redirectTo: string) {
const session = await sessionStorage.getSession();
session.set("isAuthenticated", isAuthenticated);
session.set("userRole", userRole);
// return redirect(redirectTo, {
// headers: {
// "Set-Cookie": await sessionStorage.commitSession(session),
// },
// });
// }
return redirect(redirectTo, {
headers: {
"Set-Cookie": await sessionStorage.commitSession(session),
},
});
}
// // 销毁会话(登出)
// export async function logout(request: Request) {
// const session = await getSession(request);
// 销毁会话(登出)
export async function logout(request: Request) {
const session = await getSession(request);
// return redirect("/login", {
// headers: {
// "Set-Cookie": await sessionStorage.destroySession(session),
// },
// });
// }
return redirect("/login", {
headers: {
"Set-Cookie": await sessionStorage.destroySession(session),
},
});
}
// // 添加action处理登录/登出请求
// export async function action({ request }: ActionFunctionArgs) {
// const formData = await request.formData();
// const intent = formData.get("intent");
// 添加action处理登录/登出请求
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const intent = formData.get("intent");
// if (intent === "logout") {
// return logout(request);
// }
if (intent === "logout") {
return logout(request);
}
// return null;
// }
return null;
}
// // 添加loader函数进行全局认证检查
// export async function loader({ request }: LoaderFunctionArgs) {
// // 获取当前路径
// const url = new URL(request.url);
// const pathname = url.pathname;
// 添加loader函数进行全局认证检查
export async function loader({ request }: LoaderFunctionArgs) {
// 获取当前路径
const url = new URL(request.url);
const pathname = url.pathname;
// // 排除不需要登录验证的路径
// const publicPaths = ['/login', '/favicon.ico'];
// const isPublicPath = publicPaths.some(path => pathname.startsWith(path));
// 排除不需要登录验证的路径
const publicPaths = ['/login', '/favicon.ico'];
const isPublicPath = publicPaths.some(path => pathname.startsWith(path));
// // 获取用户会话
// const { isAuthenticated } = await getUserSession(request);
// 获取用户会话
const { isAuthenticated, userRole } = await getUserSession(request);
// console.log("Auth status:", { isAuthenticated, userRole, pathname });
// // 如果访问需要认证的路径但未登录,重定向到登录页
// if (!isPublicPath && !isAuthenticated) {
// // 保存请求的URL,以便登录后重定向回来
// const session = await getSession(request);
// session.set("redirectTo", pathname);
// 如果访问需要认证的路径但未登录,重定向到登录页
if (!isPublicPath && !isAuthenticated) {
// 保存请求的URL,以便登录后重定向回来
const session = await getSession(request);
// return redirect("/login", {
// headers: {
// "Set-Cookie": await sessionStorage.commitSession(session),
// },
// });
// }
// 如果路径是/home,则将重定向目标设置为/
const redirectTarget = pathname === "/home" ? "/" : pathname;
// 保存重定向目标
session.set("redirectTo", redirectTarget);
return redirect("/login", {
headers: {
"Set-Cookie": await sessionStorage.commitSession(session),
},
});
}
// // 如果已登录且访问登录页,重定向到首页
// if (pathname === "/login" && isAuthenticated) {
// return redirect("/home");
// }
// 如果已登录且访问登录页,重定向到首页
if (pathname === "/login" && isAuthenticated) {
// console.log("Already authenticated, redirecting from login to /");
return redirect("/");
}
// // 向组件传递认证状态和当前路径
// return Response.json({ isAuthenticated, pathname });
// }
// 检查访问权限 - 如果是common用户访问了开发者专属页面,重定向到首页
if (userRole === 'common' && developerOnlyPaths.some(path => pathname.startsWith(path))) {
return redirect("/");
}
// 向组件传递认证状态和当前路径
return Response.json({ isAuthenticated, userRole, pathname });
}
// 添加客户端hydration错误处理
// if (typeof window !== "undefined") {
// window.addEventListener("error", (event) => {
// if (event.message && event.message.includes("Hydration failed")) {
// console.warn("Hydration error detected, refreshing page...");
// setTimeout(() => {
// window.location.reload();
// }, 100);
// event.preventDefault();
// }
// });
// }
export const meta: MetaFunction = () => {
return [
@@ -163,11 +175,8 @@ export function links() {
}
export default function App() {
// const { pathname } = useLoaderData<typeof loader>();
const { userRole } = useLoaderData<typeof loader>();
// // 确定哪些路径不需要Layout
// const noLayoutPaths = ['/login', '/home'];
// const needsLayout = !noLayoutPaths.includes(pathname);
return (
<html lang="zh-CN">
@@ -193,18 +202,9 @@ export default function App() {
<body className="font-sans">
<MessageModalProvider>
<ToastProvider>
{/* {needsLayout ? (
<Layout>
<Outlet />
</Layout>
) : (
<Layout userRole={userRole}>
<Outlet />
)} */}
<Layout>
<Outlet />
</Layout>
</Layout>
<RouteChangeLoader />
</ToastProvider>
</MessageModalProvider>
@@ -219,38 +219,32 @@ export default function App() {
export function ErrorBoundary() {
const error = useRouteError();
// 为错误页面设置标题和描述
let title = "发生错误";
let message = "发生了一个未知错误,请稍后重试";
if (isRouteErrorResponse(error)) {
return (
<html lang="zh-CN">
<head>
<Meta />
<Links />
<title> {error.status}</title>
</head>
<body>
<AppErrorBoundary
status={error.status}
statusText={error.statusText}
message={error.data?.message || "发生了一个错误,请稍后重试"}
/>
<Scripts />
</body>
</html>
);
title = `错误 ${error.status}`;
message = error.data?.message || "发生了一个错误,请稍后重试";
} else {
title = "意外错误";
message = "服务器发生了意外错误,请稍后重试";
}
return (
<html lang="zh-CN">
<head>
<Meta />
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="description" content={message} />
<title>{title}</title>
<Links />
<title></title>
</head>
<body>
<AppErrorBoundary
status={500}
statusText="服务器错误"
message="服务器发生了意外错误,请稍后重试"
status={isRouteErrorResponse(error) ? error.status : 500}
statusText={isRouteErrorResponse(error) ? error.statusText : "服务器错误"}
message={message}
/>
<Scripts />
</body>
+19 -12
View File
@@ -3,7 +3,7 @@ import { useNavigate, Form } from '@remix-run/react';
import { type MetaFunction, type ActionFunctionArgs, LoaderFunctionArgs, redirect } from "@remix-run/node";
import styles from "~/styles/pages/home.css?url";
import dayjs from 'dayjs';
// import { getUserSession, logout } from "~/root";
import { getUserSession, logout } from "~/root";
export const links = () => [
{ rel: "stylesheet", href: styles }
@@ -22,22 +22,21 @@ export async function action({ request }: ActionFunctionArgs) {
const intent = formData.get("intent");
if (intent === "logout") {
// return logout(request);
return logout(request);
}
return null;
}
// 验证用户登录状态
// export async function loader({ request }: LoaderFunctionArgs) {
// const { isAuthenticated } = await getUserSession(request);
export async function loader({ request }: LoaderFunctionArgs) {
const { isAuthenticated } = await getUserSession(request);
// if (!isAuthenticated) {
// return redirect("/login");
// }
// return Response.json({ isAuthenticated });
// }
if (!isAuthenticated) {
return redirect("/login");
}
return null;
}
export default function Index() {
const navigate = useNavigate();
@@ -68,6 +67,7 @@ export default function Index() {
// 处理模块点击
const handleModuleClick = (path: string) => {
// console.log("导航到路径:", path);
navigate(path);
};
@@ -80,6 +80,13 @@ export default function Index() {
// 处理登出
const handleLogout = () => {
// 清除sessionStorage中的用户角色信息
if (typeof window !== 'undefined') {
sessionStorage.removeItem('userRole');
// 可以根据需要清除其他会话数据
sessionStorage.clear();
}
// 使用Form组件提交登出请求
const form = document.getElementById('logout-form') as HTMLFormElement;
if (form) {
@@ -127,8 +134,8 @@ export default function Index() {
{/* 合同管理模块 */}
<div
className="module-card"
onClick={() => handleModuleClick('/documents')}
onKeyDown={(e) => handleKeyDown('/documents', e)}
onClick={() => handleModuleClick('/contract-template/search')}
onKeyDown={(e) => handleKeyDown('/contract-template/search', e)}
role="button"
tabIndex={0}
aria-label="合同管理"
+4 -4
View File
@@ -45,8 +45,8 @@ export async function loader({ params }: LoaderFunctionArgs) {
}
// 添加调试信息
console.log('模板详情数据:', response.data);
console.log('分类信息:', response.data.category);
// console.log('模板详情数据:', response.data);
// console.log('分类信息:', response.data.category);
return { template: response.data };
} catch (error) {
@@ -90,7 +90,7 @@ export default function ContractTemplateDetail() {
link.click();
document.body.removeChild(link);
console.log('开始下载文件:', cleanFileName);
// console.log('开始下载文件:', cleanFileName);
} catch (error) {
console.error('下载文件失败:', error);
alert('下载失败,请稍后重试');
@@ -108,7 +108,7 @@ export default function ContractTemplateDetail() {
};
const handlePreview = () => {
console.log('预览模板:', template.id);
// console.log('预览模板:', template.id);
// 页面内预览,滚动到预览区域
const previewElement = document.getElementById('template-preview');
if (previewElement) {
+1 -1
View File
@@ -296,7 +296,7 @@ export default function DocumentEdit() {
const onDocumentLoadSuccess = ({numPages}: {numPages: number}) => {
setNumPages(numPages);
console.log('文档加载成功', numPages);
// console.log('文档加载成功', numPages);
}
const renderDocumentContent = () => {
+77 -77
View File
@@ -121,7 +121,7 @@ async function uploadFileToServer(
isTestDocument: boolean
): Promise<FileUploadResponse> {
// 在实际应用中,这里会使用fetch或axios发送请求到后端API
console.log(`[API] 上传文件: ${fileName}, 大小: ${binaryData.byteLength} 字节`);
// console.log(`[API] 上传文件: ${fileName}, 大小: ${binaryData.byteLength} 字节`);
try {
// 使用封装的上传函数
@@ -206,7 +206,7 @@ export async function action({ request }: ActionFunctionArgs) {
// 获取文件信息
if (fileUpload) {
console.log(`接收到文件: ${fileUpload.name}, 大小: ${fileUpload.size}, 类型: ${fileUpload.type}`);
// console.log(`接收到文件: ${fileUpload.name}, 大小: ${fileUpload.size}, 类型: ${fileUpload.type}`);
}
// 注意: 在实际的Remix action中,我们无法直接处理文件内容
@@ -356,7 +356,7 @@ export default function FilesUpload() {
// useEffect 处理上传队列状态检查定时器 - 只在组件卸载时清除
useEffect(() => {
console.log('设置上传队列状态检查定时器');
// console.log('设置上传队列状态检查定时器');
// 标记组件已挂载
isMountedRef.current = true;
@@ -367,7 +367,7 @@ export default function FilesUpload() {
// 只在组件卸载时清除
return () => {
console.log('组件卸载,清除上传队列状态检查定时器');
// console.log('组件卸载,清除上传队列状态检查定时器');
// 标记组件已卸载
isMountedRef.current = false;
if (statusCheckIntervalRef.current) {
@@ -387,16 +387,16 @@ export default function FilesUpload() {
.filter(file => file.status !== DocumentStatus.PROCESSED && file.id)
.map(file => file.id);
console.log('未完成的文档ID:', incompleteIds);
// console.log('未完成的文档ID:', incompleteIds);
if (incompleteIds.length === 0) {
console.log('没有未完成的文档,跳过状态检查');
// console.log('没有未完成的文档,跳过状态检查');
return;
}
// 获取这些文档的最新状态
const statusResponse = await getDocumentsStatus(incompleteIds);
console.log('状态检查响应:', statusResponse);
// console.log('状态检查响应:', statusResponse);
if (statusResponse.data) {
// 更新队列中的文档状态
@@ -404,7 +404,7 @@ export default function FilesUpload() {
const updatedFiles = prevFiles.map(file => {
const updatedStatus = statusResponse.data.find(doc => doc.id === file.id);
if (updatedStatus) {
console.log(`文档 ${file.id} 状态更新: ${file.status} -> ${updatedStatus.status}`);
// console.log(`文档 ${file.id} 状态更新: ${file.status} -> ${updatedStatus.status}`);
return { ...file, status: updatedStatus.status };
}
return file;
@@ -456,7 +456,7 @@ export default function FilesUpload() {
const value = e.target.value;
// 确保只有选择了有效的文件类型才进行设置
if (value) {
console.log('【调试-handleFileTypeChange】文件类型变更为:', value);
// console.log('【调试-handleFileTypeChange】文件类型变更为:', value);
setFileType(value as FileType);
// 立即清除错误状态
setFileTypeError(null);
@@ -464,12 +464,12 @@ export default function FilesUpload() {
// 检查是否选择了合同类型
const selectedType = documentTypes.find(t => t.id.toString() === value);
const isContract = !!(selectedType && selectedType.name.includes('合同'));
console.log('【调试-handleFileTypeChange】文件类型检查:', {
selectedType,
isContract,
typeName: selectedType?.name,
currentFiles: currentFiles.length
});
// console.log('【调试-handleFileTypeChange】文件类型检查:', {
// selectedType,
// isContract,
// typeName: selectedType?.name,
// currentFiles: currentFiles.length
// });
setIsContractType(isContract);
@@ -480,10 +480,10 @@ export default function FilesUpload() {
// 如果已经有选中的文件,且选择了文件类型,且不是合同类型,则开始上传
if (currentFiles.length > 0 && !isContract) {
console.log('【调试-handleFileTypeChange】自动开始上传非合同类型文件');
// console.log('【调试-handleFileTypeChange】自动开始上传非合同类型文件');
startUpload(currentFiles);
} else if (currentFiles.length > 0 && isContract) {
console.log('【调试-handleFileTypeChange】合同类型需要手动点击开始上传按钮');
// console.log('【调试-handleFileTypeChange】合同类型需要手动点击开始上传按钮');
// 合同类型不自动上传,需要用户先上传主文件和附件,然后点击开始上传按钮
setCurrentFiles([]);
}
@@ -499,11 +499,11 @@ export default function FilesUpload() {
// 处理合同主文件选择
const handleContractMainFilesSelected = (files: FileList) => {
try {
console.log('【调试-handleContractMainFilesSelected】开始处理合同主文件选择, 文件数量:', files.length);
// console.log('【调试-handleContractMainFilesSelected】开始处理合同主文件选择, 文件数量:', files.length);
// 检查组件是否已卸载
if (!isMountedRef.current) {
console.error('【调试-handleContractMainFilesSelected】组件已卸载,取消处理');
// console.error('【调试-handleContractMainFilesSelected】组件已卸载,取消处理');
return;
}
@@ -532,15 +532,15 @@ export default function FilesUpload() {
}
if (validFiles.length > 0 && isMountedRef.current) {
console.log('【调试-handleContractMainFilesSelected】有效文件数量:', validFiles.length);
console.log('【调试-handleContractMainFilesSelected】有效文件:', validFiles.map(f => ({ name: f.name, size: f.size, type: f.type })));
// console.log('【调试-handleContractMainFilesSelected】有效文件数量:', validFiles.length);
// console.log('【调试-handleContractMainFilesSelected】有效文件:', validFiles.map(f => ({ name: f.name, size: f.size, type: f.type })));
setContractMainFiles(validFiles);
} else {
console.error('【调试-handleContractMainFilesSelected】没有有效的PDF文件或组件已卸载');
}
} else {
console.log('【调试-handleContractMainFilesSelected】未选择任何文件');
// console.log('【调试-handleContractMainFilesSelected】未选择任何文件');
}
} catch (error) {
console.error('【调试-handleContractMainFilesSelected】处理合同主文件选择时发生错误:', error);
@@ -550,7 +550,7 @@ export default function FilesUpload() {
// 处理合同附件选择
const handleContractAttachmentFilesSelected = (files: FileList) => {
try {
console.log('【调试-handleContractAttachmentFilesSelected】开始处理合同附件选择, 文件数量:', files.length);
// console.log('【调试-handleContractAttachmentFilesSelected】开始处理合同附件选择, 文件数量:', files.length);
// 检查组件是否已卸载
if (!isMountedRef.current) {
@@ -583,15 +583,15 @@ export default function FilesUpload() {
}
if (validFiles.length > 0 && isMountedRef.current) {
console.log('【调试-handleContractAttachmentFilesSelected】有效文件数量:', validFiles.length);
console.log('【调试-handleContractAttachmentFilesSelected】有效文件:', validFiles.map(f => ({ name: f.name, size: f.size, type: f.type })));
// console.log('【调试-handleContractAttachmentFilesSelected】有效文件数量:', validFiles.length);
// console.log('【调试-handleContractAttachmentFilesSelected】有效文件:', validFiles.map(f => ({ name: f.name, size: f.size, type: f.type })));
setContractAttachmentFiles(validFiles);
} else {
console.error('【调试-handleContractAttachmentFilesSelected】没有有效的PDF文件或组件已卸载');
}
} else {
console.log('【调试-handleContractAttachmentFilesSelected】未选择任何文件');
// console.log('【调试-handleContractAttachmentFilesSelected】未选择任何文件');
}
} catch (error) {
console.error('【调试-handleContractAttachmentFilesSelected】处理合同附件选择时发生错误:', error);
@@ -601,11 +601,11 @@ export default function FilesUpload() {
// 检查并准备上传
const checkAndPrepareUpload = (mainFiles: File[], attachmentFiles: File[]) => {
try {
console.log('【调试-checkAndPrepareUpload】开始检查并准备上传文件', {
mainFilesCount: mainFiles.length,
attachmentFilesCount: attachmentFiles.length,
fileType
});
// console.log('【调试-checkAndPrepareUpload】开始检查并准备上传文件', {
// mainFilesCount: mainFiles.length,
// attachmentFilesCount: attachmentFiles.length,
// fileType
// });
// 检查组件是否已卸载
if (!isMountedRef.current) {
@@ -624,14 +624,14 @@ export default function FilesUpload() {
const selectedType = documentTypes.find(t => t.id.toString() === fileType);
const isContract = !!(selectedType && selectedType.name.includes('合同'));
console.log('【调试-checkAndPrepareUpload】文件类型检查', {
selectedType,
isContract,
typeName: selectedType?.name
});
// console.log('【调试-checkAndPrepareUpload】文件类型检查', {
// selectedType,
// isContract,
// typeName: selectedType?.name
// });
if (isContract) {
console.log('【调试-checkAndPrepareUpload】合同文档类型特殊处理');
// console.log('【调试-checkAndPrepareUpload】合同文档类型特殊处理');
// 检查主文件
if(mainFiles.length === 0) {
@@ -641,11 +641,11 @@ export default function FilesUpload() {
}
// 记录主文件和附件文件信息
console.log('【调试-checkAndPrepareUpload】合同主文件:', mainFiles.map(f => ({ name: f.name, size: f.size, type: f.type })));
// console.log('【调试-checkAndPrepareUpload】合同主文件:', mainFiles.map(f => ({ name: f.name, size: f.size, type: f.type })));
if (attachmentFiles.length > 0) {
console.log('【调试-checkAndPrepareUpload】合同附件文件:', attachmentFiles.map(f => ({ name: f.name, size: f.size, type: f.type })));
// console.log('【调试-checkAndPrepareUpload】合同附件文件:', attachmentFiles.map(f => ({ name: f.name, size: f.size, type: f.type })));
} else {
console.log('【调试-checkAndPrepareUpload】无合同附件文件');
// console.log('【调试-checkAndPrepareUpload】无合同附件文件');
}
}
@@ -658,7 +658,7 @@ export default function FilesUpload() {
allFiles = [...allFiles, ...attachmentFiles];
}
console.log('【调试-checkAndPrepareUpload】合并文件后总数:', allFiles.length);
// console.log('【调试-checkAndPrepareUpload】合并文件后总数:', allFiles.length);
// 检查组件是否已卸载
if (!isMountedRef.current) {
@@ -670,7 +670,7 @@ export default function FilesUpload() {
setCurrentFiles(allFiles);
// 将准备上传的操作移到这里,暂时不执行
console.log('【调试-checkAndPrepareUpload】准备上传', allFiles.length, '个文件');
// console.log('【调试-checkAndPrepareUpload】准备上传', allFiles.length, '个文件');
if (fileType) {
try {
@@ -680,7 +680,7 @@ export default function FilesUpload() {
return;
}
console.log('【调试-checkAndPrepareUpload】开始调用startUpload函数');
// console.log('【调试-checkAndPrepareUpload】开始调用startUpload函数');
// 使用 setTimeout 延迟调用,确保状态已更新
setTimeout(() => {
@@ -725,7 +725,7 @@ export default function FilesUpload() {
// 开始上传文件
const startUpload = async (files: File[]) => {
try {
console.log('【调试-startUpload】开始上传过程,文件数量:', files.length);
// console.log('【调试-startUpload】开始上传过程,文件数量:', files.length);
// 检查组件是否已卸载
if (!isMountedRef.current) {
@@ -750,7 +750,7 @@ export default function FilesUpload() {
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
let uploadedSize = 0;
console.log('【调试-startUpload】总文件大小:', formatFileSize(totalSize));
// console.log('【调试-startUpload】总文件大小:', formatFileSize(totalSize));
// 更新步骤状态
const updatedSteps = [...processingSteps];
@@ -761,12 +761,12 @@ export default function FilesUpload() {
if (isMountedRef.current) {
setProcessingSteps(updatedSteps);
} else {
console.log('【调试-startUpload】组件已卸载,不更新处理步骤');
// console.log('【调试-startUpload】组件已卸载,不更新处理步骤');
return;
}
// 转换文件为二进制格式
console.log("【调试-startUpload】开始转换文件到二进制格式...");
// console.log("【调试-startUpload】开始转换文件到二进制格式...");
// 模拟上传进度
if (progressIntervalRef.current) {
@@ -795,21 +795,21 @@ export default function FilesUpload() {
for (const file of files) {
try {
console.log(`【调试-startUpload】准备上传文件: ${file.name}, 大小: ${formatFileSize(file.size)}`);
// console.log(`【调试-startUpload】准备上传文件: ${file.name}, 大小: ${formatFileSize(file.size)}`);
// 转换文件为二进制格式
console.log(`【调试-startUpload】开始转换文件 ${file.name} 为二进制格式`);
// console.log(`【调试-startUpload】开始转换文件 ${file.name} 为二进制格式`);
let binaryData: ArrayBuffer;
try {
binaryData = await uploadFileToBinary(file);
console.log(`【调试-startUpload】文件 ${file.name} 二进制转换成功,大小: ${binaryData.byteLength} 字节`);
// console.log(`【调试-startUpload】文件 ${file.name} 二进制转换成功,大小: ${binaryData.byteLength} 字节`);
} catch (binaryError) {
console.error(`【调试-startUpload】文件 ${file.name} 二进制转换失败:`, binaryError);
throw new Error(`文件 ${file.name} 转换失败: ${binaryError instanceof Error ? binaryError.message : '未知错误'}`);
}
let response: FileUploadResponse;
console.log(`【调试-startUpload】开始上传文件 ${file.name} 到服务器,文件类型: ${fileType}`);
// console.log(`【调试-startUpload】开始上传文件 ${file.name} 到服务器,文件类型: ${fileType}`);
try {
// 上传文件
@@ -819,7 +819,7 @@ export default function FilesUpload() {
return { success: false, error: '组件已卸载' } as FileUploadResponse;
}
console.log(`【调试-startUpload】准备上传文件 ${file.name} 到服务器`);
// console.log(`【调试-startUpload】准备上传文件 ${file.name} 到服务器`);
// 使用Promise.race添加超时处理
const uploadPromise = uploadFileToServer(
@@ -848,7 +848,7 @@ export default function FilesUpload() {
return;
}
console.log(`【调试-startUpload】文件 ${file.name} 上传响应:`, response);
// console.log(`【调试-startUpload】文件 ${file.name} 上传响应:`, response);
} catch (error) {
// 检查组件是否已卸载
if (!isMountedRef.current) {
@@ -884,7 +884,7 @@ export default function FilesUpload() {
}
};
console.log(`【调试-startUpload】文件 ${file.name} 上传成功,文件ID: ${newFile.id}`);
// console.log(`【调试-startUpload】文件 ${file.name} 上传成功,文件ID: ${newFile.id}`);
uploadedFiles.push(newFile);
} catch (fileError) {
console.error(`【调试-startUpload】处理文件 ${file.name} 时发生错误:`, fileError);
@@ -916,14 +916,14 @@ export default function FilesUpload() {
};
});
console.log(`【调试-startUpload】所有文件上传完成,更新队列`);
// console.log(`【调试-startUpload】所有文件上传完成,更新队列`);
setQueueFiles(prev => [...newDocuments, ...prev]);
// 设置当前文件为已上传的文件
setCompletedFiles(uploadedFiles);
// 完成上传后开始处理流程
console.log(`【调试-startUpload】开始文件处理流程`);
// console.log(`【调试-startUpload】开始文件处理流程`);
startProcessing(uploadedFiles);
} catch (error) {
console.error("【调试-startUpload】文件上传过程发生错误:", error);
@@ -959,7 +959,7 @@ export default function FilesUpload() {
// 开始处理上传的文件
const startProcessing = (files: UploadedFile[]) => {
try {
console.log('【调试-startProcessing】开始处理上传的文件:', files.length, '个文件');
// console.log('【调试-startProcessing】开始处理上传的文件:', files.length, '个文件');
// 检查组件是否已卸载
if (!isMountedRef.current) {
@@ -981,14 +981,14 @@ export default function FilesUpload() {
// 获取文件ID列表
const fileIds = files.map(file => file.id).filter(id => id > 0);
console.log('【调试-startProcessing】文件ID列表:', fileIds);
// console.log('【调试-startProcessing】文件ID列表:', fileIds);
if (fileIds.length === 0) {
console.error('【调试-startProcessing】没有有效的文件ID,无法开始处理');
throw new Error('没有有效的文件ID,无法开始处理');
}
console.log('【调试-startProcessing】开始处理文件,设置文件处理进度定时器');
// console.log('【调试-startProcessing】开始处理文件,设置文件处理进度定时器');
// 清除之前的进度定时器(如果存在)
if (progressIntervalRef.current) {
@@ -997,7 +997,7 @@ export default function FilesUpload() {
// 立即开始检查状态
try {
console.log('【调试-startProcessing】立即开始检查处理状态');
// console.log('【调试-startProcessing】立即开始检查处理状态');
checkProcessingStatus(fileIds);
} catch (statusError) {
console.error('【调试-startProcessing】首次检查状态失败:', statusError);
@@ -1005,7 +1005,7 @@ export default function FilesUpload() {
// 设置文件处理进度定时器,每10秒检查一次状态
progressIntervalRef.current = setInterval(() => {
console.log('【调试-startProcessing】文件处理进度定时器触发,检查文件状态');
// console.log('【调试-startProcessing】文件处理进度定时器触发,检查文件状态');
try {
checkProcessingStatus(fileIds);
} catch (intervalError) {
@@ -1050,7 +1050,7 @@ export default function FilesUpload() {
// 检查文件处理状态
const checkProcessingStatus = async (fileIds: number[]) => {
try {
console.log('【调试-checkProcessingStatus】检查文件处理状态:', fileIds);
// console.log('【调试-checkProcessingStatus】检查文件处理状态:', fileIds);
// 检查组件是否已卸载
if (!isMountedRef.current) {
@@ -1060,12 +1060,12 @@ export default function FilesUpload() {
// 如果没有文件ID,不执行检查
if (!fileIds.length) {
console.log('【调试-checkProcessingStatus】没有需要检查的文件');
// console.log('【调试-checkProcessingStatus】没有需要检查的文件');
return;
}
// 获取文件状态
console.log('【调试-checkProcessingStatus】发送请求获取文件状态');
// console.log('【调试-checkProcessingStatus】发送请求获取文件状态');
const response = await getDocumentsStatus(fileIds);
if (response.error) {
@@ -1073,26 +1073,26 @@ export default function FilesUpload() {
return;
}
console.log('【调试-checkProcessingStatus】文件状态响应:', response.data);
// console.log('【调试-checkProcessingStatus】文件状态响应:', response.data);
if (!response.data || !response.data.length) {
console.log('【调试-checkProcessingStatus】没有返回文件状态数据');
// console.log('【调试-checkProcessingStatus】没有返回文件状态数据');
return;
}
// 检查是否所有文件都已完成处理
const allCompleted = response.data.every(doc => doc.status === DocumentStatus.PROCESSED);
console.log('【调试-checkProcessingStatus】文件处理状态:', { allCompleted, statusList: response.data.map(doc => doc.status) });
// console.log('【调试-checkProcessingStatus】文件处理状态:', { allCompleted, statusList: response.data.map(doc => doc.status) });
// 更新步骤状态
if (allCompleted) {
console.log('【调试-checkProcessingStatus】所有文件处理完成,更新步骤状态为完成');
// console.log('【调试-checkProcessingStatus】所有文件处理完成,更新步骤状态为完成');
// 清除文件处理进度定时器
if (progressIntervalRef.current) {
clearInterval(progressIntervalRef.current);
progressIntervalRef.current = null;
console.log('【调试-checkProcessingStatus】文件处理完成,清除文件处理进度定时器');
// console.log('【调试-checkProcessingStatus】文件处理完成,清除文件处理进度定时器');
}
// 更新为全部完成状态
@@ -1112,7 +1112,7 @@ export default function FilesUpload() {
} else {
// 根据当前状态更新步骤
const currentStatus = response.data[0].status;
console.log('【调试-checkProcessingStatus】根据当前状态更新步骤:', currentStatus);
// console.log('【调试-checkProcessingStatus】根据当前状态更新步骤:', currentStatus);
updateProcessingSteps(currentStatus);
}
@@ -1127,7 +1127,7 @@ export default function FilesUpload() {
// 更新处理步骤状态
const updateProcessingSteps = (status: DocumentStatus) => {
console.log('更新处理步骤状态:', status);
// console.log('更新处理步骤状态:', status);
const updatedSteps = [...processingSteps];
@@ -1182,7 +1182,7 @@ export default function FilesUpload() {
const updateQueueFilesStatus = (updatedDocs: Document[]) => {
if (!updatedDocs.length) return;
console.log('更新队列中文件状态:', updatedDocs);
// console.log('更新队列中文件状态:', updatedDocs);
setQueueFiles(prevFiles => {
// 创建文件ID到状态的映射
@@ -1273,18 +1273,18 @@ export default function FilesUpload() {
// 处理查看文件
const handleViewFile = async (record: Document) => {
try {
console.log('【调试-handleViewFile】开始处理查看文件,文件ID:', record.id);
// console.log('【调试-handleViewFile】开始处理查看文件,文件ID:', record.id);
// 检查audit_status是否为0,如果是则更新为2
if (record.audit_status === 0 || record.audit_status === null) {
try {
console.log('【调试-handleViewFile】更新文件审核状态,文件ID:', record.id);
// console.log('【调试-handleViewFile】更新文件审核状态,文件ID:', record.id);
const response = await updateDocumentAuditStatus(record.id.toString(), 2);
if (response.error) {
console.error('【调试-handleViewFile】更新文件审核状态失败:', response.error);
toastService.error('更新文件审核状态失败:' + (response.error || '未知错误'));
} else {
console.log('【调试-handleViewFile】更新文件审核状态成功');
// console.log('【调试-handleViewFile】更新文件审核状态成功');
}
} catch (error) {
@@ -1294,7 +1294,7 @@ export default function FilesUpload() {
}
}
console.log(`【调试-handleViewFile】准备导航到文件详情页,文件ID: ${record.id}`);
// console.log(`【调试-handleViewFile】准备导航到文件详情页,文件ID: ${record.id}`);
// 检查组件是否已卸载
if (!isMountedRef.current) {
@@ -1306,7 +1306,7 @@ export default function FilesUpload() {
setTimeout(() => {
try {
if (isMountedRef.current) {
console.log(`【调试-handleViewFile】执行导航,URL: /reviews?id=${record.id}&previousRoute=filesUpload`);
// console.log(`【调试-handleViewFile】执行导航,URL: /reviews?id=${record.id}&previousRoute=filesUpload`);
navigate(`/reviews?id=${record.id}&previousRoute=filesUpload`);
} else {
console.error('【调试-handleViewFile】组件已卸载,取消延迟导航');
+7 -7
View File
@@ -1,5 +1,5 @@
// import React from 'react';
import { type MetaFunction, type LoaderFunctionArgs, redirect } from "@remix-run/node";
import { type MetaFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { Card } from "~/components/ui/Card";
import { Button } from "~/components/ui/Button";
@@ -43,7 +43,7 @@ export const meta: MetaFunction = () => {
// }
// 添加认证检查
export async function loader({ request }: LoaderFunctionArgs) {
export async function loader() {
// 检查用户登录状态
// const { isAuthenticated } = await getUserSession(request);
@@ -65,11 +65,11 @@ export async function loader({ request }: LoaderFunctionArgs) {
return Response.json({ error: responseDocuments.error }, { status: responseDocuments.status || 500 });
}
const recentFiles = responseDocuments.data?.documents || [];
console.log("recentFiles-------",recentFiles);
// console.log("recentFiles-------",recentFiles);
const homeData = await getHomeData();
console.log("homeData-------",homeData);
// console.log("homeData-------",homeData);
return Response.json({ homeData, recentFiles });
@@ -125,7 +125,7 @@ export default function Home() {
order: 'updated_at.desc'
};
console.log('定时获取最新文档数据...');
// console.log('定时获取最新文档数据...');
const responseDocuments = await getDocuments(documentSearchParams);
if (responseDocuments.error) {
@@ -138,7 +138,7 @@ export default function Home() {
// 检查数据是否有变化
if (JSON.stringify(newRecentFiles) !== JSON.stringify(recentFiles)) {
console.log('文档数据已更新,直接更新状态');
// console.log('文档数据已更新,直接更新状态');
// 直接更新状态,不需要刷新页面
setRecentFiles(newRecentFiles);
}
@@ -152,7 +152,7 @@ export default function Home() {
// 组件卸载时清除定时器
return () => {
console.log('清除文档数据自动更新定时器');
// console.log('清除文档数据自动更新定时器');
clearInterval(timerID);
};
}, []); // 不再依赖recentFiles,避免循环依赖
+27 -8
View File
@@ -1,8 +1,8 @@
import { useState } from "react";
import { Form, useActionData, useNavigation } from "@remix-run/react";
import { type MetaFunction, type ActionFunctionArgs, redirect, json, type LoaderFunctionArgs } from "@remix-run/node";
import { type MetaFunction, type ActionFunctionArgs, redirect, type LoaderFunctionArgs } from "@remix-run/node";
import styles from "~/styles/pages/login.css?url";
import { createUserSession, getUserSession, getSession } from "~/root";
import { createUserSession, getUserSession, getSession, type UserRole } from "~/root";
export const links = () => [
{ rel: "stylesheet", href: styles }
@@ -20,21 +20,24 @@ export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const username = formData.get("username") as string;
const password = formData.get("password") as string;
const userRole = formData.get("userRole") as UserRole || 'common';
// 简单的登录验证,实际应用中应该进行真正的身份验证
if (!username || !password) {
return json({ error: "用户名和密码不能为空" });
return Response.json({ error: "用户名和密码不能为空" });
}
// 在实际应用中,这里应该是对用户名和密码的验证逻辑
// 简化起见,我们直接视为登录成功
// 获取session中存储的重定向URL,如果没有则默认到/home
// 获取session中存储的重定向URL,如果没有则默认到/
const session = await getSession(request);
const redirectTo = session.get("redirectTo") || "/home";
// 查看session中存储的redirectTo值
const redirectTo = session.get("redirectTo") || "/";
// console.log("登录后重定向到:", redirectTo);
// 创建登录会话并重定向
return createUserSession(true, redirectTo);
return createUserSession(true, userRole, redirectTo);
}
// 加载器,获取当前会话状态
@@ -43,7 +46,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
// 如果已登录,重定向到首页
if (isAuthenticated) {
return redirect("/home");
return redirect("/");
}
return Response.json({ isAuthenticated });
@@ -52,6 +55,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
export default function Login() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [userRole, setUserRole] = useState<UserRole>("common");
const actionData = useActionData<typeof action>();
const navigation = useNavigation();
@@ -62,7 +66,7 @@ export default function Login() {
<div className="login-page">
<div className="login-container">
<div className="login-header">
<img src="/logo.png" alt="中国烟草" className="login-logo" />
{/* <img src="/logo.png" alt="中国烟草" className="login-logo" /> */}
<h1 className="login-title">AI合同及卷宗审核系统</h1>
</div>
@@ -101,6 +105,21 @@ export default function Login() {
/>
</div>
<div className="form-group">
<label htmlFor="userRole"></label>
<select
id="userRole"
name="userRole"
value={userRole}
onChange={(e) => setUserRole(e.target.value as UserRole)}
className="form-input"
required
>
<option value="common"></option>
{/* <option value="developer">开发者</option> */}
</select>
</div>
<button
type="submit"
className="login-button"
+2 -2
View File
@@ -54,7 +54,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
const page = parseInt(url.searchParams.get('page') || '1', 10);
const pageSize = parseInt(url.searchParams.get('pageSize') || '10', 10);
console.log('加载提示词模板参数:', { name, type, status, page, pageSize });
// console.log('加载提示词模板参数:', { name, type, status, page, pageSize });
// 从 API 获取数据
const result = await getPromptTemplates({
@@ -79,7 +79,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
);
}
console.log(`成功加载${result.data?.templates.length || 0}条提示词模板数据`);
// console.log(`成功加载${result.data?.templates.length || 0}条提示词模板数据`);
return Response.json({
templates: result.data?.templates || [],
+6 -6
View File
@@ -389,11 +389,11 @@ export default function ReviewDetails() {
return;
}
console.log('评查点状态更新成功:', {
id: reviewPointResultId,
result: boolResult,
message: message
});
// console.log('评查点状态更新成功:', {
// id: reviewPointResultId,
// result: boolResult,
// message: message
// });
// 更新本地状态
if (reviewData) {
@@ -451,7 +451,7 @@ export default function ReviewDetails() {
toastService.success('评查点状态已更新');
}
console.log("newReviewPoints",updatedReviewPoints);
// console.log("newReviewPoints",updatedReviewPoints);
// 如果是review操作才调用API刷新
// if (document && document.id && newStatus === 'review') {
+2 -2
View File
@@ -89,11 +89,11 @@ function mapApiToFrontend(apiGroup: ApiRuleGroup): RuleGroup {
// 数据加载器
export async function loader({ request }: LoaderFunctionArgs) {
console.log("rule-groups.new loader被调用,URL:", request.url);
// console.log("rule-groups.new loader被调用,URL:", request.url);
try {
const url = new URL(request.url);
const id = url.searchParams.get("id");
console.log("获取到的ID参数:", id);
// console.log("获取到的ID参数:", id);
// 获取一级分组列表 (用于选择父级分组)
const parentGroupsResponse = await getRuleGroups();
+21 -9
View File
@@ -50,6 +50,7 @@ import type { EvaluationPointGroup } from "~/models/evaluation_point_groups";
import { RuleContext } from "~/contexts/RuleContext";
import { postgrestGet, postgrestPost, postgrestPut } from "~/api/postgrest-client";
import { toastService } from '~/components/ui/Toast';
import type { UserRole } from '~/root';
export const meta: MetaFunction = () => {
return [
@@ -152,10 +153,13 @@ export default function RuleNew() {
const [isEditMode, setIsEditMode] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [instanceKey, setInstanceKey] = useState<string>('new');
const [userRole, setUserRole] = useState<UserRole>('common');
const [formData, setFormData] = useState<EvaluationPoint>({});
const [evaluationPointGroups, setEvaluationPointGroups] = useState<EvaluationPointGroup[]>([]);
// 检查用户是否为开发者角色
const isDeveloper = userRole === 'developer';
// 添加用于共享的字段数据状态
const [extractionFields, setExtractionFields] = useState<string[]>([]);
@@ -195,7 +199,7 @@ export default function RuleNew() {
*
*/
const resetFormData = useCallback(() => {
console.log("重置表单数据到默认状态");
// console.log("重置表单数据到默认状态");
setFormData({
name: '',
code: '',
@@ -250,7 +254,7 @@ export default function RuleNew() {
const fetchEvaluationPoint = useCallback(async (id: number) => {
try {
setIsLoading(true);
console.log(`获取评查点数据,ID: ${id}`);
// console.log(`获取评查点数据,ID: ${id}`);
// 使用 postgrestGet 替代直接调用 fetch
const postgrestParams = {
filter: {
@@ -268,7 +272,7 @@ export default function RuleNew() {
const jsonString = JSON.stringify(originalData);
const data = JSON.parse(jsonString);
console.log("数据已经过深拷贝处理,避免浏览器兼容性问题");
// console.log("数据已经过深拷贝处理,避免浏览器兼容性问题");
// 设置表单数据
setFormData(data);
@@ -311,7 +315,7 @@ export default function RuleNew() {
*/
const fetchEvaluationPointGroups = useCallback(async () => {
try {
console.log("获取评查点组数据");
// console.log("获取评查点组数据");
const response = await postgrestGet('evaluation_point_groups');
if (response.data && Array.isArray(response.data) && response.data.length > 0) {
@@ -325,7 +329,7 @@ export default function RuleNew() {
}, []);
const handleSave = async () => {
console.log("保存评查点", formData);
// console.log("保存评查点", formData);
// 验证必填字段
if (!formData.name?.trim()) {
@@ -405,7 +409,7 @@ export default function RuleNew() {
// 去重,确保不会有重复字段
const validFields = [...new Set(currentExtractionFields)];
console.log("当前有效的抽取字段:", validFields);
// console.log("当前有效的抽取字段:", validFields);
// 重要:这段代码解决了字段删除后,评查配置中仍保留已删除字段的问题
// 在保存前,我们会确保所有规则中引用的字段都是当前有效的抽取字段
@@ -548,8 +552,8 @@ export default function RuleNew() {
throw new Error(`数据大小超过限制 (${dataLength} > ${maxLength})`);
}
console.log("准备提交到API的数据(已经过深拷贝处理):", finalData);
console.log("JSON数据长度:", dataLength);
// console.log("准备提交到API的数据(已经过深拷贝处理):", finalData);
// console.log("JSON数据长度:", dataLength);
let response;
if (isEditMode) {
@@ -706,6 +710,12 @@ export default function RuleNew() {
const id = searchParams.get('id');
const mode = searchParams.get('mode');
// 从sessionStorage获取用户角色
if (typeof window !== 'undefined') {
const userRoleFromSession = sessionStorage.getItem('userRole') as UserRole || 'common';
setUserRole(userRoleFromSession);
}
// 编辑或复制模式下设置加载状态
if (id || mode === 'copy') {
setIsLoading(true);
@@ -737,6 +747,7 @@ export default function RuleNew() {
<PageHeader
title={isEditMode ? "编辑评查点" : "新增评查点"}
onSave={handleSave}
showSaveButton={isDeveloper}
/>
{/* 加载状态显示 */}
@@ -805,6 +816,7 @@ export default function RuleNew() {
onSave={handleSave}
onSaveDraft={handleSaveDraft}
isEditMode={isEditMode}
showButtons={isDeveloper}
/>
</div>
</RuleContext.Provider>
+39 -16
View File
@@ -22,6 +22,7 @@ import {
type RuleType as ApiRuleType,
type RuleGroup
} from '~/api/evaluation_points/rules';
import type { UserRole } from '~/root';
export const links = () => [
{ rel: "stylesheet", href: rulesStyles }
@@ -44,6 +45,7 @@ export type LoaderData = {
pageSize: number;
totalPages: number;
ruleTypes: ApiRuleType[]; // 添加评查点类型
userRole: UserRole; // 添加用户角色
};
// API返回的数据映射到前端模型
@@ -118,13 +120,19 @@ export async function loader({ request }: LoaderFunctionArgs) {
const apiRules = response.data?.rules || [];
const totalCount = response.data?.totalCount || 0;
const rules = apiRules.map((apiRule: ApiRule) => mapApiRuleToModel(apiRule));
// 从sessionStorage获取用户角色
const userRoleFromSession = typeof document !== 'undefined'
? sessionStorage.getItem('userRole') || 'common'
: 'common';
return Response.json({
rules,
totalCount,
currentPage: params.page,
pageSize: params.pageSize,
ruleTypes
ruleTypes,
userRole: userRoleFromSession as UserRole
}, {
headers: {
"Cache-Control": "max-age=60, s-maxage=180"
@@ -152,7 +160,7 @@ export async function action({ request }: LoaderFunctionArgs) {
try {
if (_action === 'delete') {
// 调用API删除评查点
console.log(`删除评查点 ${ruleId}`);
// console.log(`删除评查点 ${ruleId}`);
const deleteResponse = await deleteRule(ruleId as string);
@@ -178,12 +186,15 @@ const priorityLabels = {
export default function RulesIndex() {
const loaderData = useLoaderData<typeof loader>();
const { rules, totalCount, currentPage, pageSize } = loaderData;
const { rules, totalCount, currentPage, pageSize, userRole } = loaderData;
const ruleTypes = loaderData.ruleTypes || []; // 添加默认空数组避免undefined
const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate();
const fetcher = useFetcher<ActionResponse>();
// 检查用户是否为开发者角色
const isDeveloper = userRole === 'developer';
// 状态管理
const [ruleGroups, setRuleGroups] = useState<RuleGroup[]>([]);
const [loadingGroups, setLoadingGroups] = useState(false);
@@ -240,7 +251,7 @@ export default function RulesIndex() {
toastService.success(fetcher.data.message);
} else if (!fetcher.data.result) {
if(fetcher.data.message.includes("evaluation_results_evaluation_point_id_fkey")) {
toastService.error("对表evaluation_points进行更新或删除违反了表evaluation results上的外键约束evaluations results_evaluation _point_id_fkey”");
toastService.error('对表evaluation_points进行更新或删除违反了表evaluation results上的外键约束evaluations results_evaluation _point_id_fkey');
} else {
toastService.error(fetcher.data.message);
}
@@ -424,15 +435,25 @@ export default function RulesIndex() {
width: "10%",
render: (_: unknown, record: Rule) => (
<div className="operations-cell">
<Link to={`/rules-new?id=${record.id}`} className="operation-btn">
<i className="ri-edit-line"></i>
</Link>
<button className="operation-btn" onClick={() => handleCopy(record)}>
<i className="ri-file-copy-line"></i>
</button>
<button className="operation-btn operation-btn-danger" onClick={() => handleDeleteClick(record)}>
<i className="ri-delete-bin-line"></i>
</button>
{isDeveloper ? (
// 开发者可以看到编辑、复制、删除
<>
<Link to={`/rules-new?id=${record.id}`} className="operation-btn">
<i className="ri-edit-line"></i>
</Link>
<button className="operation-btn" onClick={() => handleCopy(record)}>
<i className="ri-file-copy-line"></i>
</button>
<button className="operation-btn operation-btn-danger" onClick={() => handleDeleteClick(record)}>
<i className="ri-delete-bin-line"></i>
</button>
</>
) : (
// 普通用户只能查看
<Link to={`/rules-new?id=${record.id}&mode=view`} className="operation-btn">
<i className="ri-eye-line"></i>
</Link>
)}
</div>
)
}
@@ -443,9 +464,11 @@ export default function RulesIndex() {
{/* 页面头部 */}
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-medium"></h2>
<Button type="primary" icon="ri-add-line" to="/rules-new" className="btn-add-rule">
</Button>
{isDeveloper && (
<Button type="primary" icon="ri-add-line" to="/rules-new" className="btn-add-rule">
</Button>
)}
</div>
{/* 筛选区域 */}
+2 -2
View File
@@ -169,7 +169,7 @@ export default function Documents() {
*/
function onDocumentLoadSuccess({ numPages }: DocumentLoadSuccess) {
setNumPages(numPages);
console.log("PDF加载成功,页数:", numPages);
// console.log("PDF加载成功,页数:", numPages);
}
/**
@@ -177,7 +177,7 @@ export default function Documents() {
* @param info
*/
const addDebugInfo = (info: string) => {
console.log(info);
// console.log(info);
setDebugInfo(prev => [...prev, `${new Date().toISOString().split('T')[1].split('.')[0]}: ${info}`]);
};
+50 -1
View File
@@ -13,6 +13,7 @@
max-width: 320px;
transition: opacity 0.3s ease, visibility 0.3s ease;
pointer-events: none;
overflow: visible; /* 确保容器不显示滚动条 */
}
.tooltip-visible {
@@ -23,11 +24,57 @@
.tooltip-content {
position: relative;
padding: 0.75rem 1rem;
padding: 0.75rem 0.5rem 0.75rem 0.75rem;
border-radius: 0.375rem;
font-size: 0.875rem;
line-height: 1.5;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
overflow: visible; /* 确保外层容器不显示滚动条 */
}
.tooltip-content-inner {
width: 100%;
position: relative;
}
/* 可滚动的提示框内容样式 */
.tooltip-scrollable {
overflow-y: auto;
scrollbar-width: none; /* Firefox */
scrollbar-color: rgba(0, 0, 0, 0.3) transparent;
/* 创建一个合适的内边距来容纳自定义滚动条 */
padding-right: 6px;
}
/* 隐藏默认滚动条 */
.tooltip-scrollable::-webkit-scrollbar {
width: 3px;
}
.tooltip-scrollable::-webkit-scrollbar-track {
background: transparent;
}
.tooltip-scrollable::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2);
border-radius: 3px;
}
.tooltip-scrollable:hover::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.3);
}
/* 深色主题滚动条样式 */
.tooltip-dark .tooltip-scrollable::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.2);
}
.tooltip-dark .tooltip-scrollable:hover::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.3);
}
.tooltip-dark .tooltip-scrollable {
scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
}
.tooltip-arrow {
@@ -159,6 +206,8 @@
.tooltip-body {
padding: 0.75rem 1rem;
max-height: inherit;
overflow-y: inherit;
}
.tooltip-footer {
+2 -2
View File
@@ -72,11 +72,11 @@ export function getArrayDifference<T>(current: T[], previous: T[]): { added: T[]
// 格式化日期时间
// 格式化日期时间utc的格林威治时间
export function formatDate(dateTime: string): string {
if (!dateTime) return '';
try {
return dayjs.utc(dateTime).format('YYYY-MM-DD HH:mm:ss');
return dayjs(dateTime).local().format('YYYY-MM-DD HH:mm:ss');
} catch (error) {
console.error('日期格式化失败:', error);
return dateTime;
+12 -12
View File
@@ -673,20 +673,20 @@ function logPostgrestQuery(endpoint: string, params?: QueryParams, method: strin
const baseUrl = 'http://nas.7bm.co:3000';
const normalizedEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
console.log('\n📦 PostgREST 查询日志 ======================start=============');
console.log(`📦 HTTP 方法: ${method}`);
console.log(`📦 API 端点: ${decodeUrlForDisplay(`${baseUrl}/${normalizedEndpoint}`)}`);
// console.log('\n📦 PostgREST 查询日志 ======================start=============');
// console.log(`📦 HTTP 方法: ${method}`);
// console.log(`📦 API 端点: ${decodeUrlForDisplay(`${baseUrl}/${normalizedEndpoint}`)}`);
if (params && Object.keys(params).length > 0) {
console.log('📦 查询参数:');
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
console.log(` - ${key}: ${JSON.stringify(value)}`);
}
});
}
// if (params && Object.keys(params).length > 0) {
// console.log('📦 查询参数:');
// Object.entries(params).forEach(([key, value]) => {
// if (value !== undefined) {
// console.log(` - ${key}: ${JSON.stringify(value)}`);
// }
// });
// }
console.log('PostgREST 查询日志=============================end============\n');
// console.log('PostgREST 查询日志=============================end============\n');
}
}
```
+5 -5
View File
@@ -1512,7 +1512,7 @@
const textarea = this.closest('.search-box').querySelector('.search-textarea');
const query = textarea.value.trim();
if (query) {
console.log('搜索查询:', query);
// console.log('搜索查询:', query);
// 这里可以添加实际的搜索逻辑
}
});
@@ -1523,7 +1523,7 @@
categoryCards.forEach(card => {
card.addEventListener('click', function() {
const title = this.querySelector('.category-title').textContent;
console.log('选择分类:', title);
// console.log('选择分类:', title);
});
});
@@ -1533,7 +1533,7 @@
card.addEventListener('click', function(e) {
if (!e.target.closest('button')) {
const title = this.querySelector('.template-title').textContent;
console.log('查看模板详情:', title);
// console.log('查看模板详情:', title);
}
});
});
@@ -1544,7 +1544,7 @@
btn.addEventListener('click', function(e) {
e.stopPropagation();
const action = this.textContent.trim();
console.log('执行操作:', action);
// console.log('执行操作:', action);
});
});
@@ -1554,7 +1554,7 @@
tab.addEventListener('click', function() {
filterTabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
console.log('筛选:', this.textContent);
// console.log('筛选:', this.textContent);
});
});
+5 -5
View File
@@ -1514,7 +1514,7 @@
const textarea = this.closest('.search-box').querySelector('.search-textarea');
const query = textarea.value.trim();
if (query) {
console.log('搜索查询:', query);
// console.log('搜索查询:', query);
// 这里可以添加实际的搜索逻辑
}
});
@@ -1525,7 +1525,7 @@
categoryCards.forEach(card => {
card.addEventListener('click', function() {
const title = this.querySelector('.category-title').textContent;
console.log('选择分类:', title);
// console.log('选择分类:', title);
});
});
@@ -1535,7 +1535,7 @@
card.addEventListener('click', function(e) {
if (!e.target.closest('button')) {
const title = this.querySelector('.template-title').textContent;
console.log('查看模板详情:', title);
// console.log('查看模板详情:', title);
}
});
});
@@ -1546,7 +1546,7 @@
btn.addEventListener('click', function(e) {
e.stopPropagation();
const action = this.textContent.trim();
console.log('执行操作:', action);
// console.log('执行操作:', action);
});
});
@@ -1556,7 +1556,7 @@
tab.addEventListener('click', function() {
filterTabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
console.log('筛选:', this.textContent);
// console.log('筛选:', this.textContent);
});
});
+2 -2
View File
@@ -537,7 +537,7 @@
const type = document.getElementById('search-type').value;
const status = document.getElementById('search-status').value;
console.log('搜索条件:', {
// console.log('搜索条件:', {
name: name,
type: type,
status: status
@@ -576,7 +576,7 @@
function deleteTemplate(id) {
if (confirm('确定要删除该模板吗?删除后无法恢复。')) {
// 实际应该调用API删除数据
console.log('删除模板ID:', id);
// console.log('删除模板ID:', id);
alert('删除成功!');
// 刷新页面
window.location.reload();
+1 -1
View File
@@ -700,7 +700,7 @@
created_by: 'admin' // 当前登录用户
};
console.log('保存数据:', formData);
// console.log('保存数据:', formData);
// 实际应用中应使用AJAX请求保存数据
alert('保存成功!');
+1 -1
View File
@@ -502,7 +502,7 @@
};
// 实际应用中应该使用AJAX请求发送数据
console.log('保存文档修改:', formData);
// console.log('保存文档修改:', formData);
// 模拟保存成功
alert('文档信息已成功更新!');
+6 -6
View File
@@ -499,7 +499,7 @@
const fileName = tr.querySelector('.file-name').textContent;
if(confirm(`确认删除文档 "${fileName}"`)) {
console.log('删除文档:', fileName);
// console.log('删除文档:', fileName);
// 实际应用中调用API删除文档
tr.remove();
}
@@ -518,7 +518,7 @@
}
if(confirm(`确认删除选中的 ${selectedIds.length} 个文档?`)) {
console.log('批量删除文档IDs:', selectedIds);
// console.log('批量删除文档IDs:', selectedIds);
// 实际应用中调用API批量删除文档
selectedIds.forEach(id => {
document.querySelector(`.row-checkbox[data-id="${id}"]`).closest('tr').remove();
@@ -533,7 +533,7 @@
const fileName = tr.querySelector('.file-name').textContent;
const docId = tr.querySelector('.row-checkbox').getAttribute('data-id');
console.log('查看/审核文档:', fileName, 'ID:', docId);
// console.log('查看/审核文档:', fileName, 'ID:', docId);
window.location.href = `评查详情页面.html?id=${docId}`;
});
});
@@ -545,7 +545,7 @@
const fileName = tr.querySelector('.file-name').textContent;
const docId = tr.querySelector('.row-checkbox').getAttribute('data-id');
console.log('修改文档:', fileName, 'ID:', docId);
// console.log('修改文档:', fileName, 'ID:', docId);
window.location.href = `文档-修改.html?id=${docId}`;
});
});
@@ -557,7 +557,7 @@
const tr = this.closest('tr');
const fileName = tr.querySelector('.file-name').textContent;
console.log('下载文档:', fileName);
// console.log('下载文档:', fileName);
// 实际应用中调用API下载文档
alert(`开始下载 ${fileName}`);
});
@@ -575,7 +575,7 @@
dateTo: document.querySelector('input[type="date"]:nth-of-type(2)').value
};
console.log('搜索条件:', filters);
// console.log('搜索条件:', filters);
// 实际应用中调用API搜索文档
});
+2 -2
View File
@@ -955,7 +955,7 @@
statusText.textContent = '上传成功';
// 实际应用中应该使用AJAX请求上传文件
console.log('文件上传成功:', fileName, metadata);
// console.log('文件上传成功:', fileName, metadata);
// 检查是否所有文件都已上传
const allUploaded = Array.from(document.querySelectorAll('.file-status')).every(
@@ -1010,7 +1010,7 @@
// })
// .then(response => response.json())
// .then(data => {
// console.log('上传成功:', data);
// // console.log('上传成功:', data);
// statusText.textContent = '上传成功';
// progressBar.style.width = '100%';
//
+2 -2
View File
@@ -318,7 +318,7 @@
if (!confirm('确定要删除该文档类型吗?此操作不会影响关联的评查点分组,但可能会影响使用该类型的文档评查。')) {
e.preventDefault();
} else {
console.log('删除文档类型ID:', typeId);
// console.log('删除文档类型ID:', typeId);
// 实际应调用API删除数据
alert('删除成功!');
// 刷新页面
@@ -333,7 +333,7 @@
const nameInput = document.querySelector('.form-input').value;
const groupSelect = document.querySelector('.form-select').value;
console.log('搜索条件:', {
// console.log('搜索条件:', {
name: nameInput,
groupId: groupSelect
});
+1 -1
View File
@@ -388,7 +388,7 @@
checkpoint_group_ids: selectedGroups
};
console.log('提交数据:', formData);
// console.log('提交数据:', formData);
// 实际应用中应通过AJAX提交数据
alert('保存成功!');
+3 -3
View File
@@ -2048,7 +2048,7 @@
}
// 在实际应用中应调用API保存草稿
console.log('保存人工审核草稿:', {
// console.log('保存人工审核草稿:', {
reviewId: reviewId,
opinion: opinion
});
@@ -2068,7 +2068,7 @@
}
// 在实际应用中应调用API提交审核结果
console.log('通过人工审核:', {
// console.log('通过人工审核:', {
reviewId: reviewId,
opinion: opinion,
result: 'approved'
@@ -2118,7 +2118,7 @@
}
// 在实际应用中应调用API提交审核结果
console.log('不通过人工审核:', {
// console.log('不通过人工审核:', {
reviewId: reviewId,
opinion: opinion,
result: 'rejected'
+3 -3
View File
@@ -2048,7 +2048,7 @@
}
// 在实际应用中应调用API保存草稿
console.log('保存人工审核草稿:', {
// console.log('保存人工审核草稿:', {
reviewId: reviewId,
opinion: opinion
});
@@ -2068,7 +2068,7 @@
}
// 在实际应用中应调用API提交审核结果
console.log('通过人工审核:', {
// console.log('通过人工审核:', {
reviewId: reviewId,
opinion: opinion,
result: 'approved'
@@ -2118,7 +2118,7 @@
}
// 在实际应用中应调用API提交审核结果
console.log('不通过人工审核:', {
// console.log('不通过人工审核:', {
reviewId: reviewId,
opinion: opinion,
result: 'rejected'
+1 -1
View File
@@ -870,7 +870,7 @@
.filter(f => !f.includes('(全部)') && !f.includes('上传时间 ↓'));
if (filters.length > 0) {
console.log('应用筛选:', filters.join(', '));
// console.log('应用筛选:', filters.join(', '));
}
});
});
+6 -6
View File
@@ -354,7 +354,7 @@
filterSelects.forEach(select => {
select.addEventListener('change', function() {
// 这里可以根据选择的值进行筛选,实际应用中可以通过AJAX请求后端接口
console.log('筛选条件变更:', select.value);
// console.log('筛选条件变更:', select.value);
});
});
@@ -364,7 +364,7 @@
if (searchBtn) {
searchBtn.addEventListener('click', function() {
// 这里可以根据输入的关键词进行搜索,实际应用中可以通过AJAX请求后端接口
console.log('搜索关键词:', searchInput.value);
// console.log('搜索关键词:', searchInput.value);
});
}
@@ -377,7 +377,7 @@
// 给当前点击的页码添加active状态
this.classList.add('ant-pagination-item-active');
// 这里可以根据页码加载不同页的数据,实际应用中可以通过AJAX请求后端接口
console.log('切换到页码:', this.textContent);
// console.log('切换到页码:', this.textContent);
});
});
@@ -391,7 +391,7 @@
const row = this.closest('tr');
const id = row.children[0].textContent;
// 编辑操作,可以跳转到编辑页面或打开编辑模态框
console.log('编辑评查点:', id);
// console.log('编辑评查点:', id);
window.location.href = `新增评查点.html?id=${id}&mode=edit`;
});
});
@@ -402,7 +402,7 @@
const id = row.children[0].textContent;
const name = row.children[1].textContent;
// 复制操作
console.log('复制评查点:', id, name);
// console.log('复制评查点:', id, name);
// 在实际应用中,这里可以弹出确认对话框,然后进行复制操作
alert(`确认复制评查点: ${name}?`);
});
@@ -414,7 +414,7 @@
const id = row.children[0].textContent;
const name = row.children[1].textContent;
// 删除操作
console.log('删除评查点:', id, name);
// console.log('删除评查点:', id, name);
// 在实际应用中,这里可以弹出确认对话框,然后进行删除操作
if (confirm(`确认删除评查点: ${name}?`)) {
// 执行删除操作,实际应用中可以通过AJAX请求后端接口
+19 -19
View File
@@ -825,23 +825,23 @@
});
// 调试代码 - 确保页面加载后检查所有自定义提示词容器
console.log("页面加载完成,检查自定义提示词容器");
// console.log("页面加载完成,检查自定义提示词容器");
const customPromptContainers = [
document.getElementById('llm-custom-prompt-container'),
document.getElementById('multimodal-custom-prompt-container')
];
customPromptContainers.forEach((container, index) => {
console.log(`容器 ${index + 1} 状态:`, container ? container.style.display : '未找到元素');
// console.log(`容器 ${index + 1} 状态:`, container ? container.style.display : '未找到元素');
});
// 确保提示词类型切换正常工作
console.log("设置提示词类型切换事件");
// console.log("设置提示词类型切换事件");
// 直接绑定事件到单选按钮
document.querySelectorAll('input[name="llm-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
console.log("大模型提示词类型变更:", this.value);
// console.log("大模型提示词类型变更:", this.value);
const customContainer = document.getElementById('llm-custom-prompt-container');
const systemInfo = document.getElementById('llm-system-prompt-info');
@@ -857,7 +857,7 @@
document.querySelectorAll('input[name="multimodal-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
console.log("多模态提示词类型变更:", this.value);
// console.log("多模态提示词类型变更:", this.value);
const customContainer = document.getElementById('multimodal-custom-prompt-container');
const systemInfo = document.getElementById('multimodal-system-prompt-info');
@@ -873,12 +873,12 @@
// 页面加载完成后,确保自定义提示词容器状态正确
function initPromptContainers() {
console.log("初始化提示词容器状态");
// console.log("初始化提示词容器状态");
// 大模型抽取
const llmPromptType = document.querySelector('input[name="llm-prompt-type"]:checked');
if (llmPromptType) {
console.log("当前大模型提示词类型:", llmPromptType.value);
// console.log("当前大模型提示词类型:", llmPromptType.value);
if (llmPromptType.value === 'custom') {
document.getElementById('llm-custom-prompt-container').style.display = 'block';
document.getElementById('llm-system-prompt-info').style.display = 'none';
@@ -888,7 +888,7 @@
// 多模态抽取
const multimodalPromptType = document.querySelector('input[name="multimodal-prompt-type"]:checked');
if (multimodalPromptType) {
console.log("当前多模态提示词类型:", multimodalPromptType.value);
// console.log("当前多模态提示词类型:", multimodalPromptType.value);
if (multimodalPromptType.value === 'custom') {
document.getElementById('multimodal-custom-prompt-container').style.display = 'block';
document.getElementById('multimodal-system-prompt-info').style.display = 'none';
@@ -1182,23 +1182,23 @@
});
// 调试代码 - 确保页面加载后检查所有自定义提示词容器
console.log("页面加载完成,检查自定义提示词容器");
// console.log("页面加载完成,检查自定义提示词容器");
const customPromptContainers = [
document.getElementById('llm-custom-prompt-container'),
document.getElementById('multimodal-custom-prompt-container')
];
customPromptContainers.forEach((container, index) => {
console.log(`容器 ${index + 1} 状态:`, container ? container.style.display : '未找到元素');
// console.log(`容器 ${index + 1} 状态:`, container ? container.style.display : '未找到元素');
});
// 确保提示词类型切换正常工作
console.log("设置提示词类型切换事件");
// console.log("设置提示词类型切换事件");
// 直接绑定事件到单选按钮
document.querySelectorAll('input[name="llm-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
console.log("大模型提示词类型变更:", this.value);
// console.log("大模型提示词类型变更:", this.value);
const customContainer = document.getElementById('llm-custom-prompt-container');
const systemInfo = document.getElementById('llm-system-prompt-info');
@@ -1214,7 +1214,7 @@
document.querySelectorAll('input[name="multimodal-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
console.log("多模态提示词类型变更:", this.value);
// console.log("多模态提示词类型变更:", this.value);
const customContainer = document.getElementById('multimodal-custom-prompt-container');
const systemInfo = document.getElementById('multimodal-system-prompt-info');
@@ -1230,12 +1230,12 @@
// 页面加载完成后,确保自定义提示词容器状态正确
function initPromptContainers() {
console.log("初始化提示词容器状态");
// console.log("初始化提示词容器状态");
// 大模型抽取
const llmPromptType = document.querySelector('input[name="llm-prompt-type"]:checked');
if (llmPromptType) {
console.log("当前大模型提示词类型:", llmPromptType.value);
// console.log("当前大模型提示词类型:", llmPromptType.value);
if (llmPromptType.value === 'custom') {
document.getElementById('llm-custom-prompt-container').style.display = 'block';
document.getElementById('llm-system-prompt-info').style.display = 'none';
@@ -1245,7 +1245,7 @@
// 多模态抽取
const multimodalPromptType = document.querySelector('input[name="multimodal-prompt-type"]:checked');
if (multimodalPromptType) {
console.log("当前多模态提示词类型:", multimodalPromptType.value);
// console.log("当前多模态提示词类型:", multimodalPromptType.value);
if (multimodalPromptType.value === 'custom') {
document.getElementById('multimodal-custom-prompt-container').style.display = 'block';
document.getElementById('multimodal-system-prompt-info').style.display = 'none';
@@ -2011,7 +2011,7 @@ function checkRule(data) {
const addConditionBtn = container.querySelector('.add-condition-btn');
if (addConditionBtn) {
addConditionBtn.addEventListener('click', function() {
console.log('添加条件按钮被点击');
// console.log('添加条件按钮被点击');
const conditionsContainer = container.querySelector('.conditions-container');
if (!conditionsContainer) {
@@ -2059,7 +2059,7 @@ function checkRule(data) {
});
}
console.log('已添加新的条件行');
// console.log('已添加新的条件行');
});
}
@@ -2127,7 +2127,7 @@ function checkRule(data) {
highlightedCode.textContent = codeEditor.value;
try {
hljs.highlightElement(highlightedCode);
console.log('代码块已高亮', index);
// console.log('代码块已高亮', index);
} catch (e) {
console.error('代码高亮失败:', e);
}
+1 -1
View File
@@ -375,7 +375,7 @@
if (!confirm('确定要删除该分组吗?此操作将同时删除该分组下的所有评查点,且不可恢复。')) {
e.preventDefault();
} else {
console.log('删除分组ID:', groupId);
// console.log('删除分组ID:', groupId);
// 实际应调用API删除数据
alert('删除成功!');
// 刷新页面
+1 -1
View File
@@ -462,7 +462,7 @@
}
// 实际应调用API保存数据
console.log('保存分组数据:', groupData);
// console.log('保存分组数据:', groupData);
// 模拟保存成功
alert(groupData.id ? '分组更新成功!' : '分组添加成功!');
+4 -4
View File
@@ -365,7 +365,7 @@
const environment = document.getElementById('search-environment').value;
const status = document.getElementById('search-status').value;
console.log('搜索参数:', { name, module, environment, status });
// console.log('搜索参数:', { name, module, environment, status });
// 实际应用中应通过AJAX请求获取数据并更新表格
alert('执行搜索...');
}
@@ -373,7 +373,7 @@
// 查看配置详情
function viewConfig(id) {
// 实际应用中应通过AJAX请求获取数据
console.log('查看配置:', id);
// console.log('查看配置:', id);
// 模拟数据
let configData = {
@@ -434,7 +434,7 @@
// 编辑配置
function editConfig(id) {
console.log('编辑配置:', id);
// console.log('编辑配置:', id);
window.location.href = `配置-新增.html?id=${id}`;
}
@@ -444,7 +444,7 @@
const statusText = newStatus ? '启用' : '禁用';
if (confirm(`确定要${statusText}该配置吗?`)) {
console.log('切换配置状态:', id, '新状态:', newStatus);
// console.log('切换配置状态:', id, '新状态:', newStatus);
// 实际应用中应通过AJAX请求更新状态
alert('状态已更新');
// 刷新页面或更新UI
+1 -1
View File
@@ -591,7 +591,7 @@
config_data: configDataObj
};
console.log('提交数据:', formData);
// console.log('提交数据:', formData);
// 实际应用中应通过AJAX提交数据
setTimeout(() => {
+1 -1
View File
@@ -1133,7 +1133,7 @@ var require_react_development = __commonJS({
function disableLogs() {
{
if (disabledDepth === 0) {
prevLog = console.log;
prevLog = // console.log;
prevInfo = console.info;
prevWarn = console.warn;
prevError = console.error;
File diff suppressed because one or more lines are too long
+12 -12
View File
@@ -8195,7 +8195,7 @@ function RemixRootDefaultErrorBoundary({
let heyDeveloper = /* @__PURE__ */ React4.createElement("script", {
dangerouslySetInnerHTML: {
__html: `
console.log(
// console.log(
"\u{1F4BF} Hey developer \u{1F44B}. You can provide a way better UX than this when your app throws errors. Check out https://remix.run/guides/errors for more information."
);
`
@@ -8270,7 +8270,7 @@ function RemixRootDefaultHydrateFallback() {
}, /* @__PURE__ */ React5.createElement("script", {
dangerouslySetInnerHTML: {
__html: `
console.log(
// console.log(
"\u{1F4BF} Hey developer \u{1F44B}. You can provide a way better UX than this " +
"when your app is loading JS modules and/or running \`clientLoader\` " +
"functions. Check out https://remix.run/route/hydrate-fallback " +
@@ -9493,15 +9493,15 @@ var LiveReload = (
ws.onmessage = async (message) => {
let event = JSON.parse(message.data);
if (event.type === "LOG") {
console.log(event.message);
// console.log(event.message);
}
if (event.type === "RELOAD") {
console.log("💿 Reloading window ...");
// console.log("💿 Reloading window ...");
window.location.reload();
}
if (event.type === "HMR") {
if (!window.__hmr__ || !window.__hmr__.contexts) {
console.log("💿 [HMR] No HMR context, reloading window ...");
// console.log("💿 [HMR] No HMR context, reloading window ...");
window.location.reload();
return;
}
@@ -9509,10 +9509,10 @@ var LiveReload = (
let updateAccepted = false;
let needsRevalidation = new Set();
for (let update of event.updates) {
console.log("[HMR] " + update.reason + " [" + update.id +"]")
// console.log("[HMR] " + update.reason + " [" + update.id +"]")
if (update.revalidate) {
needsRevalidation.add(update.routeId);
console.log("[HMR] Revalidating [" + update.routeId + "]");
// console.log("[HMR] Revalidating [" + update.routeId + "]");
}
let imported = await import(update.url + '?t=' + event.assetsManifest.hmr.timestamp);
if (window.__hmr__.contexts[update.id]) {
@@ -9520,7 +9520,7 @@ var LiveReload = (
imported
);
if (accepted) {
console.log("[HMR] Update accepted by", update.id);
// console.log("[HMR] Update accepted by", update.id);
updateAccepted = true;
}
}
@@ -9530,12 +9530,12 @@ var LiveReload = (
{ needsRevalidation, assetsManifest: event.assetsManifest }
);
if (accepted) {
console.log("[HMR] Update accepted by", "remix:manifest");
// console.log("[HMR] Update accepted by", "remix:manifest");
updateAccepted = true;
}
}
if (!updateAccepted) {
console.log("[HMR] Update rejected, reloading...");
// console.log("[HMR] Update rejected, reloading...");
window.location.reload();
}
}
@@ -9547,7 +9547,7 @@ var LiveReload = (
};
ws.onclose = (event) => {
if (event.code === 1006) {
console.log("Remix dev asset server web socket closed. Reconnecting...");
// console.log("Remix dev asset server web socket closed. Reconnecting...");
setTimeout(
() =>
remixLiveReloadConnect({
@@ -9558,7 +9558,7 @@ var LiveReload = (
}
};
ws.onerror = (error) => {
console.log("Remix dev asset server web socket error:");
// console.log("Remix dev asset server web socket error:");
console.error(error);
};
}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1316,7 +1316,7 @@ var require_react_dom_development = __commonJS({
function disableLogs() {
{
if (disabledDepth === 0) {
prevLog = console.log;
prevLog = // console.log;
prevInfo = console.info;
prevWarn = console.warn;
prevError = console.error;
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -176,7 +176,7 @@ var require_react_jsx_dev_runtime_development = __commonJS({
function disableLogs() {
{
if (disabledDepth === 0) {
prevLog = console.log;
prevLog = // console.log;
prevInfo = console.info;
prevWarn = console.warn;
prevError = console.error;
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -176,7 +176,7 @@ var require_react_jsx_runtime_development = __commonJS({
function disableLogs() {
{
if (disabledDepth === 0) {
prevLog = console.log;
prevLog = // console.log;
prevInfo = console.info;
prevWarn = console.warn;
prevError = console.error;
File diff suppressed because one or more lines are too long
+4
View File
@@ -21,6 +21,10 @@ export default defineConfig({
}),
tsconfigPaths(),
],
define: {
// 在构建时为客户端代码提供 process.env.NODE_ENV 变量
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || "development"),
},
server: {
host: '0.0.0.0',
port: 5173,