diff --git a/app/api/files/files-upload.ts b/app/api/files/files-upload.ts new file mode 100644 index 0000000..c317975 --- /dev/null +++ b/app/api/files/files-upload.ts @@ -0,0 +1,211 @@ +import { postgrestGet, postgrestPut, postgrestPost, type PostgrestParams } from '../postgrest-client'; +import dayjs from 'dayjs'; + +/** + * 格式化日期 + * @param dateString 日期字符串 + * @returns 格式化后的日期字符串 + */ +function formatDate(dateString: string): string { + if (!dateString) return ''; + try { + return dayjs(dateString).format('YYYY-MM-DD HH:mm:ss'); + } catch (error) { + console.error('日期格式化失败:', error); + return dateString; + } +} + + +/** + * 从不同格式的 API 响应中提取数据 + * @param responseData API 响应数据 + * @returns 提取后的数据或 null + */ +function extractApiData(responseData: unknown): T | null { + if (!responseData) return null; + + // 格式1: { code: number, msg: string, data: T } + if (typeof responseData === 'object' && responseData !== null && + 'code' in responseData && + 'data' in responseData && + (responseData as { data: unknown }).data) { + return (responseData as { data: T }).data; + } + + // 格式2: 直接是数据对象 + return responseData as T; +} + +// 文档状态枚举 +export enum DocumentStatus { + CUTTING = "Cutting", + EXTRACTIONING = "extractioning", + REVIEWING = "reviewing", + COMPLETED = "completed" +} + +// 文档类型接口 +export interface DocumentType { + id: number; + name: string; +} + +// 提取结果接口 +interface ExtractedResult { + [key: string]: unknown; +} + +// 摘要接口 +interface Summary { + [key: string]: unknown; +} + +// 文档接口 +export interface Document { + id: number; + name: string; + type_id: number; + file_size: number; + status: DocumentStatus; + created_at: string; + document_number?: string; + path?: string; + storage_type?: string; + is_test_document?: boolean; + evaluation_level?: string; + ocr_result?: Record; + extracted_results?: ExtractedResult; + sumary?: Summary; + remark?: string; +} + +/** + * 获取当天的文档列表 + * @returns 文档列表 + */ +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); + + const params: PostgrestParams = { + select: ` + id, + name, + type_id, + file_size, + status, + created_at, + document_number, + path, + storage_type, + is_test_document, + evaluation_level, + ocr_result, + extracted_results, + sumary, + remark + `, + order: 'created_at.desc', + filter: { + 'created_at': `gte.${today}` + } + }; + + // console.log('发送请求参数:', params); + const response = await postgrestGet('documents', params); + // console.log('API 响应:', response); + + if (response.error) { + console.error('API 返回错误:', response.error); + return { error: response.error, status: response.status }; + } + + const extractedData = extractApiData(response.data); + // console.log('提取后的数据:', extractedData); + + if (!extractedData) { + console.error('数据提取失败'); + return { error: '获取数据失败', status: 500 }; + } + + return { data: extractedData }; + } catch (error) { + console.error('获取当天文档列表失败:', error); + return { + error: error instanceof Error ? error.message : '获取当天文档列表失败', + status: 500 + }; + } +} + +/** + * 获取文档类型列表 + * @returns 文档类型列表 + */ +export async function getDocumentTypes(): Promise<{data: DocumentType[]; error?: never} | {data?: never; error: string; status?: number}> { + try { + const params: PostgrestParams = { + select: 'id, name' + }; + + const response = await postgrestGet('document_types', params); + + if (response.error) { + return { error: response.error, status: response.status }; + } + + const extractedData = extractApiData(response.data); + if (!extractedData) { + return { error: '获取数据失败', status: 500 }; + } + + return { data: extractedData }; + } catch (error) { + console.error('获取文档类型列表失败:', error); + return { + error: error instanceof Error ? error.message : '获取文档类型列表失败', + status: 500 + }; + } +} + +/** + * 获取指定文档的状态 + * @param documentIds 文档ID列表 + * @returns 文档状态列表 + */ +export async function getDocumentsStatus(documentIds: number[]): Promise<{data: Document[]; error?: never} | {data?: never; error: string; status?: number}> { + try { + if (!documentIds || documentIds.length === 0) { + return { data: [] }; + } + + const params: PostgrestParams = { + select: 'id, status', + filter: { + 'id': `in.(${documentIds.join(',')})` + } + }; + + const response = await postgrestGet('documents', params); + + if (response.error) { + return { error: response.error, status: response.status }; + } + + const extractedData = extractApiData(response.data); + if (!extractedData) { + return { error: '获取数据失败', status: 500 }; + } + + return { data: extractedData }; + } catch (error) { + console.error('获取文档状态失败:', error); + return { + error: error instanceof Error ? error.message : '获取文档状态失败', + status: 500 + }; + } +} \ No newline at end of file diff --git a/app/api/prompts/prompts.ts b/app/api/prompts/prompts.ts new file mode 100644 index 0000000..18b4b2d --- /dev/null +++ b/app/api/prompts/prompts.ts @@ -0,0 +1,493 @@ +import { postgrestGet, postgrestPut, postgrestPost, postgrestDelete, type PostgrestParams } from '../postgrest-client'; +import dayjs from 'dayjs'; + +// 提示词模板接口 +export interface PromptTemplate { + id: number; + template_name: string; + template_type: string; + description: string | null; + template_content: string; + variables: Record; // 变量定义 + status: number; + version: string; + created_by: number; + created_at: string; + updated_at: string; +} + +// 提示词模板前端接口 +export interface PromptTemplateUI { + id: string; + template_name: string; + template_type: 'Extraction' | 'Evaluation' | 'Summary' | 'Common'; + description: string; + template_content: string; + variables: Record; // 变量定义 + status: 'active' | 'inactive' | 'system'; + version: string; + created_by: number; + created_at: string; + updated_at: string; +} + +// 搜索参数接口 +export interface PromptSearchParams { + name?: string; + type?: string; + status?: string; + page?: number; + pageSize?: number; +} + +/** + * 格式化日期 + * @param dateString 日期字符串 + * @returns 格式化后的日期字符串 + */ +function formatDate(dateString: string): string { + if (!dateString) return ''; + try { + return dayjs(dateString).format('YYYY-MM-DD HH:mm:ss'); + } catch (error) { + console.error('日期格式化失败:', error); + return dateString; + } +} + +/** + * 从不同格式的 API 响应中提取数据 + * @param responseData API 响应数据 + * @returns 提取后的数据或 null + */ +function extractApiData(responseData: unknown): T | null { + if (!responseData) return null; + + // 格式1: { code: number, msg: string, data: T } + if (typeof responseData === 'object' && responseData !== null && + 'code' in responseData && + 'data' in responseData && + (responseData as { data: unknown }).data) { + return (responseData as { data: T }).data; + } + + // 格式2: 直接是数据对象 + return responseData as T; +} + +/** + * 将API状态值转换为UI状态值 + * @param status API状态值(数字) + * @returns UI状态值(字符串) + */ +function mapStatusToUI(status: number): 'active' | 'inactive' | 'system' { + switch(status) { + case 0: return 'inactive'; + case 1: return 'active'; + case 2: return 'system'; + default: return 'inactive'; + } +} + +/** + * 将UI状态值转换为API状态值 + * @param status UI状态值(字符串) + * @returns API状态值(数字) + */ +function mapStatusToAPI(status: string): number { + switch(status) { + case 'active': return 1; + case 'inactive': return 0; + case 'system': return 2; + default: return 0; + } +} + +/** + * 将数据库模板转换为UI模板 + * @param template 数据库模板 + * @returns UI模板 + */ +export function convertToUITemplate(template: PromptTemplate): PromptTemplateUI { + return { + id: template.id ? template.id.toString() : '', + template_name: template.template_name, + template_type: template.template_type as "Extraction" | "Evaluation" | "Summary" | "Common", + description: template.description || '', + template_content: template.template_content, + variables: template.variables, + status: mapStatusToUI(template.status), + version: template.version, + created_by: template.created_by, + created_at: formatDate(template.created_at), + updated_at: formatDate(template.updated_at) + }; +} + +/** + * 获取提示词模板列表 + * @param searchParams 搜索参数 + * @returns 提示词模板列表和总数 + */ +export async function getPromptTemplates(searchParams: PromptSearchParams = {}): Promise<{ + data?: { templates: PromptTemplateUI[], total: number }; + error?: string; + status?: number; +}> { + try { + console.log('获取提示词模板列表,参数:', searchParams); + + const page = searchParams.page || 1; + const pageSize = searchParams.pageSize || 10; + + // 构建查询参数 + const params: PostgrestParams = { + select: ` + id, + template_name, + template_type, + description, + template_content, + variables, + status, + version, + created_by, + created_at, + updated_at + `, + order: 'updated_at.desc', + headers: { + 'Prefer': 'count=exact' + }, + limit: pageSize, + offset: (page - 1) * pageSize, + filter: {} as Record + }; + + // 添加筛选条件 + const filter: Record = {}; + filter['status'] = `gte.0`; + if (searchParams.name) { + filter['template_name'] = `ilike.%${searchParams.name}%`; + } + + if (searchParams.type) { + filter['template_type'] = `eq.${searchParams.type}`; + } + + if (searchParams.status) { + let statusValue: number; + switch (searchParams.status) { + case 'active': statusValue = 1; break; + case 'inactive': statusValue = 0; break; + case 'system': statusValue = 2; break; + default: statusValue = -1; + } + + if (statusValue >= 0) { + filter['status'] = `eq.${statusValue}`; + } + } + params.filter = filter; + + // 发送API请求 + console.log('API请求参数:', params); + const response = await postgrestGet('prompt_templates', params); + + if (response.error) { + console.error('API返回错误:', response.error); + return { error: response.error, status: response.status }; + } + + // 提取API返回的数据 + const extractedData = extractApiData(response.data); + + if (!extractedData) { + console.error('提取数据失败,原始响应:', response.data); + return { error: '获取提示词模板数据失败', status: 500 }; + } + + console.log(`成功获取${extractedData.length}条提示词模板数据`); + + // 从响应头中获取总数 + let totalCount = 0; + const responseWithHeaders = response as { data: PromptTemplate[]; headers: Record }; + if(responseWithHeaders.headers){ + const rangeHeader = responseWithHeaders.headers['content-range']; + if(rangeHeader){ + const total = rangeHeader.split('/')[1]; + if(total !== '*'){ + totalCount = parseInt(total, 10); + } + } + } + // 返回转换后的数据 + return { + data: { + templates: extractedData.map(convertToUITemplate), + total: totalCount + } + }; + } catch (error) { + console.error('获取提示词模板出错:', error); + return { + error: error instanceof Error ? error.message : '获取提示词模板失败', + status: 500 + }; + } +} + +/** + * 获取提示词模板详情 + * @param id 模板ID + * @returns 提示词模板详情 + */ +export async function getPromptTemplate(id: string): Promise<{ + data?: PromptTemplateUI; + error?: string; + status?: number; +}> { + try { + if (!id) { + return { error: '模板ID不能为空', status: 400 }; + } + + const params: PostgrestParams = { + select: ` + id, + template_name, + template_type, + description, + template_content, + variables, + status, + version, + created_by, + created_at, + updated_at + `, + filter: { + 'id': `eq.${id}` + } + }; + + const response = await postgrestGet('prompt_templates', params); + + if (response.error) { + return { error: response.error, status: response.status }; + } + + const extractedData = extractApiData(response.data); + + if (!extractedData || extractedData.length === 0) { + return { error: '未找到指定模板', status: 404 }; + } + + return { data: convertToUITemplate(extractedData[0]) }; + } catch (error) { + console.error('获取提示词模板详情失败:', error); + return { + error: error instanceof Error ? error.message : '获取提示词模板详情失败', + status: 500 + }; + } +} + +/** + * 创建提示词模板 + * @param template 提示词模板数据 + * @returns 创建的提示词模板 + */ +export async function createPromptTemplate(template: Partial): Promise<{ + data?: PromptTemplateUI; + error?: string; + status?: number; +}> { + try { + // 验证必填字段 + if (!template.template_name || !template.template_type || !template.template_content) { + return { error: '模板名称、类型和内容不能为空', status: 400 }; + } + + // 准备变量数据 + let variablesData: Record = {}; + if (typeof template.variables === 'string') { + try { + variablesData = JSON.parse(template.variables); + } catch (e) { + console.error('解析变量JSON失败:', e); + } + } else if (template.variables) { + variablesData = template.variables; + } + + // 准备API数据 + const apiTemplate: Partial = { + template_name: template.template_name, + template_type: template.template_type, + description: template.description || null, + template_content: template.template_content, + variables: variablesData, + status: mapStatusToAPI(template.status || 'active'), + version: template.version || 'v1.0', + created_by: 1 // 固定创建者为1 + }; + + if(apiTemplate){ + console.log('apiTemplate', apiTemplate); + // throw new Error('测试错误'); + } + + const response = await postgrestPost>( + 'prompt_templates', + apiTemplate + ); + + if (response.error) { + return { error: response.error, status: response.status }; + } + + const extractedData = extractApiData(response.data); + + if (!extractedData) { + return { error: '创建提示词模板失败', status: 500 }; + } + + return { data: convertToUITemplate(extractedData) }; + } catch (error) { + console.error('创建提示词模板失败:', error); + return { + error: error instanceof Error ? error.message : '创建提示词模板失败', + status: 500 + }; + } +} + +/** + * 更新提示词模板 + * @param id 模板ID + * @param template 提示词模板数据 + * @returns 更新后的提示词模板 + */ +export async function updatePromptTemplate(id: string, template: Partial): Promise<{ + data?: PromptTemplateUI; + error?: string; + status?: number; +}> { + try { + if (!id) { + return { error: '模板ID不能为空', status: 400 }; + } + + // 准备变量数据 + let variablesData: Record = {}; + if (typeof template.variables === 'string') { + try { + variablesData = JSON.parse(template.variables); + } catch (e) { + console.error('解析变量JSON失败:', e); + } + } else if (template.variables) { + variablesData = template.variables; + } + + // 准备API数据 + const apiTemplate: Partial = {}; + + if (template.template_name !== undefined) { + apiTemplate.template_name = template.template_name; + } + + if (template.template_type !== undefined) { + apiTemplate.template_type = template.template_type; + } + + if (template.description !== undefined) { + apiTemplate.description = template.description; + } + + if (template.template_content !== undefined) { + apiTemplate.template_content = template.template_content; + } + + if (template.variables !== undefined) { + apiTemplate.variables = variablesData; + } + + if (template.status !== undefined) { + apiTemplate.status = mapStatusToAPI(template.status); + } + + if (template.version !== undefined) { + apiTemplate.version = template.version; + } + + // if(apiTemplate){ + // console.log('apiTemplate', apiTemplate); + // throw new Error('测试错误'); + // } + + const response = await postgrestPut>( + 'prompt_templates', + apiTemplate, + { id } + ); + + if (response.error) { + return { error: response.error, status: response.status }; + } + + const extractedData = extractApiData(response.data); + + if (!extractedData) { + return { error: '更新提示词模板失败', status: 500 }; + } + + return { data: convertToUITemplate(extractedData) }; + } catch (error) { + console.error('更新提示词模板失败:', error); + return { + error: error instanceof Error ? error.message : '更新提示词模板失败', + status: 500 + }; + } +} + +/** + * 删除提示词模板 + * @param id 模板ID + * @returns 成功或失败信息 + */ +export async function deletePromptTemplate(id: string): Promise<{ + success?: boolean; + error?: string; + status?: number; +}> { + try { + if (!id) { + return { error: '模板ID不能为空', status: 400 }; + } + + // 使用真实删除替代状态更新 + const response = await postgrestDelete( + 'prompt_templates', + { + filter: { + 'id': `eq.${id}` + } + } + ); + + if (response.error) { + return { error: response.error, status: response.status }; + } + + return { success: true }; + } catch (error) { + console.error('删除提示词模板失败:', error); + return { + error: error instanceof Error ? error.message : '删除提示词模板失败', + status: 500 + }; + } +} \ No newline at end of file diff --git a/app/api/system_setting/config-lists.ts b/app/api/system_setting/config-lists.ts index e69de29..cf90cb2 100644 --- a/app/api/system_setting/config-lists.ts +++ b/app/api/system_setting/config-lists.ts @@ -0,0 +1,322 @@ +import { postgrestGet, postgrestPut, postgrestPost, type PostgrestParams } from '../postgrest-client'; +import dayjs from 'dayjs'; +// 配置项接口 +export interface ConfigItem { + id: number; + name: string; + type: string; + description: string; + environment: string; + config: Record; + remark: string; + is_active: boolean; + version: string; + created_by: number; + created_at: string; + updated_at: string; +} +/** + * 格式化日期 + * @param dateString 日期字符串 + * @returns 格式化后的日期字符串 + */ +function formatDate(dateString: string): string { + if (!dateString) return ''; + try { + return dayjs(dateString).format('YYYY-MM-DD HH:mm:ss'); + } catch (error) { + console.error('日期格式化失败:', error); + return dateString; + } +} + + +/** + * 从不同格式的 API 响应中提取数据 + * @param responseData API 响应数据 + * @returns 提取后的数据或 null + */ +function extractApiData(responseData: unknown): T | null { + if (!responseData) return null; + + // 格式1: { code: number, msg: string, data: T } + if (typeof responseData === 'object' && responseData !== null && + 'code' in responseData && + 'data' in responseData && + (responseData as { data: unknown }).data) { + return (responseData as { data: T }).data; + } + + // 格式2: 直接是数据对象 + return responseData as T; +} + + + + + +// 获取配置列表 +export async function getConfigLists(params: { + name?: string; + type?: string; + environment?: string; + is_active?: boolean; + page?: number; + pageSize?: number; +}): Promise<{data: ConfigItem[]; total: number; error?: never} | {data?: never; error: string}> { + try { + const { + name, + type, + environment, + is_active, + page = 1, + pageSize = 10 + } = params; + + // 构建查询参数 + const queryParams: PostgrestParams = { + select: '*', + order: 'created_at.desc', + limit: pageSize, + offset: (page-1)*pageSize, + filter: {} as Record, + headers: { + 'Prefer': 'count=exact' + } + }; + + // 添加筛选条件 + const filter: Record = {}; + if (name) { + filter['name'] = `ilike.%${name}%`; + } + if (type) { + filter['type'] = `eq.${type}`; + } + if (environment) { + filter['environment'] = `eq.${environment}`; + } + if (is_active !== undefined) { + filter['is_active'] = `eq.${is_active}`; + } + queryParams.filter = filter; + + // 获取数据 + const response = await postgrestGet('configurations', queryParams); + + if (response.error) { + return { error: response.error }; + } + + const data = extractApiData(response.data); + if (!data) { + return { error: '获取数据失败' }; + } + + // 格式化日期 + const formattedData = data.map(item => ({ + ...item, + created_at: formatDate(item.created_at), + updated_at: formatDate(item.updated_at) + })); + + // 从响应头中获取总数 + let totalCount = 0; + const responseWithHeaders = response as { data: ConfigItem[]; headers: Record }; + if (responseWithHeaders.headers) { + const rangeHeader = responseWithHeaders.headers['content-range']; + if (rangeHeader) { + const total = rangeHeader.split('/')[1]; + if (total !== '*') { + totalCount = parseInt(total, 10); + } + } + } + + return { + data: formattedData, + total: totalCount + }; + } catch (error) { + console.error('获取配置列表失败:', error); + return { error: error instanceof Error ? error.message : '获取配置列表失败' }; + } +} + +// 获取配置类型和环境选项 +export async function getConfigOptions(): Promise<{data: {types: string[]; environments: string[]}; error?: never} | {data?: never; error: string}> { + try { + // 获取类型选项 + const typeResponse = await postgrestGet<{type: string}[]>('configurations', { + select: 'type' + }); + + if (typeResponse.error) { + return { error: typeResponse.error }; + } + + const typesData = extractApiData<{type: string}[]>(typeResponse.data); + if (!typesData) { + return { error: '获取类型选项失败' }; + } + + // 获取环境选项 + const envResponse = await postgrestGet<{environment: string}[]>('configurations', { + select: 'environment' + }); + + if (envResponse.error) { + return { error: envResponse.error }; + } + + const envData = extractApiData<{environment: string}[]>(envResponse.data); + if (!envData) { + return { error: '获取环境选项失败' }; + } + + // 手动去重 + const types = [...new Set(typesData.map(item => item.type))]; + const environments = [...new Set(envData.map(item => item.environment))]; + + return { + data: { + types, + environments + } + }; + } catch (error) { + console.error('获取配置选项失败:', error); + return { error: error instanceof Error ? error.message : '获取配置选项失败' }; + } +} + +// 获取配置详情 +export async function getConfigDetail(id: string): Promise<{data: ConfigItem; error?: never} | {data?: never; error: string}> { + try { + const response = await postgrestGet('configurations', { + filter: { + 'id': `eq.${id}` + } + }); + + if (response.error) { + return { error: response.error }; + } + + const data = extractApiData(response.data); + if (!data || data.length === 0) { + return { error: '未找到配置' }; + } + + const config = data[0]; + return { + data: { + ...config, + created_at: formatDate(config.created_at), + updated_at: formatDate(config.updated_at) + } + }; + } catch (error) { + console.error('获取配置详情失败:', error); + return { error: error instanceof Error ? error.message : '获取配置详情失败' }; + } +} + +// 创建配置 +export async function createConfig(data: { + name: string; + type: string; + environment: string; + config: Record; + is_active: boolean; + remark?: string; +}): Promise<{data: ConfigItem; error?: never} | {data?: never; error: string}> { + try { + const response = await postgrestPost('configurations', data); + + if (response.error) { + return { error: response.error }; + } + + const createdData = extractApiData(response.data); + if (!createdData) { + return { error: '创建配置失败' }; + } + + return { + data: { + ...createdData, + created_at: formatDate(createdData.created_at), + updated_at: formatDate(createdData.updated_at) + } + }; + } catch (error) { + console.error('创建配置失败:', error); + return { error: error instanceof Error ? error.message : '创建配置失败' }; + } +} + +// 更新配置 +export async function updateConfig(id: string, data: { + name: string; + type: string; + environment: string; + config: Record; + is_active: boolean; + remark?: string; +}): Promise<{data: ConfigItem; error?: never} | {data?: never; error: string}> { + try { + const response = await postgrestPut('configurations', data, { + id: id.toString() + }); + + if (response.error) { + return { error: response.error }; + } + + const updatedData = extractApiData(response.data); + if (!updatedData) { + return { error: '更新配置失败' }; + } + + return { + data: { + ...updatedData, + created_at: formatDate(updatedData.created_at), + updated_at: formatDate(updatedData.updated_at) + } + }; + } catch (error) { + console.error('更新配置失败:', error); + return { error: error instanceof Error ? error.message : '更新配置失败' }; + } +} + +// 更新配置状态 +export async function updateConfigStatus(id: number, is_active: boolean): Promise<{success: boolean; error?: string}> { + try { + const response = await postgrestPut( + 'configurations', + { is_active }, + { id: id.toString() } + ); + + if (response.error) { + return { success: false, error: response.error }; + } + + const updatedData = extractApiData(response.data); + if (!updatedData) { + return { success: false, error: '更新配置状态失败' }; + } + + return { success: true }; + } catch (error) { + console.error('更新配置状态失败:', error); + return { + success: false, + error: error instanceof Error ? error.message : '更新配置状态失败' + }; + } +} diff --git a/app/components/layout/Sidebar.tsx b/app/components/layout/Sidebar.tsx index a819030..663fb02 100644 --- a/app/components/layout/Sidebar.tsx +++ b/app/components/layout/Sidebar.tsx @@ -37,12 +37,12 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) { path: '/files/upload', icon: 'ri-upload-cloud-line' }, - { - id: 'file-list', - title: '文件列表', - path: '/files', - icon: 'ri-file-list-3-line' - }, + // { + // id: 'file-list', + // title: '文件列表', + // path: '/files', + // icon: 'ri-file-list-3-line' + // }, { id:'documents', title:'文档列表', @@ -80,15 +80,7 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) { title: '新增评查点', path: '/rules/new', icon: 'ri-add-circle-line' - } - ] - }, - { - id: 'review-management', - title: '评查结果', - path: '/reviews', - icon: 'ri-bar-chart-box-line', - children: [ + }, { id: 'review-detail', title: '评查详情', @@ -109,12 +101,12 @@ export function Sidebar({ onToggle, collapsed }: SidebarProps) { path: '/config-lists', icon: 'ri-list-check-3' }, - { - id: 'basic-settings', - title: '基础设置', - path: '/settings', - icon: 'ri-equalizer-line' - }, + // { + // id: 'basic-settings', + // title: '基础设置', + // path: '/settings', + // icon: 'ri-equalizer-line' + // }, { id: 'document-types', title: '文档类型', diff --git a/app/components/ui/UploadArea.tsx b/app/components/ui/UploadArea.tsx index 84c01a9..2998412 100644 --- a/app/components/ui/UploadArea.tsx +++ b/app/components/ui/UploadArea.tsx @@ -50,7 +50,15 @@ export const UploadArea = forwardRef(({ })); const handleClick = useCallback(() => { - if (!disabled && !shouldPreventFileSelect && fileInputRef.current) { + if (disabled) return; + + if (shouldPreventFileSelect) { + // 如果应该阻止文件选择,则触发表单提交 + const form = fileInputRef.current?.closest('form'); + if (form) { + form.requestSubmit(); + } + } else if (fileInputRef.current) { fileInputRef.current.click(); } }, [disabled, shouldPreventFileSelect]); @@ -76,7 +84,15 @@ export const UploadArea = forwardRef(({ e.preventDefault(); setIsDragOver(false); - if (!disabled && !shouldPreventFileSelect && e.dataTransfer.files.length > 0) { + if (disabled) return; + + if (shouldPreventFileSelect) { + // 如果应该阻止文件选择,则触发表单提交 + const form = e.currentTarget.closest('form'); + if (form) { + form.requestSubmit(); + } + } else if (e.dataTransfer.files.length > 0) { onFilesSelected(e.dataTransfer.files); } }, [disabled, shouldPreventFileSelect, onFilesSelected]); diff --git a/app/routes/config-lists._index.tsx b/app/routes/config-lists._index.tsx index 95d7b9a..764abc5 100644 --- a/app/routes/config-lists._index.tsx +++ b/app/routes/config-lists._index.tsx @@ -7,13 +7,13 @@ import { FilterPanel, FilterSelect, SearchFilter } from "~/components/ui/FilterP import { Pagination } from "~/components/ui/Pagination"; import { Table } from "~/components/ui/Table"; import { Tag } from "~/components/ui/Tag"; +import { getConfigLists, getConfigOptions, updateConfigStatus, type ConfigItem } from "~/api/system_setting/config-lists"; import configListsStyles from "~/styles/pages/config-lists_index.css?url"; export const links = () => [ { rel: "stylesheet", href: configListsStyles } ]; - export const meta: MetaFunction = () => { return [ { title: "系统配置管理 - 中国烟草AI合同及卷宗审核系统" }, @@ -54,165 +54,55 @@ export const MODULE_LABELS: Record = { [ConfigModule.NOTIFICATION]: '通知' }; -// 配置数据类型 -interface ConfigDataType { - [key: string]: string | number | boolean | string[] | ConfigDataType | ConfigDataType[]; -} - -// 配置项模型 -interface ConfigItem { - id: string; - configName: string; - module: ConfigModule; - environment: ConfigEnvironment; - isActive: boolean; - configData: ConfigDataType; - createdAt: string; - updatedAt: string; -} - interface LoaderData { configs: ConfigItem[]; totalCount: number; currentPage: number; pageSize: number; totalPages: number; + types: string[]; + environments: string[]; } export async function loader({ request }: LoaderFunctionArgs) { const url = new URL(request.url); - const configName = url.searchParams.get("configName") || ""; - const module = url.searchParams.get("module") || ""; + const name = url.searchParams.get("name") || ""; + const type = url.searchParams.get("type") || ""; const environment = url.searchParams.get("environment") || ""; - const isActive = url.searchParams.get("isActive") || ""; + const is_active = url.searchParams.get("is_active") ? url.searchParams.get("is_active") === "true" : undefined; const currentPage = parseInt(url.searchParams.get("page") || "1", 10); const pageSize = parseInt(url.searchParams.get("pageSize") || "10", 10); try { - // 模拟数据,实际项目中应从API获取 - const mockConfigs: ConfigItem[] = [ - { - id: "1", - configName: "database_connection", - module: ConfigModule.SYSTEM, - environment: ConfigEnvironment.PROD, - isActive: true, - configData: { - database: { - host: "db.cluster.com", - port: 5432, - pool_size: 20, - ssl: true - }, - cache: { - ttl: 3600, - max_entries: 1000 - }, - feature_flags: ["new_ui", "analytics_v2"] - }, - createdAt: "2023-07-10 10:15:23", - updatedAt: "2023-07-15 14:30:26" - }, - { - id: "2", - configName: "text_extraction_ai", - module: ConfigModule.AI, - environment: ConfigEnvironment.TEST, - isActive: true, - configData: { - model: "gpt-4", - parameters: { - temperature: 0.7, - max_tokens: 2000 - }, - api_key: "sk-**********", - timeout: 30 - }, - createdAt: "2023-07-12 08:45:12", - updatedAt: "2023-07-14 09:15:33" - }, - { - id: "3", - configName: "notification_service", - module: ConfigModule.NOTIFICATION, - environment: ConfigEnvironment.DEV, - isActive: false, - configData: { - email: { - smtp_server: "smtp.example.com", - port: 587, - use_tls: true, - sender: "noreply@example.com" - }, - sms: { - provider: "aliyun", - region: "cn-hangzhou", - sign_name: "AI审核系统" - } - }, - createdAt: "2023-07-05 13:20:45", - updatedAt: "2023-07-10 16:45:19" - }, - { - id: "4", - configName: "file_storage", - module: ConfigModule.FILE, - environment: ConfigEnvironment.PROD, - isActive: true, - configData: { - type: "oss", - region: "cn-shanghai", - bucket: "contracts-ai-review", - access_control: "private", - lifecycle_rules: [ - { - prefix: "temp/", - ttl_days: 7 - } - ] - }, - createdAt: "2023-06-28 09:30:18", - updatedAt: "2023-07-08 11:22:07" - } - ]; - - // 过滤数据 - let filteredConfigs = [...mockConfigs]; - - if (configName) { - filteredConfigs = filteredConfigs.filter(config => - config.configName.toLowerCase().includes(configName.toLowerCase()) - ); + // 获取配置列表 + const configsResponse = await getConfigLists({ + name, + type, + environment, + is_active, + page: currentPage, + pageSize + }); + + if (configsResponse.error || !configsResponse.data) { + throw new Error(configsResponse.error || "获取配置列表失败"); } - - if (module) { - filteredConfigs = filteredConfigs.filter(config => config.module === module); + + // 获取配置选项 + const optionsResponse = await getConfigOptions(); + + if (optionsResponse.error || !optionsResponse.data) { + throw new Error(optionsResponse.error || "获取配置选项失败"); } - - if (environment) { - filteredConfigs = filteredConfigs.filter(config => config.environment === environment); - } - - if (isActive) { - const activeValue = isActive === 'true'; - filteredConfigs = filteredConfigs.filter(config => config.isActive === activeValue); - } - - // 计算分页信息 - const totalCount = filteredConfigs.length; - const totalPages = Math.ceil(totalCount / pageSize); - - // 分页截取 - const startIndex = (currentPage - 1) * pageSize; - const endIndex = startIndex + pageSize; - const paginatedConfigs = filteredConfigs.slice(startIndex, endIndex); - + return json({ - configs: paginatedConfigs, - totalCount, + configs: configsResponse.data, + totalCount: configsResponse.total, currentPage, pageSize, - totalPages + totalPages: Math.ceil(configsResponse.total / pageSize), + types: optionsResponse.data.types, + environments: optionsResponse.data.environments }, { headers: { "Cache-Control": "max-age=60, s-maxage=180" @@ -233,28 +123,18 @@ export async function action({ request }: ActionFunctionArgs) { return json({ success: false, error: "缺少配置ID" }, { status: 400 }); } + // 进行更新启用和禁用的状态 try { if (_action === 'toggleStatus') { - const isActive = formData.get('isActive') === 'true'; - const newStatus = !isActive; + const is_active = formData.get('is_active') === 'true'; - // 实际项目中应调用API更新状态 - console.log(`切换配置 ${configId} 状态为: ${newStatus}`); + const response = await updateConfigStatus(parseInt(configId as string), is_active); - // 模拟API调用 - // const response = await fetch(`/api/configs/${configId}/status`, { - // method: 'PATCH', - // headers: { - // 'Content-Type': 'application/json', - // }, - // body: JSON.stringify({ isActive: newStatus }), - // }); + if (!response.success) { + return json({ success: false, error: response.error }, { status: 500 }); + } - // if (!response.ok) { - // throw new Error(`状态切换失败: ${response.status}`); - // } - - return json({ success: true, newStatus }); + return json({ success: true }); } return json({ success: false, error: "未知操作" }, { status: 400 }); @@ -275,7 +155,7 @@ export function ErrorBoundary() { } export default function ConfigListsIndex() { - const { configs, totalCount, currentPage, pageSize } = useLoaderData(); + const { configs, totalCount, currentPage, pageSize, types, environments } = useLoaderData(); const [searchParams, setSearchParams] = useSearchParams(); const submit = useSubmit(); const [showDetailModal, setShowDetailModal] = useState(false); @@ -300,9 +180,9 @@ export default function ConfigListsIndex() { const handleConfigNameSearch = (value: string) => { const newParams = new URLSearchParams(searchParams); if (value) { - newParams.set('configName', value); + newParams.set('name', value); } else { - newParams.delete('configName'); + newParams.delete('name'); } // 搜索时,重置到第一页 @@ -312,11 +192,11 @@ export default function ConfigListsIndex() { }; const handleToggleStatus = (config: ConfigItem) => { - if (window.confirm(`确定要${config.isActive ? '禁用' : '启用'}该配置吗?`)) { + if (window.confirm(`确定要${config.is_active ? '禁用' : '启用'}该配置吗?`)) { const formData = new FormData(); formData.append('_action', 'toggleStatus'); - formData.append('configId', config.id); - formData.append('isActive', String(config.isActive)); + formData.append('configId', config.id.toString()); + formData.append('is_active', String(!config.is_active)); submit(formData, { method: 'post' }); } @@ -342,10 +222,20 @@ export default function ConfigListsIndex() { // 处理重置筛选 const handleReset = () => { + const nameInput = document.querySelector('input[placeholder="请输入配置名称"]') as HTMLInputElement; + const typeSelect = document.querySelector('select[name="type"]') as HTMLInputElement; + const environmentSelect = document.querySelector('select[name="environment"]') as HTMLInputElement; + const statusSelect = document.querySelector('select[name="is_active"]') as HTMLInputElement; + setSearchParams(new URLSearchParams()); + + if(nameInput) nameInput.value = '' + if(typeSelect) typeSelect.value = '' + if(environmentSelect) environmentSelect.value = '' + if(statusSelect) statusSelect.value = '' + }; - // 关闭详情模态框 const closeDetailModal = () => { setShowDetailModal(false); @@ -356,43 +246,42 @@ export default function ConfigListsIndex() { const columns = [ { title: "配置名称", - dataIndex: "configName" as keyof ConfigItem, - key: "configName", + dataIndex: "name" as keyof ConfigItem, + key: "name", width: "20%" }, { title: "所属模块", - key: "module", + key: "type", width: "10%", - render: (_: unknown, record: ConfigItem) => MODULE_LABELS[record.module] + render: (_: unknown, record: ConfigItem) => record.type }, { title: "环境", key: "environment", width: "15%", render: (_: unknown, record: ConfigItem) => { - const envClass = `env-tag env-tag-${record.environment}`; return ( - - {ENVIRONMENT_LABELS[record.environment]} + + {record.environment} ); } }, { title: "状态", - key: "isActive", + key: "is_active", width: "15%", render: (_: unknown, record: ConfigItem) => ( - - {record.isActive ? '已启用' : '已禁用'} + + {record.is_active ? '已启用' : '已禁用'} ) }, { title: "最后更新时间", - dataIndex: "updatedAt" as keyof ConfigItem, - key: "updatedAt", + dataIndex: "updated_at" as keyof ConfigItem, + key: "updated_at", width: "15%" }, { @@ -417,29 +306,17 @@ export default function ConfigListsIndex() { ) } ]; - // 生成环境选项 - const environmentOptions = Object.entries(ENVIRONMENT_LABELS).map(([value, label]) => ({ - value, - label - })); - - // 生成模块选项 - const moduleOptions = Object.entries(MODULE_LABELS).map(([value, label]) => ({ - value, - label - })); - return (
{/* 页面头部 */} @@ -458,9 +335,9 @@ export default function ConfigListsIndex() { - + */} } noActionDivider={true} @@ -468,7 +345,7 @@ export default function ConfigListsIndex() { ({ value: type, label: type }))]} onChange={handleFilterChange} className="flex-1 min-w-[200px]" /> @@ -487,17 +364,16 @@ export default function ConfigListsIndex() { label="环境" name="environment" value={searchParams.get('environment') || ''} - options={[{ value: '', label: '全部' }, ...environmentOptions]} + options={[ ...environments.map(env => ({ value: env, label: env }))]} onChange={handleFilterChange} className="flex-1 min-w-[200px]" />
配置名称
-
{selectedConfig.configName}
+
{selectedConfig.name}
所属模块
-
{MODULE_LABELS[selectedConfig.module]}
+
{selectedConfig.type}
环境
- - {ENVIRONMENT_LABELS[selectedConfig.environment]} + + {selectedConfig.environment}
@@ -565,8 +441,8 @@ export default function ConfigListsIndex() {
状态
- - {selectedConfig.isActive ? '已启用' : '已禁用'} + + {selectedConfig.is_active ? '已启用' : '已禁用'}
@@ -574,19 +450,19 @@ export default function ConfigListsIndex() {
配置数据
-                  {JSON.stringify(selectedConfig.configData, null, 2)}
+                  {JSON.stringify(selectedConfig.config, null, 2)}
                 
创建时间
-
{selectedConfig.createdAt}
+
{selectedConfig.created_at}
更新时间
-
{selectedConfig.updatedAt}
+
{selectedConfig.updated_at}
diff --git a/app/routes/config-lists.new.tsx b/app/routes/config-lists.new.tsx index 6e4301d..7689981 100644 --- a/app/routes/config-lists.new.tsx +++ b/app/routes/config-lists.new.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import { Button } from "~/components/ui/Button"; import { Card } from "~/components/ui/Card"; import { ConfigModule, MODULE_LABELS, ENVIRONMENT_LABELS } from "./config-lists._index"; +import { getConfigOptions, getConfigDetail, createConfig, updateConfig } from "~/api/system_setting/config-lists"; import configNewStyles from "~/styles/pages/config-lists_new.css?url"; export const links = () => [ @@ -39,18 +40,20 @@ export const EXTENDED_ENVIRONMENT_LABELS: Record = { }; interface ConfigData { - id: string; - configName: string; - module: ConfigModule; - environment: string; // 使用扩展的环境类型 - isActive: boolean; - configData: string; // JSON字符串 - remarks?: string; // 添加备注字段 + id: number; + name: string; + type: string; + environment: string; + is_active: boolean; + config: Record; + remark?: string; } interface LoaderData { config?: ConfigData; isEdit: boolean; + types: string[]; + environments: string[]; } export async function loader({ request }: LoaderFunctionArgs) { @@ -58,193 +61,107 @@ export async function loader({ request }: LoaderFunctionArgs) { const id = url.searchParams.get("id"); let config: ConfigData | undefined = undefined; + // 获取配置选项 + const optionsResponse = await getConfigOptions(); + if (optionsResponse.error) { + throw new Error(optionsResponse.error); + } + if (id) { - try { - // 实际应用中,应从API获取配置详情 - // const response = await fetch(`${process.env.API_BASE_URL}/api/configs/${id}`); - // if (!response.ok) throw new Error(`获取配置详情失败: ${response.status}`); - // config = await response.json(); - // config.configData = JSON.stringify(config.configData, null, 2); - - // 使用模拟数据 - if (id === "1") { - config = { - id: "1", - configName: "database_connection", - module: ConfigModule.SYSTEM, - environment: ExtendedConfigEnvironment.PROD, - isActive: true, - remarks: "数据库连接配置,包含主库和从库配置", - configData: JSON.stringify({ - database: { - host: "db.cluster.com", - port: 5432, - pool_size: 20, - ssl: true - }, - cache: { - ttl: 3600, - max_entries: 1000 - }, - feature_flags: ["new_ui", "analytics_v2"] - }, null, 2) - }; - } else if (id === "2") { - config = { - id: "2", - configName: "text_extraction_ai", - module: ConfigModule.AI, - environment: ExtendedConfigEnvironment.TEST, - isActive: true, - remarks: "AI文本抽取服务配置", - configData: JSON.stringify({ - model: "gpt-4", - parameters: { - temperature: 0.7, - max_tokens: 2000 - }, - api_key: "sk-**********", - timeout: 30 - }, null, 2) - }; - } else if (id === "3") { - config = { - id: "3", - configName: "notification_service", - module: ConfigModule.NOTIFICATION, - environment: ExtendedConfigEnvironment.DEV, - isActive: false, - remarks: "通知服务配置,目前处于开发测试阶段", - configData: JSON.stringify({ - email: { - smtp_server: "smtp.example.com", - port: 587, - use_tls: true, - sender: "noreply@example.com" - }, - sms: { - provider: "aliyun", - region: "cn-hangzhou", - sign_name: "AI审核系统" - } - }, null, 2) - }; - } else if (id === "4") { - config = { - id: "4", - configName: "file_storage", - module: ConfigModule.FILE, - environment: ExtendedConfigEnvironment.COMMON, - isActive: true, - remarks: "文件存储通用配置,适用于所有环境", - configData: JSON.stringify({ - type: "oss", - region: "cn-shanghai", - bucket: "contracts-ai-review", - access_control: "private", - lifecycle_rules: [ - { - prefix: "temp/", - ttl_days: 7 - } - ] - }, null, 2) - }; - } - } catch (error) { - console.error("获取配置详情失败:", error); - // 在实际应用中,应该将错误信息返回给客户端 - // 这里简单处理,返回空config + // 获取配置详情 + const detailResponse = await getConfigDetail(id); + if (detailResponse.error) { + throw new Error(detailResponse.error); } + config = detailResponse.data; } return Response.json({ config, - isEdit: !!config + isEdit: !!config, + types: optionsResponse.data?.types || [], + environments: optionsResponse.data?.environments || [] }); } - interface ActionData { success?: boolean; errors?: { - configName?: string; - module?: string; + name?: string; + type?: string; environment?: string; - configData?: string; + config?: string; general?: string; }; } export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); - const configId = formData.get("id") as string; - const configName = formData.get("configName") as string; - const module = formData.get("module") as string; + const id = formData.get("id") as string; + const name = formData.get("name") as string; + const type = formData.get("type") as string; const environment = formData.get("environment") as string; - const configData = formData.get("configData") as string; - const isActive = formData.get("isActive") === "true"; - const remarks = formData.get("remarks") as string; + const config = formData.get("config") as string; + const is_active = formData.get("is_active") === "true"; + const remark = formData.get("remark") as string; const errors: ActionData["errors"] = {}; // 表单验证 - if (!configName || configName.trim() === "") { - errors.configName = "配置名称不能为空"; + if (!name || name.trim() === "") { + errors.name = "配置名称不能为空"; } - if (!module) { - errors.module = "请选择所属模块"; + if (!type) { + errors.type = "请选择所属模块"; } if (!environment) { errors.environment = "请选择环境"; } - if (!configData || configData.trim() === "") { - errors.configData = "配置数据不能为空"; + if (!config || config.trim() === "") { + errors.config = "配置数据不能为空"; } else { try { - JSON.parse(configData); + JSON.parse(config); } catch (e) { - errors.configData = "配置数据必须是有效的JSON格式"; + errors.config = "配置数据必须是有效的JSON格式"; } } if (Object.keys(errors).length > 0) { - return json({ errors }); + return Response.json({ errors }); } try { - // 实际应用中,应调用API保存数据 - console.log("保存配置:", { configId, configName, module, environment, configData, isActive, remarks }); + const configData = { + name, + type, + environment, + config: JSON.parse(config), + is_active, + remark + }; - // 模拟API调用 - // const response = await fetch(`${process.env.API_BASE_URL}/api/configs${configId ? `/${configId}` : ''}`, { - // method: configId ? "PUT" : "POST", - // headers: { - // "Content-Type": "application/json", - // }, - // body: JSON.stringify({ - // id: configId, - // configName, - // module, - // environment, - // configData: JSON.parse(configData), - // isActive, - // remarks, - // }), - // }); - // - // if (!response.ok) { - // throw new Error(`保存失败: ${response.status}`); - // } + if (id) { + // 更新配置 + const response = await updateConfig(id, configData); + if (response.error) { + throw new Error(response.error); + } + } else { + // 创建配置 + const response = await createConfig(configData); + if (response.error) { + throw new Error(response.error); + } + } - // 保存成功后重定向到列表页 return redirect("/config-lists"); } catch (error) { console.error("保存配置失败:", error); - return json({ + return Response.json({ success: false, errors: { general: "保存配置失败,请稍后重试" @@ -253,18 +170,17 @@ export async function action({ request }: ActionFunctionArgs) { } } -// JSON模板数据 -const JSON_TEMPLATES = { +// 配置模板常量 +const CONFIG_TEMPLATES = { database: { - database: { - host: "localhost", - port: 5432, - username: "db_user", - password: "******", - name: "app_database", - pool_size: 10, - timeout: 5000, - ssl: false + host: "localhost", + port: 5432, + database: "mydb", + username: "admin", + password: "******", + pool: { + min: 2, + max: 10 } }, file: { @@ -290,8 +206,7 @@ const JSON_TEMPLATES = { }; export default function ConfigNew() { - const { config, isEdit } = useLoaderData(); - + const { config, isEdit, types, environments } = useLoaderData(); const actionData = useActionData(); const navigation = useNavigation(); const isSubmitting = navigation.state === "submitting"; @@ -304,18 +219,14 @@ export default function ConfigNew() { const [selectedModule, setSelectedModule] = useState(""); const [selectedEnvironment, setSelectedEnvironment] = useState(""); + // 在 ConfigNew 组件中添加状态来跟踪当前选中的模板 + const [selectedTemplate, setSelectedTemplate] = useState(null); + useEffect(() => { // 初始化配置数据 - if (config?.configData) { - setConfigDataValue(config.configData); - } - - // 初始化模块和环境的选中状态 - if (config?.module) { - setSelectedModule(config.module); - } - - if (config?.environment) { + if (config) { + setConfigDataValue(JSON.stringify(config.config, null, 2)); + setSelectedModule(config.type); setSelectedEnvironment(config.environment); } @@ -333,7 +244,6 @@ export default function ConfigNew() { }, feature_flags: ["new_ui", "analytics_v2"] }, null, 2)); - }, [config]); // 处理JSON数据变更 @@ -360,6 +270,7 @@ export default function ConfigNew() { // 格式化JSON const handleFormatJson = () => { + if (configDataValue.trim() === "") return; try { @@ -375,60 +286,6 @@ export default function ConfigNew() { } }; - // 加载JSON模板 - const handleLoadTemplate = (type: keyof typeof JSON_TEMPLATES) => { - const template = JSON_TEMPLATES[type]; - setConfigDataValue(JSON.stringify(template, null, 2)); - setJsonError(null); - }; - - // 模块标签点击 - const handleModuleTagClick = (module: string) => { - setSelectedModule(module); - }; - - // 环境标签点击 - const handleEnvironmentTagClick = (env: string) => { - setSelectedEnvironment(env); - }; - - // 显示JSON语法高亮 - const renderJsonWithSyntaxHighlight = (json: string) => { - try { - // 如果是空字符串,直接返回 - if (!json.trim()) return ""; - - // 解析并格式化JSON - const parsed = JSON.parse(json); - const formatted = JSON.stringify(parsed, null, 2); - - // 添加语法高亮 - return formatted - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)/g, (match) => { - let cls = 'number'; - if (/^"/.test(match)) { - if (/:$/.test(match)) { - cls = 'key'; - match = match.replace(':', ''); - } else { - cls = 'string'; - } - } else if (/true|false/.test(match)) { - cls = 'boolean'; - } else if (/null/.test(match)) { - cls = 'null'; - } - return `${match}`; - }); - } catch (e) { - // 如果解析失败,返回原始JSON - return json; - } - }; - return (
@@ -444,6 +301,12 @@ export default function ConfigNew() {
+ + {actionData?.errors?.general && ( +
+
{actionData.errors.general}
+
+ )}
@@ -452,18 +315,18 @@ export default function ConfigNew() { {/* 配置名称和状态 */}
- + - {actionData?.errors?.configName && ( -
{actionData.errors.configName}
+ {actionData?.errors?.name && ( +
{actionData.errors.name}
)}
唯一标识符,配置名称应使用英文,推荐使用下划线命名方式 @@ -471,18 +334,18 @@ export default function ConfigNew() {
- +
-
@@ -495,33 +358,33 @@ export default function ConfigNew() { {/* 所属模块 */}
- + setSelectedModule(e.target.value)} placeholder="请输入或选择所属模块" - readOnly required /> - {actionData?.errors?.module && ( -
{actionData.errors.module}
+ {actionData?.errors?.type && ( +
{actionData.errors.type}
)}
- {Object.entries(MODULE_LABELS).map(([value, label]) => ( + {types.map((type: string) => ( ))}
@@ -542,23 +405,23 @@ export default function ConfigNew() { type="text" id="environmentDisplay" className={`form-input ${actionData?.errors?.environment ? 'input-error' : ''}`} - value={selectedEnvironment ? EXTENDED_ENVIRONMENT_LABELS[selectedEnvironment] || selectedEnvironment : ''} + value={selectedEnvironment} + onChange={(e) => setSelectedEnvironment(e.target.value)} placeholder="请输入或选择环境" - readOnly required /> {actionData?.errors?.environment && (
{actionData.errors.environment}
)}
- {Object.entries(EXTENDED_ENVIRONMENT_LABELS).map(([value, label]) => ( + {environments.map((env: string) => ( ))}
@@ -569,14 +432,14 @@ export default function ConfigNew() { {/* 配置数据 */}
- +
{/* 左侧JSON编辑区 */}
+ {actionData?.errors?.template_content && ( +
{actionData.errors.template_content}
+ )}
提示词模板是AI完成特定任务的指令,请清晰描述任务需求和输出格式
@@ -642,6 +641,7 @@ export default function PromptsNew() { className="form-input" value={varName} readOnly + /> {!isViewMode && (
- +
); } \ No newline at end of file diff --git a/app/styles/pages/config-lists_new.css b/app/styles/pages/config-lists_new.css index db8c050..b6171d6 100644 --- a/app/styles/pages/config-lists_new.css +++ b/app/styles/pages/config-lists_new.css @@ -41,7 +41,7 @@ .config-new-page .form-input, .config-new-page .form-select, .config-new-page .form-textarea { - @apply block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm; + @apply block w-full border rounded-md shadow-sm py-2 px-3 focus:outline-none sm:text-sm; } .config-new-page .form-textarea { @@ -97,9 +97,9 @@ /* JSON编辑器 */ .config-new-page .json-editor { - @apply w-full min-h-[320px] font-mono text-sm leading-relaxed - border border-gray-300 rounded-md p-3 bg-gray-50 text-gray-800 - focus:outline-none focus:ring-primary-500 focus:border-primary-500; + @apply w-full min-h-[400px] font-mono text-sm leading-relaxed + border rounded-md p-3 bg-gray-50 text-gray-800 + focus:outline-none focus:border-[#00684a] focus:ring focus:ring-[#00684a] focus:ring-opacity-20; } .config-new-page .editor-actions { @@ -108,7 +108,7 @@ /* 示例卡片 */ .config-new-page .example-card { - @apply h-full flex flex-col bg-gray-50 rounded-md border border-gray-200 overflow-hidden; + @apply h-[500px] flex flex-col bg-gray-50 rounded-md border border-gray-200 overflow-hidden; } .config-new-page .example-header { @@ -120,7 +120,7 @@ } .config-new-page .example-content { - @apply p-3 flex-grow overflow-auto; + @apply p-3 flex-grow overflow-auto text-sm ; } .config-new-page .example-pre { @@ -136,4 +136,43 @@ .config-new-page .code-json .string { @apply text-green-600; } .config-new-page .code-json .number { @apply text-purple-600; } .config-new-page .code-json .boolean { @apply text-blue-600; } -.config-new-page .code-json .null { @apply text-gray-500; } \ No newline at end of file +.config-new-page .code-json .null { @apply text-gray-500; } + +.config-new-page .json-display { + font-family: monospace; + white-space: pre; + background-color: #f8fafc; + padding: 1rem; + border-radius: 0.375rem; + overflow-x: auto; +} + +.config-new-page .json-line { + line-height: 1.5; +} + +.config-new-page .json-char { + color: #334155; +} + +.config-new-page .json-brace { + color: #64748b; + font-weight: bold; +} + +.config-new-page .json-bracket { + color: #64748b; + font-weight: bold; +} + +.config-new-page .json-quote { + color: #0ea5e9; +} + +.config-new-page .json-colon { + color: #64748b; +} + +.config-new-page .json-comma { + color: #64748b; +} \ No newline at end of file diff --git a/app/styles/pages/document-types_index.css b/app/styles/pages/document-types_index.css new file mode 100644 index 0000000..e69de29 diff --git a/app/styles/pages/files_upload.css b/app/styles/pages/files_upload.css index c685715..7dc7434 100644 --- a/app/styles/pages/files_upload.css +++ b/app/styles/pages/files_upload.css @@ -38,7 +38,7 @@ } .file-upload-page .form-select { - @apply block w-full px-3 py-2 text-base border-gray-300 rounded-md shadow-sm focus:outline-none; + @apply block w-full px-3 py-2 text-base rounded-md shadow-sm focus:outline-none; } .file-upload-page .form-select:focus { diff --git a/app/styles/pages/prompts_new.css b/app/styles/pages/prompts_new.css index 2e3586d..5dac2aa 100644 --- a/app/styles/pages/prompts_new.css +++ b/app/styles/pages/prompts_new.css @@ -41,7 +41,8 @@ .prompt-new-page .form-input:focus, .prompt-new-page .form-select:focus, -.prompt-new-page .form-textarea:focus { +.prompt-new-page .form-textarea:focus, +.prompt-new-page .form-code-editor:focus { @apply outline-none border-[var(--primary-color)] shadow-[0_0_0_2px_rgba(0,104,74,0.2)]; } diff --git a/html/文档类型-列表.html b/html/文档类型-列表.html index 99f4cb1..300010c 100644 --- a/html/文档类型-列表.html +++ b/html/文档类型-列表.html @@ -4,10 +4,12 @@ 中国烟草AI合同及卷宗审核系统 - 文档类型列表 - - + + + - +