Files
leaudit-platform-frontend/html/评查点-新增.html
T

2796 lines
149 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>中国烟草AI合同及卷宗审核系统 - 新增评查点</title>
<link href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<!-- 引入外部CSS文件 -->
<link href="./main.css" rel="stylesheet">
<!-- 添加Highlight.js样式 - 使用monokai-sublime主题 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/monokai-sublime.min.css">
<!-- 引入Highlight.js主要库 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<!-- 特别引入JavaScript语言支持 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/javascript.min.js"></script>
<!-- 特别引入Python语言支持 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
<style>
/* 新增切换按钮样式 */
#extraction-method-tabs {
display: flex;
border-bottom: 1px solid #f0f0f0;
background-color: white;
border-radius: 4px;
overflow: hidden;
}
#extraction-method-tabs .tab-nav-item {
padding: 10px 16px;
font-size: 14px;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s;
display: flex;
align-items: center;
}
#extraction-method-tabs .tab-nav-item:hover {
color: var(--primary-color);
background-color: rgba(0, 104, 74, 0.05);
}
#extraction-method-tabs .tab-nav-item.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
background-color: rgba(0, 104, 74, 0.1);
font-weight: 500;
}
.extraction-config {
padding: 12px;
border: 1px solid #f0f0f0;
border-radius: 4px;
margin-bottom: 12px;
}
.field-tags-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
}
/* 紧凑型字段标签样式 */
.extraction-config .chips-container {
padding: 6px;
min-height: 36px;
}
.extraction-config .chip {
padding: 3px 6px;
margin-right: 6px;
margin-bottom: 6px;
font-size: 12px;
}
.extraction-config .chip .close-btn {
margin-left: 4px;
font-size: 12px;
}
/* 使表单元素更紧凑 */
.extraction-config .form-input,
.extraction-config .form-select,
.extraction-config button.ant-btn {
padding: 4px 8px;
font-size: 13px;
height: auto;
line-height: 1.5;
}
.field-tag {
background-color: #f5f5f5;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 4px 12px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s;
}
.field-tag:hover {
background-color: #e8f5e9;
border-color: #a5d6a7;
}
.field-tag.selected {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
/* 代码编辑器样式 */
.code-editor-wrapper {
border: 1px solid #e0e0e0;
border-radius: 4px;
overflow: hidden;
background-color: #272822; /* Monokai背景色 */
}
.code-editor-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background-color: #2d2d2d;
border-bottom: 1px solid #444;
color: #f8f8f2;
}
.code-editor-filename {
font-family: monospace;
font-size: 13px;
color: #f8f8f2;
}
.code-editor-actions {
display: flex;
gap: 8px;
}
.code-editor-actions i {
cursor: pointer;
color: #d4d4d4;
}
.code-editor-actions i:hover {
color: #ffffff;
}
.code-editor-container {
position: relative;
}
.code-editor-textarea {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
padding: 12px;
width: 100%;
height: 400px; /* 原来是200px,加高2倍 */
background-color: #272822;
color: #f8f8f2;
border: none;
resize: vertical;
min-height: 400px; /* 原来是200px,加高2倍 */
tab-size: 4;
position: relative;
z-index: 2; /* 确保输入框在高亮层之上 */
}
.code-editor-textarea:focus {
outline: none;
box-shadow: none;
}
.code-editor-textarea::placeholder {
color: #75715e;
}
.hljs {
background: #272822 !important;
padding: 12px !important;
max-height: 400px; /* 原来是200px,加高2倍 */
overflow: auto;
}
/* 修改highlight.js样式以更好地匹配编辑器 */
.hljs-container {
position: absolute;
top: 0;
left: 0;
right: 0;
pointer-events: none; /* 确保不拦截鼠标事件 */
z-index: 1; /* 确保在编辑器输入框下方 */
height: 400px; /* 加高2倍 */
overflow: auto;
opacity: 1; /* 确保高亮内容可见 */
}
.hljs-container pre {
margin: 0;
padding: 0;
}
.hljs-container code {
display: block;
padding: 12px !important;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
overflow: visible;
white-space: pre-wrap;
background: transparent !important;
}
.code-copy-success {
position: fixed;
bottom: 20px;
right: 20px;
background-color: rgba(0, 104, 74, 0.9);
color: white;
padding: 8px 16px;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
transition: all 0.3s;
opacity: 0;
transform: translateY(20px);
}
.code-copy-success.show {
opacity: 1;
transform: translateY(0);
}
/* Prism.js覆盖样式 */
pre[class*="language-"] {
margin: 0;
border-radius: 0;
}
/* 隐藏的编辑器 */
.hidden-editor {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
z-index: 2;
}
/* 新增变量标签样式 */
.var-tag {
display: inline-flex;
align-items: center;
padding: 4px 8px;
background-color: rgba(0, 104, 74, 0.1);
color: var(--primary-color);
border: 1px solid rgba(0, 104, 74, 0.2);
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
}
.var-tag:hover {
background-color: rgba(0, 104, 74, 0.2);
border-color: rgba(0, 104, 74, 0.3);
}
.var-tag::before {
content: '{';
margin-right: 1px;
}
.var-tag::after {
content: '}';
margin-left: 1px;
}
.law-reference {
background-color: #f0f9ff;
border: 1px solid #bae0ff;
border-radius: 4px;
padding: 12px;
margin-top: 8px;
}
.law-reference-title {
color: #0958d9;
font-weight: 500;
margin-bottom: 8px;
}
.law-reference-articles {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 8px;
}
.law-article {
background-color: #e6f4ff;
border: 1px solid #91caff;
border-radius: 12px;
padding: 2px 8px;
font-size: 12px;
color: #0958d9;
}
.law-reference-content {
font-size: 13px;
color: #434343;
line-height: 1.6;
border-left: 3px solid #bae0ff;
padding-left: 8px;
margin-top: 8px;
}
.expand-icon {
transition: transform 0.3s;
}
.expanded .expand-icon {
transform: rotate(180deg);
}
/* 自定义宽度类 */
.w-3\/10 {
width: 30%;
}
.w-7\/10 {
width: 70%;
}
/* 正则表达式配置行样式优化 */
.regex-field-row {
padding: 6px !important;
margin-bottom: 6px !important;
}
.regex-field-row input {
padding: 3px 6px;
font-size: 12px;
}
</style>
</head>
<body>
<div class="layout-container">
<!-- 侧边栏 -->
<div class="sidebar">
<div class="py-6 px-4 border-b border-gray-100">
<div class="flex items-center">
<i class="ri-file-search-line text-primary text-xl mr-2"></i>
<h2 class="text-lg font-medium">AI审核系统</h2>
</div>
</div>
<div class="py-4">
<div class="sidebar-menu-item">
<a href="#" class="flex items-center text-sm font-medium">
<i class="ri-dashboard-line mr-3"></i>
<span>首页</span>
</a>
</div>
<div class="sidebar-menu-item">
<a href="#" class="flex items-center text-sm font-medium">
<i class="ri-file-upload-line mr-3"></i>
<span>文件上传</span>
</a>
</div>
<div class="sidebar-menu-item active">
<a href="#" class="flex items-center text-sm font-medium">
<i class="ri-list-check-2 mr-3"></i>
<span>评查规则库</span>
</a>
</div>
<div class="sidebar-menu-item">
<a href="#" class="flex items-center text-sm font-medium">
<i class="ri-history-line mr-3"></i>
<span>审核历史</span>
</a>
</div>
<div class="sidebar-menu-item">
<a href="#" class="flex items-center text-sm font-medium">
<i class="ri-settings-3-line mr-3"></i>
<span>系统设置</span>
</a>
</div>
</div>
</div>
<!-- 主内容区 -->
<div class="main-content">
<!-- 面包屑导航 -->
<div class="breadcrumb">
<span class="breadcrumb-item">首页</span>
<span class="breadcrumb-item">评查规则库</span>
<span class="breadcrumb-item">合同基本要素检查</span>
<span class="breadcrumb-item">评查点管理</span>
<span class="breadcrumb-item">新增评查点</span>
</div>
<!-- 页面头部 -->
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-medium">新增评查点</h2>
<div class="flex">
<button class="ant-btn ant-btn-default mr-2">
<i class="ri-arrow-left-line"></i> 返回
</button>
<button class="ant-btn ant-btn-primary">
<i class="ri-save-line"></i> 保存
</button>
</div>
</div>
<!-- 基本信息 -->
<div class="ant-card">
<div class="ant-card-header">
<h3>基本信息</h3>
</div>
<div class="ant-card-body">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<label class="form-label">
评查点名称 <span class="required-mark">*</span>
</label>
<input type="text" class="form-input" placeholder="请输入评查点名称">
<div class="form-tip">请使用简洁明了的名称,不超过30个字符</div>
</div>
<div>
<label class="form-label">
评查点编码 <span class="required-mark">*</span>
</label>
<input type="text" class="form-input" placeholder="请输入评查点编码">
<div class="form-tip">用于系统标识的唯一编码</div>
</div>
<div>
<label class="form-label">
风险等级: <span class="required-mark">*</span>
</label>
<select class="form-select">
<option value="high">高风险</option>
<option value="medium">中风险</option>
<option value="low">低风险</option>
</select>
<div class="form-tip">请定义评查点的风险等级</div>
</div>
<div>
<label class="form-label">
评查点类型 <span class="required-mark">*</span>
</label>
<select class="form-select" id="checkpointType">
<option value="">请选择评查点类型</option>
<option value="essential">基本要素类</option>
<option value="content">内容合规类</option>
<option value="format">格式规范类</option>
<option value="legal">法律风险类</option>
<option value="business">业务专项类</option>
</select>
<div class="form-tip">评查点类型用于分类管理,便于规则统一调用</div>
</div>
<div>
<label class="form-label">所属规则组</label>
<select class="form-select">
<option value="contract-base">合同基本要素检查</option>
<option value="contract-sales">销售合同专项检查</option>
<option value="contract-purchase">采购合同专项检查</option>
<option value="license">专卖许可证审核规则</option>
<option value="punishment">行政处罚规范性检查</option>
</select>
</div>
<div>
<label class="form-label">是否启用</label>
<select class="form-select">
<option value="true"></option>
<option value="false"></option>
</select>
<div class="form-tip">创建后是否立即启用此评查点</div>
</div>
<div class="col-span-1 md:col-span-3">
<div class="flex justify-between items-center cursor-pointer" id="description-toggle">
<label class="form-label mb-0">评查点描述与法律依据</label>
<i class="ri-arrow-down-s-line text-lg expand-icon"></i>
</div>
<div id="description-section" class="hidden mt-2">
<div class="mb-4">
<textarea class="form-textarea" style="min-height: 80px;" placeholder="请输入评查点描述,包括适用场景、评查目的等"></textarea>
<div class="form-tip">详细描述有助于其他用户了解该评查点的用途</div>
</div>
<!-- 引用法典输入区域 -->
<div class="border-t border-gray-100 pt-4 mb-4">
<label class="form-label">引用法典</label>
<div class="mb-3">
<label class="text-sm text-gray-600 mb-1 block">法典名称</label>
<input type="text" class="form-input" placeholder="例如:《中华人民共和国民法典》" id="law-name">
</div>
<div class="mb-3">
<label class="text-sm text-gray-600 mb-1 block">条款号 <span class="text-xs text-gray-400">(多个条款请用逗号分隔)</span></label>
<input type="text" class="form-input" placeholder="例如:第五百八十五条,第五百八十六条" id="law-articles">
<div class="form-tip">多个条款用逗号分隔,将自动转换为数组格式</div>
</div>
<div class="mb-3">
<label class="text-sm text-gray-600 mb-1 block">条款内容</label>
<textarea class="form-textarea" style="min-height: 60px;" placeholder="例如:当事人应当按照约定全面履行自己的义务。" id="law-content"></textarea>
</div>
<div class="p-3 bg-blue-50 border border-blue-200 rounded-md text-sm text-blue-700 mb-2">
<i class="ri-information-line mr-1"></i> 引用的法律条文将在评查结果中显示,帮助用户理解评查规则的法律依据
</div>
<!-- 预览区域 -->
<div class="p-3 border border-gray-200 rounded-md bg-gray-50 mt-3">
<div class="text-sm font-medium mb-2">预览效果</div>
<div class="law-reference">
<div class="law-reference-title" id="preview-law-name">《中华人民共和国民法典》</div>
<div class="law-reference-articles" id="preview-law-articles">
<span class="law-article">第五百八十五条</span>
<span class="law-article">第五百八十六条</span>
</div>
<div class="law-reference-content" id="preview-law-content">
当事人应当按照约定全面履行自己的义务。
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 抽取设置 -->
<div class="ant-card">
<div class="ant-card-header">
<h3>抽取设置</h3>
</div>
<div class="ant-card-body">
<div class="mb-6">
<!-- 将单选按钮改为切换按钮 -->
<div class="tab-nav mb-4" id="extraction-method-tabs">
<div class="tab-nav-item active" data-method="llm_ocr">
<i class="ri-brain-line mr-1"></i> 大模型抽取
</div>
<div class="tab-nav-item" data-method="llm">
<i class="ri-scan-line mr-1"></i> 多模态抽取
</div>
<div class="tab-nav-item" data-method="ocr_regex">
<i class="ri-code-box-line mr-1"></i> 正则抽取
</div>
</div>
</div>
<!-- 大模型抽取配置(原OCR+LLM -->
<div class="extraction-config" id="llm-ocr-config">
<div class="grid grid-cols-1 gap-3">
<div class="col-span-1">
<label class="form-label mb-1">抽取字段</label>
<div class="flex mb-2">
<input type="text" class="form-input mr-2" id="field-input-ocr" placeholder="请输入字段名,多个字段可用、或,或空格分隔">
<button class="ant-btn ant-btn-default" id="add-field-btn-ocr">添加</button>
</div>
<div class="chips-container" id="fields-container-ocr">
<div class="chip">合同编号 <span class="close-btn">&times;</span></div>
<div class="chip">合同金额 <span class="close-btn">&times;</span></div>
<div class="chip">签订日期 <span class="close-btn">&times;</span></div>
<div class="chip">甲方名称 <span class="close-btn">&times;</span></div>
<div class="chip">乙方名称 <span class="close-btn">&times;</span></div>
</div>
<div class="form-tip mt-1 text-xs">支持一次输入多个字段</div>
</div>
</div>
<div class="grid grid-cols-1 gap-3 mt-3">
<div class="col-span-1">
<label class="form-label mb-1">提示词设置</label>
<div class="flex items-center mb-2">
<label class="inline-flex items-center mr-6">
<input type="radio" name="llm-prompt-type" value="system" checked class="form-radio"
onclick="document.getElementById('llm-custom-prompt-container').style.display='none';
document.getElementById('llm-system-prompt-info').style.display='block';">
<span class="ml-2">使用系统默认提示词</span>
</label>
<label class="inline-flex items-center">
<input type="radio" name="llm-prompt-type" value="custom" class="form-radio"
onclick="document.getElementById('llm-custom-prompt-container').style.display='block';
document.getElementById('llm-system-prompt-info').style.display='none';">
<span class="ml-2">使用自定义提示词</span>
</label>
</div>
<div class="bg-gray-50 p-2 rounded text-xs text-gray-600 mb-2" id="llm-system-prompt-info">
系统将根据评查点类型和抽取目标自动生成适合的提示词,您无需额外配置。
</div>
<div id="llm-custom-prompt-container" style="display: none;" class="border border-dashed border-gray-300 p-3 rounded-md">
<div class="mb-2">
<label class="form-label mb-1 text-sm">选择提示词模板</label>
<select class="form-select" id="llm-prompt-template">
<option value="">请选择模板</option>
<option value="1">行政处罚-抽取通用模板</option>
<option value="4">采购合同-乙方资质抽取</option>
<option value="5">合同-关键条款抽取</option>
<option value="6">烟草许可证-信息抽取</option>
</select>
</div>
<div class="mb-2">
<label class="form-label mb-1 text-sm">提示词内容</label>
<textarea class="form-textarea" id="llm-prompt-content" rows="4" placeholder="选择模板后自动填充,您也可以进行修改..." readonly></textarea>
<div class="form-tip mt-1 bg-gray-50 p-2 rounded text-xs">
<p class="mb-1"><strong>支持的变量</strong>(点击变量将其添加到提示词中):</p>
<div class="flex flex-wrap gap-1">
<button type="button" class="var-tag" data-var="docType">docType</button>
<button type="button" class="var-tag" data-var="fieldsList">fieldsList</button>
<button type="button" class="var-tag" data-var="companyName">companyName</button>
<button type="button" class="var-tag" data-var="documentId">documentId</button>
<button type="button" class="var-tag" data-var="date">date</button>
<button type="button" class="var-tag" data-var="industry">industry</button>
<button type="button" class="var-tag" data-var="ocrText">ocrText</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 多模态抽取配置(原LLM抽取) -->
<div class="extraction-config hidden" id="llm-config">
<div class="grid grid-cols-1 gap-3">
<div class="col-span-1">
<label class="form-label mb-1">抽取字段与类型</label>
<div class="flex mb-2">
<input type="text" class="form-input mr-2" id="field-input" placeholder="请输入字段名">
<select class="form-select mr-2" id="field-type">
<option value="default">默认</option>
<option value="seal">印章</option>
<option value="cross-seal">骑缝章</option>
<option value="handwriting">手写体</option>
<option value="print">印刷体</option>
<option value="english">英文</option>
<option value="number">数字</option>
<option value="currency">货币</option>
</select>
<button class="ant-btn ant-btn-default" id="add-field-btn">添加</button>
</div>
<div class="chips-container" id="fields-container">
<div class="chip">合同编号 <span class="badge bg-blue-100 text-blue-800 text-xs ml-1">默认</span> <span class="close-btn">&times;</span></div>
<div class="chip">合同金额 <span class="badge bg-green-100 text-green-800 text-xs ml-1">货币</span> <span class="close-btn">&times;</span></div>
<div class="chip">签订日期 <span class="badge bg-purple-100 text-purple-800 text-xs ml-1">印刷体</span> <span class="close-btn">&times;</span></div>
<div class="chip">甲方签章 <span class="badge bg-red-100 text-red-800 text-xs ml-1">印章</span> <span class="close-btn">&times;</span></div>
<div class="chip">手写签名 <span class="badge bg-yellow-100 text-yellow-800 text-xs ml-1">手写体</span> <span class="close-btn">&times;</span></div>
</div>
<div class="form-tip mt-1 text-xs">请为每个字段选择适当的抽取类型,有助于提高识别准确率</div>
</div>
</div>
<div class="grid grid-cols-1 gap-3 mt-3">
<div class="col-span-1">
<label class="form-label mb-1">提示词设置</label>
<div class="flex items-center mb-2">
<label class="inline-flex items-center mr-6">
<input type="radio" name="multimodal-prompt-type" value="system" checked class="form-radio"
onclick="document.getElementById('multimodal-custom-prompt-container').style.display='none';
document.getElementById('multimodal-system-prompt-info').style.display='block';">
<span class="ml-2">使用系统默认提示词</span>
</label>
<label class="inline-flex items-center">
<input type="radio" name="multimodal-prompt-type" value="custom" class="form-radio"
onclick="document.getElementById('multimodal-custom-prompt-container').style.display='block';
document.getElementById('multimodal-system-prompt-info').style.display='none';">
<span class="ml-2">使用自定义提示词</span>
</label>
</div>
<div class="bg-gray-50 p-2 rounded text-xs text-gray-600 mb-2" id="multimodal-system-prompt-info">
系统将根据评查点类型和抽取目标自动生成适合的提示词,支持图表、印章等图像内容抽取。
</div>
<div id="multimodal-custom-prompt-container" style="display: none;" class="border border-dashed border-gray-300 p-3 rounded-md">
<div class="mb-2">
<label class="form-label mb-1 text-sm">选择提示词模板</label>
<select class="form-select" id="multimodal-prompt-template">
<option value="">请选择模板</option>
<option value="1">行政处罚-抽取通用模板</option>
<option value="4">采购合同-乙方资质抽取</option>
<option value="7">多模态-印章识别模板</option>
<option value="8">多模态-表格抽取模板</option>
<option value="9">多模态-手写内容识别模板</option>
</select>
</div>
<div class="mb-2">
<label class="form-label mb-1 text-sm">提示词内容</label>
<textarea class="form-textarea" id="multimodal-prompt-content" rows="4" placeholder="选择模板后自动填充,您也可以进行修改..." readonly></textarea>
<div class="form-tip mt-1 bg-gray-50 p-2 rounded text-xs">
<p class="mb-1"><strong>支持的变量</strong>(点击变量将其添加到提示词中):</p>
<div class="flex flex-wrap gap-1">
<button type="button" class="var-tag" data-var="docType">docType</button>
<button type="button" class="var-tag" data-var="fieldsList">fieldsList</button>
<button type="button" class="var-tag" data-var="companyName">companyName</button>
<button type="button" class="var-tag" data-var="documentId">documentId</button>
<button type="button" class="var-tag" data-var="date">date</button>
<button type="button" class="var-tag" data-var="industry">industry</button>
<button type="button" class="var-tag" data-var="contentType">contentType</button>
<button type="button" class="var-tag" data-var="pageRange">pageRange</button>
<button type="button" class="var-tag" data-var="colorMode">colorMode</button>
<button type="button" class="var-tag" data-var="ocrText">ocrText</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 正则抽取配置(原OCR+正则) -->
<div class="extraction-config hidden" id="ocr-regex-config">
<div class="grid grid-cols-1 gap-3">
<div class="col-span-1">
<div class="mb-2">
<div class="flex justify-between items-center mb-1">
<label class="form-label m-0">字段正则表达式配置</label>
<button class="ant-btn ant-btn-default" id="add-regex-field-row">
<i class="ri-add-line"></i> 添加字段
</button>
</div>
<div class="mt-2" id="regex-fields-container">
<!-- 字段-正则表达式配置行 -->
<div class="regex-field-row flex items-start mb-2 border border-gray-200 rounded-md p-2 bg-gray-50">
<div class="w-3/10 mr-2">
<label class="text-xs text-gray-600 mb-0 block">字段名称</label>
<input type="text" class="form-input regex-field-name" placeholder="如:合同编号">
</div>
<div class="w-7/10 mr-2">
<label class="text-xs text-gray-600 mb-0 block">正则表达式</label>
<input type="text" class="form-input regex-expression" placeholder="如:\\d{4}[-/年](0?[1-9]|1[0-2])[-/月](0?[1-9]|[12][0-9]|3[01])[日]?">
</div>
<div class="flex flex-col justify-end pt-3">
<button class="text-red-500 hover:text-red-700 remove-regex-field-row">
<i class="ri-delete-bin-line"></i>
</button>
</div>
</div>
</div>
</div>
<div class="mt-2">
<label class="form-label mb-1">常用正则模板</label>
<div class="flex flex-wrap gap-1 mt-1">
<div class="chip cursor-pointer regex-template" data-regex="\\d{4}[-/年](0?[1-9]|1[0-2])[-/月](0?[1-9]|[12][0-9]|3[01])[日]?">日期格式:yyyy-mm-dd</div>
<div class="chip cursor-pointer regex-template" data-regex="[A-Z]{2,5}-\\d{4,10}">合同编号格式</div>
<div class="chip cursor-pointer regex-template" data-regex="(人民币|RMB)?\\s?(\\d{1,3}(,\\d{3})*(\\.\\d{2})?)\\s?[万元]?">金额格式</div>
<div class="chip cursor-pointer regex-template" data-regex="\\d{3}-\\d{8}|\\d{4}-\\d{7,8}">座机号码格式</div>
<div class="chip cursor-pointer regex-template" data-regex="1[3-9]\\d{9}">手机号码格式</div>
<div class="chip cursor-pointer regex-template" data-regex="[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]">身份证号码格式</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
/* 新增切换按钮样式 */
#extraction-method-tabs {
display: flex;
border-bottom: 1px solid #f0f0f0;
background-color: white;
border-radius: 4px;
overflow: hidden;
}
#extraction-method-tabs .tab-nav-item {
padding: 10px 16px;
font-size: 14px;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s;
display: flex;
align-items: center;
}
#extraction-method-tabs .tab-nav-item:hover {
color: var(--primary-color);
background-color: rgba(0, 104, 74, 0.05);
}
#extraction-method-tabs .tab-nav-item.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
background-color: rgba(0, 104, 74, 0.1);
font-weight: 500;
}
.extraction-config {
padding: 12px;
border: 1px solid #f0f0f0;
border-radius: 4px;
margin-bottom: 12px;
}
/* 自定义宽度类 */
.w-3\/10 {
width: 30%;
}
.w-7\/10 {
width: 70%;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 初始化highlight.js
hljs.configure({
languages: ['javascript', 'python']
});
// 调试代码 - 确保页面加载后检查所有自定义提示词容器
// 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("设置提示词类型切换事件");
// 直接绑定事件到单选按钮
document.querySelectorAll('input[name="llm-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
// console.log("大模型提示词类型变更:", this.value);
const customContainer = document.getElementById('llm-custom-prompt-container');
const systemInfo = document.getElementById('llm-system-prompt-info');
if (this.value === 'system') {
if (customContainer) customContainer.style.display = 'none';
if (systemInfo) systemInfo.style.display = 'block';
} else {
if (customContainer) customContainer.style.display = 'block';
if (systemInfo) systemInfo.style.display = 'none';
}
});
});
document.querySelectorAll('input[name="multimodal-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
// console.log("多模态提示词类型变更:", this.value);
const customContainer = document.getElementById('multimodal-custom-prompt-container');
const systemInfo = document.getElementById('multimodal-system-prompt-info');
if (this.value === 'system') {
if (customContainer) customContainer.style.display = 'none';
if (systemInfo) systemInfo.style.display = 'block';
} else {
if (customContainer) customContainer.style.display = 'block';
if (systemInfo) systemInfo.style.display = 'none';
}
});
});
// 页面加载完成后,确保自定义提示词容器状态正确
function initPromptContainers() {
// console.log("初始化提示词容器状态");
// 大模型抽取
const llmPromptType = document.querySelector('input[name="llm-prompt-type"]:checked');
if (llmPromptType) {
// 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';
}
}
// 多模态抽取
const multimodalPromptType = document.querySelector('input[name="multimodal-prompt-type"]:checked');
if (multimodalPromptType) {
// 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';
}
}
}
// 确保初始化在页面完全加载后执行
setTimeout(initPromptContainers, 100);
// 抽取方式切换
const extractMethodTabs = document.querySelectorAll('#extraction-method-tabs .tab-nav-item');
const extractConfigs = {
'llm': document.getElementById('llm-config'),
'llm_ocr': document.getElementById('llm-ocr-config'),
'ocr_regex': document.getElementById('ocr-regex-config')
};
extractMethodTabs.forEach(tab => {
tab.addEventListener('click', function() {
// 移除所有tab的活动状态
extractMethodTabs.forEach(t => t.classList.remove('active'));
// 添加当前tab的活动状态
this.classList.add('active');
// 隐藏所有配置
Object.values(extractConfigs).forEach(config => {
if (config) config.classList.add('hidden');
});
// 显示选中的配置
const method = this.getAttribute('data-method');
if (extractConfigs[method]) {
extractConfigs[method].classList.remove('hidden');
}
});
});
// 原有的其他 JavaScript 代码保持不变...
});
</script>
<!-- 评查设置 -->
<div class="ant-card">
<div class="ant-card-header">
<h3>评查设置</h3>
</div>
<div class="ant-card-body">
<div class="flex items-center justify-between mb-4">
<div>
<h5 class="font-medium">评查逻辑规则</h5>
<div class="text-sm text-secondary mt-1">可添加多个评查规则,通过组合逻辑实现复杂评查</div>
</div>
<div>
<span class="badge rounded-pill bg-primary text-white px-2 py-1" id="logic-count">1个规则</span>
</div>
</div>
<div class="mb-6">
<label class="form-label">
组合逻辑 <span class="required-mark">*</span>
</label>
<div class="form-radio-group">
<label class="form-radio-item">
<input type="radio" name="ruleLogic" class="form-radio" value="all" checked>
<span>全部满足(AND</span>
</label>
<label class="form-radio-item">
<input type="radio" name="ruleLogic" class="form-radio" value="any">
<span>任一满足(OR</span>
</label>
<label class="form-radio-item">
<input type="radio" name="ruleLogic" class="form-radio" value="custom">
<span>自定义组合</span>
</label>
</div>
</div>
<!-- 自定义组合逻辑配置 -->
<div class="mb-6 hidden" id="rule-custom-logic">
<label class="form-label">自定义组合逻辑 <span class="required-mark">*</span></label>
<textarea class="form-textarea" placeholder="请输入自定义组合逻辑,例如:(规则1 AND 规则2) OR 规则3"></textarea>
<div class="form-tip">使用规则编号和逻辑运算符(AND、OR、NOT)组合</div>
</div>
<!-- 评查规则容器 -->
<div id="rule-items-container">
<!-- 第一条评查规则 -->
<div class="rule-item border border-dashed border-gray-300 rounded-md p-6 mb-6 relative">
<div class="absolute top-3 right-3 flex gap-2">
<span class="badge rounded-pill bg-primary text-white px-2 py-1">规则 #1</span>
<button class="text-gray-400 hover:text-red-500" disabled>
<i class="ri-delete-bin-line"></i>
</button>
</div>
<div class="mb-4">
<label class="form-label">
评查类型 <span class="required-mark">*</span>
</label>
<select class="form-select rule-type-select" data-index="1">
<option value="">请选择评查类型</option>
<option value="exists">有无判断</option>
<option value="consistency">一致性判断</option>
<option value="format">格式判断</option>
<option value="logic">逻辑判断</option>
<option value="regex">正则表达式</option>
<option value="ai">大模型判断</option>
<option value="code">自定义代码</option>
</select>
<div class="form-tip">选择评查类型后将显示对应的配置项</div>
</div>
<!-- 规则配置区域 - 将动态显示 -->
<div class="rule-config-container" data-index="1">
<!-- 配置内容将根据选择的评查类型动态加载 -->
<div class="rule-placeholder text-center py-6 text-secondary">
<i class="ri-settings-3-line text-4xl mb-2 block"></i>
<p>请先选择评查类型</p>
</div>
</div>
</div>
</div>
<!-- 添加规则按钮 -->
<div class="text-center mb-6">
<button class="ant-btn ant-btn-default" id="add-rule-btn">
<i class="ri-add-line mr-1"></i> 添加评查规则
</button>
</div>
<div class="divider"></div>
<!-- 评查结果提示信息 -->
<div class="mb-4">
<label class="form-label">
评查结果提示信息
</label>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label class="form-label text-sm">评查通过信息</label>
<textarea class="form-textarea" style="height: 80px !important; min-height: 60px !important;" rows="1" placeholder="请输入评查通过时的提示信息">文档检查通过,符合规范要求。</textarea>
</div>
<div>
<label class="form-label text-sm">评查不通过信息</label>
<textarea class="form-textarea" style="height: 80px !important; min-height: 60px !important;" rows="1" placeholder="请输入评查不通过时的提示信息">文档存在以下问题,请修改后重新提交。</textarea>
</div>
<div>
<label class="form-label text-sm">建议信息</label>
<textarea class="form-textarea" style="height: 80px !important; min-height: 60px !important;" rows="1" placeholder="请输入对用户的建议信息"></textarea>
</div>
</div>
</div>
<!-- 不通过提示类别 -->
<style>
/* 隐藏单选按钮 */
.severity-radio {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
</style>
<div class="mb-4">
<label class="form-label">建议信息类别</label>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<label class="flex items-center cursor-pointer border-l-4 border-blue-500 bg-blue-50 p-3 hover:bg-blue-100 rounded-r-md severity-option" data-value="info">
<input type="radio" name="errorSeverity" class="severity-radio" value="info">
<i class="ri-information-line text-blue-500 text-xl mr-3"></i>
<div>
<div class="font-medium">提示 (Info)</div>
<div class="text-sm text-gray-500">提示性信息,不影响</div>
</div>
</label>
<label class="flex items-center cursor-pointer border-l-4 border-yellow-500 bg-yellow-50 p-3 hover:bg-yellow-100 rounded-r-md severity-option" data-value="warning">
<input type="radio" name="errorSeverity" class="severity-radio" value="warning">
<i class="ri-alert-line text-yellow-500 text-xl mr-3"></i>
<div>
<div class="font-medium">警告 (Warning)</div>
<div class="text-sm text-gray-500">警告信息,建议修改但不强制</div>
</div>
</label>
<label class="flex items-center cursor-pointer border-l-4 border-red-500 bg-red-50 p-3 hover:bg-red-100 rounded-r-md severity-option" data-value="error">
<input type="radio" name="errorSeverity" class="severity-radio" value="error" checked>
<i class="ri-error-warning-line text-red-500 text-xl mr-3"></i>
<div>
<div class="font-medium">错误 (Error)</div>
<div class="text-sm text-gray-500">严重错误,必须修正</div>
</div>
</label>
</div>
<div class="form-tip">不同类别会影响问题的展示方式和处理流程</div>
</div>
<script>
// 为不通过提示类别添加单选效果
document.addEventListener('DOMContentLoaded', function() {
const severityOptions = document.querySelectorAll('.severity-option');
const severityRadios = document.querySelectorAll('.severity-radio');
// 初始化选中状态
updateSelectedState();
// 为每个选项添加点击事件
severityOptions.forEach(option => {
option.addEventListener('click', function() {
const radio = this.querySelector('input[type="radio"]');
radio.checked = true;
updateSelectedState();
});
});
// 更新选中状态的函数
function updateSelectedState() {
severityOptions.forEach(option => {
const radio = option.querySelector('input[type="radio"]');
if (radio.checked) {
option.classList.add('selected-severity');
option.style.borderLeftWidth = '8px';
option.style.boxShadow = '0 0 0 2px ' + getSeverityColor(radio.value);
} else {
option.classList.remove('selected-severity');
option.style.borderLeftWidth = '4px';
option.style.boxShadow = 'none';
}
});
}
// 获取对应的颜色
function getSeverityColor(value) {
switch(value) {
case 'info': return 'rgba(59, 130, 246, 0.5)'; // 蓝色
case 'warning': return 'rgba(245, 158, 11, 0.5)'; // 黄色
case 'error': return 'rgba(239, 68, 68, 0.5)'; // 红色
default: return 'transparent';
}
}
});
</script>
<!-- 评查后动作 -->
<div class="mb-4">
<label class="form-label">
评查后动作 <span class="required-mark">*</span>
</label>
<select class="form-select" id="action-type">
<option value="none" selected></option>
<option value="manual">人工确认</option>
<option value="replace">内容替换</option>
</select>
</div>
<!-- 动作描述区域 -->
<div class="mb-4 hidden" id="action-description-container">
<label class="form-label">动作描述</label>
<textarea class="form-textarea" id="action-description" placeholder="请输入动作描述,说明评查通过或未通过时的处理方式">若合同缺少必要信息,系统将自动提醒用户补充相关内容</textarea>
</div>
</div>
</div>
<!-- 操作按钮区 -->
<div class="flex justify-between mt-6 pb-10">
<button class="ant-btn ant-btn-default">
<i class="ri-draft-line"></i> 保存为草稿
</button>
<div>
<button class="ant-btn ant-btn-default mr-2">
<i class="ri-arrow-left-line"></i> 返回
</button>
<button class="ant-btn ant-btn-primary">
<i class="ri-save-line"></i> 保存
</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 初始化highlight.js
hljs.configure({
languages: ['javascript', 'python']
});
// 调试代码 - 确保页面加载后检查所有自定义提示词容器
// 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("设置提示词类型切换事件");
// 直接绑定事件到单选按钮
document.querySelectorAll('input[name="llm-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
// console.log("大模型提示词类型变更:", this.value);
const customContainer = document.getElementById('llm-custom-prompt-container');
const systemInfo = document.getElementById('llm-system-prompt-info');
if (this.value === 'system') {
if (customContainer) customContainer.style.display = 'none';
if (systemInfo) systemInfo.style.display = 'block';
} else {
if (customContainer) customContainer.style.display = 'block';
if (systemInfo) systemInfo.style.display = 'none';
}
});
});
document.querySelectorAll('input[name="multimodal-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
// console.log("多模态提示词类型变更:", this.value);
const customContainer = document.getElementById('multimodal-custom-prompt-container');
const systemInfo = document.getElementById('multimodal-system-prompt-info');
if (this.value === 'system') {
if (customContainer) customContainer.style.display = 'none';
if (systemInfo) systemInfo.style.display = 'block';
} else {
if (customContainer) customContainer.style.display = 'block';
if (systemInfo) systemInfo.style.display = 'none';
}
});
});
// 页面加载完成后,确保自定义提示词容器状态正确
function initPromptContainers() {
// console.log("初始化提示词容器状态");
// 大模型抽取
const llmPromptType = document.querySelector('input[name="llm-prompt-type"]:checked');
if (llmPromptType) {
// 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';
}
}
// 多模态抽取
const multimodalPromptType = document.querySelector('input[name="multimodal-prompt-type"]:checked');
if (multimodalPromptType) {
// 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';
}
}
}
// 确保初始化在页面完全加载后执行
setTimeout(initPromptContainers, 100);
// 抽取方式切换
const extractMethodTabs = document.querySelectorAll('#extraction-method-tabs .tab-nav-item');
const extractConfigs = {
'llm': document.getElementById('llm-config'),
'llm_ocr': document.getElementById('llm-ocr-config'),
'ocr_regex': document.getElementById('ocr-regex-config')
};
extractMethodTabs.forEach(tab => {
tab.addEventListener('click', function() {
// 移除所有tab的活动状态
extractMethodTabs.forEach(t => t.classList.remove('active'));
// 添加当前tab的活动状态
this.classList.add('active');
// 隐藏所有配置
Object.values(extractConfigs).forEach(config => {
if (config) config.classList.add('hidden');
});
// 显示选中的配置
const method = this.getAttribute('data-method');
if (extractConfigs[method]) {
extractConfigs[method].classList.remove('hidden');
}
});
});
// 评查后动作切换
const actionTypeSelect = document.getElementById('action-type');
const actionTemplates = {
'info': document.getElementById('info-template'),
'manual': document.getElementById('manual-template'),
'replace': document.getElementById('replace-template')
};
const actionDescriptionContainer = document.getElementById('action-description-container');
actionTypeSelect.addEventListener('change', function() {
// 隐藏所有模板
Object.values(actionTemplates).forEach(template => {
if (template) template.classList.add('hidden');
});
// 判断是否选择了"无"
if (this.value === 'none') {
actionDescriptionContainer.classList.add('hidden');
} else {
actionDescriptionContainer.classList.remove('hidden');
// 显示选中的模板
const selectedAction = this.value;
if (selectedAction && actionTemplates[selectedAction]) {
actionTemplates[selectedAction].classList.remove('hidden');
}
}
});
// 使用模板按钮
const templateBtns = document.querySelectorAll('.use-template-btn');
templateBtns.forEach(btn => {
btn.addEventListener('click', function() {
const targetId = this.getAttribute('data-target');
const target = targetId ? document.getElementById(targetId) :
this.closest('.template-card').previousElementSibling;
if (target) {
const template = this.closest('.template-card').querySelector('.template-content').textContent;
target.value = template;
target.focus();
}
});
});
// 字段添加功能 - LLM抽取
setupFieldsManager('field-input', 'add-field-btn', 'fields-container');
// 字段添加功能 - OCR+LLM
setupFieldsManager('field-input-ocr', 'add-field-btn-ocr', 'fields-container-ocr');
// 模板提示词选择功能
setupPromptTemplateSelector('prompt-template-select', 'llm-config');
setupPromptTemplateSelector('prompt-template-select-ocr', 'llm-ocr-config');
// 正则表达式模板点击功能
document.querySelectorAll('.regex-template').forEach(template => {
template.addEventListener('click', function() {
const regex = this.getAttribute('data-regex');
// 获取当前选中/激活的正则表达式输入框
const activeTextarea = document.activeElement;
let targetTextarea;
// 如果当前激活的元素是正则表达式输入框,则向其添加模板
if (activeTextarea && activeTextarea.classList.contains('regex-expression')) {
targetTextarea = activeTextarea;
} else {
// 否则查找页面上的最后一个正则表达式输入框
const allTextareas = document.querySelectorAll('.regex-expression');
if (allTextareas.length > 0) {
targetTextarea = allTextareas[allTextareas.length - 1];
}
}
// 如果找到目标输入框,向其添加正则模板
if (targetTextarea) {
targetTextarea.value = regex;
targetTextarea.focus();
}
});
});
// 添加正则字段行
const addRegexFieldRowBtn = document.getElementById('add-regex-field-row');
if (addRegexFieldRowBtn) {
addRegexFieldRowBtn.addEventListener('click', function() {
addRegexFieldRow();
});
}
// 初始化已有的删除按钮
initRegexFieldRowButtons();
// 添加正则字段行函数
function addRegexFieldRow() {
const container = document.getElementById('regex-fields-container');
if (container) {
// 创建新的字段行
const newRow = document.createElement('div');
newRow.className = 'regex-field-row flex items-start mb-3 border border-gray-200 rounded-md p-3 bg-gray-50';
newRow.innerHTML = `
<div class="w-3/10 mr-3">
<label class="text-sm text-gray-600 mb-1 block">字段名称</label>
<input type="text" class="form-input regex-field-name" placeholder="如:合同编号">
</div>
<div class="w-7/10 mr-3">
<label class="text-sm text-gray-600 mb-1 block">正则表达式</label>
<input type="text" class="form-input regex-expression" placeholder="如:\\d{4}[-/年](0?[1-9]|1[0-2])[-/月](0?[1-9]|[12][0-9]|3[01])[日]?">
</div>
<div class="flex flex-col justify-end pt-5">
<button class="text-red-500 hover:text-red-700 remove-regex-field-row">
<i class="ri-delete-bin-line"></i>
</button>
</div>
`;
// 添加到容器中
container.appendChild(newRow);
// 绑定删除按钮事件
const removeBtn = newRow.querySelector('.remove-regex-field-row');
if (removeBtn) {
removeBtn.addEventListener('click', function() {
newRow.remove();
});
}
// 自动聚焦到新添加的字段名称输入框
const fieldNameInput = newRow.querySelector('.regex-field-name');
if (fieldNameInput) {
fieldNameInput.focus();
}
}
}
// 初始化正则字段行按钮
function initRegexFieldRowButtons() {
document.querySelectorAll('.remove-regex-field-row').forEach(btn => {
btn.addEventListener('click', function() {
this.closest('.regex-field-row').remove();
});
});
}
// 通用函数设置字段管理器
function setupFieldsManager(inputId, btnId, containerId) {
const fieldInput = document.getElementById(inputId);
const addFieldBtn = document.getElementById(btnId);
const fieldsContainer = document.getElementById(containerId);
const fieldTypeSelect = inputId === 'field-input' ? document.getElementById('field-type') : null; // 仅多模态抽取有类型
if (addFieldBtn) {
addFieldBtn.addEventListener('click', function() {
if (fieldInput.value.trim()) {
if (fieldTypeSelect) {
// 多模态抽取,带类型
const fieldValue = fieldInput.value.trim();
const fieldType = fieldTypeSelect.value;
const fieldTypeName = fieldTypeSelect.options[fieldTypeSelect.selectedIndex].text;
// 创建字段标签
const chip = document.createElement('div');
chip.className = 'chip';
// 设置不同类型的徽章颜色
let badgeClass = 'bg-blue-100 text-blue-800';
switch(fieldType) {
case 'seal': badgeClass = 'bg-red-100 text-red-800'; break;
case 'cross-seal': badgeClass = 'bg-red-100 text-red-800'; break;
case 'handwriting': badgeClass = 'bg-yellow-100 text-yellow-800'; break;
case 'print': badgeClass = 'bg-purple-100 text-purple-800'; break;
case 'english': badgeClass = 'bg-indigo-100 text-indigo-800'; break;
case 'number': badgeClass = 'bg-gray-100 text-gray-800'; break;
case 'currency': badgeClass = 'bg-green-100 text-green-800'; break;
}
chip.innerHTML = `${fieldValue} <span class="badge ${badgeClass} text-xs ml-1" data-type="${fieldType}">${fieldTypeName}</span> <span class="close-btn">&times;</span>`;
chip.setAttribute('data-field-type', fieldType);
fieldsContainer.appendChild(chip);
// 绑定删除事件
chip.querySelector('.close-btn').addEventListener('click', function() {
chip.remove();
});
// 清空输入框,重置类型选择
fieldInput.value = '';
fieldTypeSelect.value = 'default';
} else {
// 标准字段管理,分割多个字段
const fieldValues = fieldInput.value.split(/[\s、,]+/).map(value => value.trim()).filter(value => value !== '');
// 添加每个字段
fieldValues.forEach(fieldValue => {
if (fieldValue) {
const chip = document.createElement('div');
chip.className = 'chip';
chip.innerHTML = `${fieldValue} <span class="close-btn">&times;</span>`;
fieldsContainer.appendChild(chip);
// 绑定删除事件
chip.querySelector('.close-btn').addEventListener('click', function() {
chip.remove();
});
}
});
fieldInput.value = '';
}
fieldInput.focus();
}
});
}
// 已有的字段绑定删除事件
if (fieldsContainer) {
fieldsContainer.querySelectorAll('.close-btn').forEach(btn => {
btn.addEventListener('click', function() {
this.closest('.chip').remove();
});
});
}
}
// 设置提示词模板选择器
function setupPromptTemplateSelector(selectId, configId) {
const select = document.getElementById(selectId);
const container = document.getElementById(configId);
const textarea = container.querySelector('textarea');
if (select && textarea) {
select.addEventListener('change', function() {
if (this.value && this.value !== 'custom') {
// 这里可以根据选择的模板设置不同的提示词
const templates = {
'template1': `从合同文本中抽取以下信息:
1. 合同编号
2. 合同金额(人民币)
3. 签订日期
4. 甲方名称
5. 乙方名称
仅返回JSON格式:
{
"合同编号": "",
"合同金额": "",
"签订日期": "",
"甲方名称": "",
"乙方名称": ""
}`,
'template2': `从合同中抽取付款条件相关信息:
1. 付款方式
2. 付款时间
3. 付款条件
4. 违约金比例
仅返回JSON格式`,
'template3': `从合同中抽取交付方式相关信息:
1. 交付时间
2. 交付地点
3. 交付方式
4. 验收标准
仅返回JSON格式`
};
textarea.value = templates[this.value] || '';
} else if (this.value === 'custom') {
// 选择自定义,保留现有内容或清空
// textarea.value = '';
}
});
}
}
// 评查规则类型切换
function setupRuleTypeSelect(select) {
select.addEventListener('change', function() {
const ruleIndex = this.getAttribute('data-index');
const configContainer = document.querySelector(`.rule-config-container[data-index="${ruleIndex}"]`);
const selectedType = this.value;
// 清空当前配置
configContainer.innerHTML = '';
if (!selectedType) {
configContainer.innerHTML = `
<div class="rule-placeholder text-center py-6 text-secondary">
<i class="ri-settings-3-line text-4xl mb-2 block"></i>
<p>请先选择评查类型</p>
</div>
`;
return;
}
// 更新规则项的类型标识
const ruleItem = this.closest('.rule-item');
const badge = ruleItem.querySelector('.badge');
// 添加评查类型标签
badge.classList.remove('bg-primary', 'rule-type-exists', 'rule-type-consistency', 'rule-type-format',
'rule-type-logic', 'rule-type-regex', 'rule-type-ai', 'rule-type-code');
switch(selectedType) {
case 'exists':
badge.classList.add('rule-type-exists');
break;
case 'consistency':
badge.classList.add('rule-type-consistency');
break;
case 'format':
badge.classList.add('rule-type-format');
break;
case 'logic':
badge.classList.add('rule-type-logic');
break;
case 'regex':
badge.classList.add('rule-type-regex');
break;
case 'ai':
badge.classList.add('rule-type-ai');
break;
case 'code':
badge.classList.add('rule-type-code');
break;
default:
badge.classList.add('bg-primary');
}
// 加载对应类型的配置
loadRuleConfig(selectedType, configContainer, ruleIndex);
});
}
// 加载评查规则配置
function loadRuleConfig(type, container, index) {
let configHTML = '';
switch(type) {
case 'exists':
configHTML = `
<div class="config-section">
<div class="mb-4">
<label class="form-label">可选字段 <span class="required-mark">*</span></label>
<div class="field-tags-container exists-fields-container" id="exists-fields-container-${index}">
<!-- 这些标签将从抽取设置中动态生成 -->
</div>
<div class="form-tip mt-2">点击选择需要判断是否存在的字段,已选中的字段会高亮显示</div>
</div>
<div class="mb-4">
<label class="form-label">判断逻辑 <span class="required-mark">*</span></label>
<div class="form-radio-group">
<label class="form-radio-item">
<input type="radio" name="existsLogic_${index}" class="form-radio" value="all" checked>
<span>所有字段必须存在</span>
</label>
<label class="form-radio-item">
<input type="radio" name="existsLogic_${index}" class="form-radio" value="any">
<span>任一字段存在即可</span>
</label>
</div>
</div>
</div>
`;
break;
case 'consistency':
configHTML = `
<div class="config-section">
<div class="mb-4">
<label class="form-label">比较字段配置 <span class="required-mark">*</span></label>
<div id="consistency-fields-container-${index}">
<div class="bg-gray-50 p-4 rounded-md mb-2">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
<div>
<label class="form-label text-sm">源字段</label>
<select class="form-select">
<option value="">请选择源字段</option>
<option value="甲方名称">甲方名称</option>
<option value="合同金额大写">合同金额大写</option>
</select>
</div>
<div>
<label class="form-label text-sm">目标字段</label>
<select class="form-select">
<option value="">请选择目标字段</option>
<option value="甲方签章">甲方签章</option>
<option value="合同金额小写">合同金额小写</option>
</select>
</div>
<div>
<label class="form-label text-sm">比较方式 <span class="required-mark">*</span></label>
<select class="form-select">
<option value="">请选择比较方式</option>
<option value="exact">精确匹配</option>
<option value="contains">包含关系</option>
<option value="semantic">大模型语义匹配</option>
</select>
</div>
</div>
<div class="flex justify-end">
<button class="text-red-500 hover:text-red-700 consistency-remove-btn">
<i class="ri-delete-bin-line"></i> 删除
</button>
</div>
</div>
</div>
<div>
<button class="ant-btn ant-btn-default add-consistency-pair-btn" data-index="${index}">
<i class="ri-add-line"></i> 添加比较对
</button>
</div>
</div>
<div class="mb-4">
<label class="form-label">逻辑关系 <span class="required-mark">*</span></label>
<div class="form-radio-group">
<label class="form-radio-item">
<input type="radio" name="logicRelation_${index}" class="form-radio" value="and" checked>
<span>AND(所有条件都满足)</span>
</label>
<label class="form-radio-item">
<input type="radio" name="logicRelation_${index}" class="form-radio" value="or">
<span>OR(任一条件满足)</span>
</label>
</div>
</div>
</div>
`;
break;
case 'format':
configHTML = `
<div class="config-section">
<div class="mb-4">
<label class="form-label">判断字段 <span class="required-mark">*</span></label>
<select class="form-select">
<option value="">请选择判断字段</option>
<option value="签订日期">签订日期</option>
<option value="合同编号">合同编号</option>
<option value="联系电话">联系电话</option>
</select>
</div>
<div class="mb-4">
<label class="form-label">格式类型 <span class="required-mark">*</span></label>
<select class="form-select format-type" data-index="${index}">
<option value="">请选择格式类型</option>
<option value="date">日期格式</option>
<option value="number">数字格式</option>
<option value="phone">电话号码</option>
<option value="email">电子邮箱</option>
<option value="bankcard">银行卡号</option>
<option value="idcard">身份证号码</option>
<option value="zipcode">邮政编码</option>
<option value="uscc">统一社会信用代码</option>
</select>
</div>
<div class="mb-4">
<label class="form-label">参数设置</label>
<input type="text" class="form-input" placeholder="请输入参数设置">
</div>
</div>
`;
break;
case 'logic':
configHTML = `
<div class="config-section">
<div class="mb-4">
<label class="form-label">条件设置 <span class="required-mark">*</span></label>
<div class="conditions-container" id="conditions-container-${index}">
<div class="condition-row bg-gray-50 p-4 rounded-md mb-2">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-2">
<div>
<label class="form-label text-sm">字段</label>
<select class="form-select">
<option value="">请选择字段</option>
<option value="合同金额">合同金额</option>
<option value="签订日期">签订日期</option>
<option value="合同期限">合同期限</option>
</select>
</div>
<div>
<label class="form-label text-sm">运算符</label>
<select class="form-select">
<option value="eq">等于 (=)</option>
<option value="neq"> (≠)</option>
<option value="gt">大于 (>)</option>
<option value="gte">大于等于 (≥)</option>
<option value="lt">小于 (<)</option>
<option value="lte">小于等于 (≤)</option>
<option value="contains">包含</option>
<option value="not_contains">不包含</option>
</select>
</div>
<div>
<label class="form-label text-sm">值</label>
<input type="text" class="form-input" placeholder="请输入比较值">
</div>
</div>
<div class="flex justify-end">
<button class="text-red-500 hover:text-red-700 remove-condition-btn">
<i class="ri-delete-bin-line"></i> 删除
</button>
</div>
</div>
</div>
<div class="mt-2">
<button class="ant-btn ant-btn-default add-condition-btn" data-index="${index}">
<i class="ri-add-line"></i> 添加条件
</button>
</div>
</div>
<div class="mb-4">
<label class="form-label">逻辑关系 <span class="required-mark">*</span></label>
<div class="form-radio-group">
<label class="form-radio-item">
<input type="radio" name="logicRelation_${index}" class="form-radio" value="and" checked>
<span>AND(所有条件都满足)</span>
</label>
<label class="form-radio-item">
<input type="radio" name="logicRelation_${index}" class="form-radio" value="or">
<span>OR(任一条件满足)</span>
</label>
</div>
</div>
</div>
`;
break;
case 'regex':
configHTML = `
<div class="config-section">
<div class="mb-4">
<label class="form-label">检查字段 <span class="required-mark">*</span></label>
<select class="form-select">
<option value="">请选择检查字段</option>
<option value="合同编号">合同编号</option>
<option value="甲方名称">甲方名称</option>
<option value="合同正文">合同正文</option>
</select>
</div>
<div class="mb-4">
<label class="form-label">正则表达式 <span class="required-mark">*</span></label>
<textarea class="form-textarea" placeholder="请输入正则表达式"></textarea>
</div>
<div class="mb-4">
<label class="form-label">匹配类型 <span class="required-mark">*</span></label>
<div class="form-radio-group">
<label class="form-radio-item">
<input type="radio" name="regexMatchType_${index}" class="form-radio" value="match" checked>
<span>必须匹配(符合为通过)</span>
</label>
<label class="form-radio-item">
<input type="radio" name="regexMatchType_${index}" class="form-radio" value="not_match">
<span>不得匹配(不符合为通过)</span>
</label>
</div>
</div>
</div>
`;
break;
case 'ai':
configHTML = `
<div class="config-section">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="form-label">
模型选择 <span class="required-mark">*</span>
</label>
<select class="form-select">
<option value="deepseek">DeepSeek</option>
<option value="qwen72b">Qwen72B-VL</option>
<option value="qwen14b">Qwen14B</option>
</select>
</div>
<div>
<label class="form-label">温度参数</label>
<input type="number" class="form-input" placeholder="0.1" value="0.1" min="0" max="1" step="0.1">
</div>
<div class="col-span-1 md:col-span-2">
<label class="form-label">大模型 Prompt <span class="required-mark">*</span></label>
<textarea class="form-textarea ai-prompt-textarea" placeholder="请输入提示词,引导模型进行判断">请判断以下{字段}内容是否符合规范要求,仅回答"符合"或"不符合",并简要说明理由。
{字段内容}</textarea>
</div>
<!-- Tag Buttons Section -->
<div class="col-span-1 md:col-span-2 flex flex-wrap gap-2 mt-2">
<button class="ant-btn ant-btn-default tag-button" data-tag="合同编号">合同编号</button>
<button class="ant-btn ant-btn-default tag-button" data-tag="合同金额">合同金额</button>
<button class="ant-btn ant-btn-default tag-button" data-tag="签订日期">签订日期</button>
<button class="ant-btn ant-btn-default tag-button" data-tag="甲方名称">甲方名称</button>
<button class="ant-btn ant-btn-default tag-button" data-tag="乙方名称">乙方名称</button>
</div>
</div>
</div>
`;
break;
case 'code':
configHTML = `
<div class="config-section">
<div class="mb-4">
<label class="form-label">代码语言 <span class="required-mark">*</span></label>
<div class="form-radio-group">
<label class="form-radio-item">
<input type="radio" name="codeLanguage_${index}" class="form-radio code-language-radio" value="javascript" checked>
<span>JavaScript</span>
</label>
<label class="form-radio-item">
<input type="radio" name="codeLanguage_${index}" class="form-radio code-language-radio" value="python">
<span>Python</span>
</label>
</div>
</div>
<div class="mb-4">
<label class="form-label">自定义代码 <span class="required-mark">*</span></label>
<div class="code-editor-wrapper">
<div class="code-editor-header">
<div class="code-editor-filename" id="filename-${index}">script.js</div>
<div class="code-editor-actions">
<i class="ri-file-copy-line code-copy-btn" title="复制代码" data-index="${index}"></i>
</div>
</div>
<div class="code-editor-container">
<textarea class="code-editor-textarea" id="code-editor-${index}" placeholder="请输入自定义代码">// 示例代码
function checkRule(data) {
// data 包含抽取的字段
try {
// 在此编写检查逻辑
if (data.fieldName && condition) {
return {
pass: true,
message: "检查通过"
};
} else {
return {
pass: false,
message: "检查不通过,原因:..."
};
}
} catch (error) {
return {
pass: false,
message: "执行出错:" + error.message
};
}
}</textarea>
<div class="hljs-container">
<pre><code class="language-javascript" id="highlighted-code-${index}"></code></pre>
</div>
</div>
</div>
</div>
</div>
`;
break;
}
container.innerHTML = configHTML;
// 为新加载的配置添加事件
setupRuleConfigEvents(container, index, type);
}
// 为规则配置添加事件
function setupRuleConfigEvents(container, index, type) {
switch(type) {
case 'exists':
// 从当前活跃的抽取设置中获取字段
const activeExtractConfig = document.querySelector('.extraction-config:not(.hidden)');
const fieldsContainer = activeExtractConfig.querySelector('.chips-container');
const existsFieldsContainer = container.querySelector('.exists-fields-container');
// 清空现有字段
existsFieldsContainer.innerHTML = '';
// 获取抽取字段并创建可选标签
if (fieldsContainer) {
const chips = fieldsContainer.querySelectorAll('.chip');
chips.forEach(chip => {
const fieldName = chip.textContent.replace('×', '').trim();
const fieldTag = document.createElement('div');
fieldTag.className = 'field-tag';
fieldTag.setAttribute('data-field', fieldName);
fieldTag.textContent = fieldName;
// 添加点击事件
fieldTag.addEventListener('click', function() {
this.classList.toggle('selected');
});
existsFieldsContainer.appendChild(fieldTag);
});
}
// 如果没有找到字段,添加一些默认字段作为示例
if (existsFieldsContainer.children.length === 0) {
const defaultFields = ['合同编号', '甲方名称', '乙方名称', '签订日期', '合同金额'];
defaultFields.forEach(fieldName => {
const fieldTag = document.createElement('div');
fieldTag.className = 'field-tag';
fieldTag.setAttribute('data-field', fieldName);
fieldTag.textContent = fieldName;
// 添加点击事件
fieldTag.addEventListener('click', function() {
this.classList.toggle('selected');
});
existsFieldsContainer.appendChild(fieldTag);
});
}
break;
case 'consistency':
// 一致性判断配置事件
setupConsistencyEvents(container, index);
break;
case 'logic':
// 处理逻辑判断的添加条件和删除功能
const addConditionBtn = container.querySelector('.add-condition-btn');
if (addConditionBtn) {
addConditionBtn.addEventListener('click', function() {
// console.log('添加条件按钮被点击');
const conditionsContainer = container.querySelector('.conditions-container');
if (!conditionsContainer) {
console.error('找不到条件容器');
return;
}
const conditionRow = conditionsContainer.querySelector('.condition-row');
if (!conditionRow) {
console.error('找不到条件行模板');
return;
}
const newCondition = conditionRow.cloneNode(true);
// 清空选中值
newCondition.querySelectorAll('select, input').forEach(el => {
if (el.tagName === 'SELECT') {
el.value = el.querySelector('option').value;
} else {
el.value = '';
}
});
// 确保删除按钮可见
const removeBtn = newCondition.querySelector('.remove-condition-btn');
if (removeBtn) {
removeBtn.style.display = 'block';
// 绑定删除事件
removeBtn.addEventListener('click', function() {
if (conditionsContainer.querySelectorAll('.condition-row').length > 1) {
this.closest('.condition-row').remove();
}
});
}
conditionsContainer.appendChild(newCondition);
// 显示所有条件行的删除按钮
const allRemoveBtns = conditionsContainer.querySelectorAll('.remove-condition-btn');
if (allRemoveBtns.length > 1) {
allRemoveBtns.forEach(btn => {
btn.style.display = 'block';
});
}
// console.log('已添加新的条件行');
});
}
// 为已有的删除按钮绑定事件
const removeButtons = container.querySelectorAll('.remove-condition-btn');
removeButtons.forEach(btn => {
btn.addEventListener('click', function() {
const conditionsContainer = this.closest('.conditions-container');
if (conditionsContainer.querySelectorAll('.condition-row').length > 1) {
this.closest('.condition-row').remove();
} else {
// 如果只有一个条件,隐藏删除按钮
this.style.display = 'none';
}
});
});
// 隐藏第一个删除按钮(如果只有一个条件)
const firstRemoveBtn = container.querySelector('.remove-condition-btn');
const conditionsContainer = container.querySelector('.conditions-container');
if (firstRemoveBtn && conditionsContainer && conditionsContainer.querySelectorAll('.condition-row').length === 1) {
firstRemoveBtn.style.display = 'none';
}
break;
case 'format':
// 格式类型切换
const formatTypeSelect = container.querySelector('.format-type');
if (formatTypeSelect) {
formatTypeSelect.addEventListener('change', function() {
// 已删除日期格式和自定义格式相关的配置显示/隐藏逻辑
});
}
break;
case 'ai':
// AI判断目标切换
const aiJudgeTargetRadios = container.querySelectorAll('.ai-judge-target');
aiJudgeTargetRadios.forEach(radio => {
radio.addEventListener('change', function() {
const targetIndex = this.getAttribute('data-target-index');
const fieldSelect = document.getElementById(`ai-field-select-${targetIndex}`);
if (this.value === 'field') {
fieldSelect.classList.remove('hidden');
} else {
fieldSelect.classList.add('hidden');
}
});
});
break;
case 'code':
// 处理代码编辑器相关事件
const codeEditor = container.querySelector('.code-editor-textarea');
const highlightedCode = container.querySelector('.hljs-container code');
const languageRadios = container.querySelectorAll('.code-language-radio');
const filenameDisplay = container.querySelector('.code-editor-filename');
const copyBtn = container.querySelector('.code-copy-btn');
// 初始化代码高亮
if (codeEditor && highlightedCode) {
// 确保立即执行初始高亮
setTimeout(() => {
highlightedCode.textContent = codeEditor.value;
try {
hljs.highlightElement(highlightedCode);
// console.log('代码块已高亮', index);
} catch (e) {
console.error('代码高亮失败:', e);
}
}, 0);
// 实时更新高亮
codeEditor.addEventListener('input', function() {
highlightedCode.textContent = this.value;
try {
hljs.highlightElement(highlightedCode);
} catch (e) {
console.error('更新高亮失败:', e);
}
});
// 处理Tab键
codeEditor.addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
e.preventDefault();
// 获取光标位置
const start = this.selectionStart;
const end = this.selectionEnd;
// 在光标位置插入Tab
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
// 将光标移动到Tab之后
this.selectionStart = this.selectionEnd = start + 4;
// 更新高亮代码
highlightedCode.textContent = this.value;
hljs.highlightElement(highlightedCode);
}
});
}
// 语言切换
if (languageRadios && highlightedCode && filenameDisplay && codeEditor) {
languageRadios.forEach(radio => {
radio.addEventListener('change', function() {
if (this.checked) {
// 更改文件名显示
if (this.value === 'javascript') {
filenameDisplay.textContent = 'script.js';
highlightedCode.className = 'language-javascript';
// JavaScript 示例代码
codeEditor.value = `// 示例代码
function checkRule(data) {
// data 包含抽取的字段
try {
// 在此编写检查逻辑
if (data.fieldName && condition) {
return {
pass: true,
message: "检查通过"
};
} else {
return {
pass: false,
message: "检查不通过,原因:..."
};
}
} catch (error) {
return {
pass: false,
message: "执行出错:" + error.message
};
}
}`;
} else if (this.value === 'python') {
filenameDisplay.textContent = 'script.py';
highlightedCode.className = 'language-python';
// Python 示例代码
codeEditor.value = `# 示例代码
def check_rule(data):
# data 包含抽取的字段
try:
# 在此编写检查逻辑
if data.get('field_name') and condition:
return {
'pass': True,
'message': "检查通过"
}
else:
return {
'pass': False,
'message': "检查不通过,原因:..."
}
except Exception as error:
return {
'pass': False,
'message': f"执行出错:{str(error)}"
}`;
}
// 重新高亮
highlightedCode.textContent = codeEditor.value;
try {
hljs.highlightElement(highlightedCode);
} catch (e) {
console.error('切换语言后高亮失败:', e);
}
}
});
});
}
// 复制代码功能
if (copyBtn && codeEditor) {
copyBtn.addEventListener('click', function() {
// 获取代码内容
const code = codeEditor.value;
// 复制到剪贴板
navigator.clipboard.writeText(code).then(() => {
// 显示复制成功提示
let notification = document.querySelector('.code-copy-success');
if (!notification) {
notification = document.createElement('div');
notification.className = 'code-copy-success';
notification.textContent = '代码已复制到剪贴板';
document.body.appendChild(notification);
}
// 显示提示
setTimeout(() => {
notification.classList.add('show');
// 3秒后隐藏提示
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}, 10);
}).catch(err => {
console.error('复制失败:', err);
});
});
}
break;
}
}
// 添加评查规则
function setupAddRuleBtn() {
const addRuleBtn = document.getElementById('add-rule-btn');
const ruleItemsContainer = document.getElementById('rule-items-container');
const ruleCountElement = document.getElementById('logic-count');
if (addRuleBtn && ruleItemsContainer) {
addRuleBtn.addEventListener('click', function() {
const ruleItems = ruleItemsContainer.querySelectorAll('.rule-item');
const newIndex = ruleItems.length + 1;
// 创建一个新规则区域
const newRuleHTML = `
<div class="rule-item border border-dashed border-gray-300 rounded-md p-6 mb-6 relative">
<div class="absolute top-3 right-3 flex gap-2">
<span class="badge rounded-pill bg-primary text-white px-2 py-1">规则 #${newIndex}</span>
<button class="text-red-500 hover:text-red-700 delete-rule-btn">
<i class="ri-delete-bin-line"></i>
</button>
</div>
<div class="mb-4">
<label class="form-label">
评查类型 <span class="required-mark">*</span>
</label>
<select class="form-select rule-type-select" data-index="${newIndex}">
<option value="">请选择评查类型</option>
<option value="exists">有无判断</option>
<option value="consistency">一致性判断</option>
<option value="format">格式判断</option>
<option value="logic">逻辑判断</option>
<option value="regex">正则表达式</option>
<option value="ai">大模型判断</option>
<option value="code">自定义代码</option>
</select>
<div class="form-tip">选择评查类型后将显示对应的配置项</div>
</div>
<div class="rule-config-container" data-index="${newIndex}">
<div class="rule-placeholder text-center py-6 text-secondary">
<i class="ri-settings-3-line text-4xl mb-2 block"></i>
<p>请先选择评查类型</p>
</div>
</div>
</div>
`;
// 添加新规则到容器
ruleItemsContainer.insertAdjacentHTML('beforeend', newRuleHTML);
// 获取新添加的规则项
const newRuleElement = ruleItemsContainer.lastElementChild;
// 绑定删除按钮事件
const deleteBtn = newRuleElement.querySelector('.delete-rule-btn');
deleteBtn.addEventListener('click', function() {
newRuleElement.remove();
// 重新编号
reindexRules();
});
// 绑定类型选择事件
const typeSelect = newRuleElement.querySelector('.rule-type-select');
setupRuleTypeSelect(typeSelect);
// 更新规则计数
updateRuleCount();
});
}
}
// 重新编号评查规则
function reindexRules() {
const ruleItems = document.querySelectorAll('.rule-item');
ruleItems.forEach((item, index) => {
const newIndex = index + 1;
// 更新编号
const badge = item.querySelector('.badge');
if (badge) {
badge.textContent = `规则 #${newIndex}`;
}
// 更新数据索引
const typeSelect = item.querySelector('.rule-type-select');
typeSelect.setAttribute('data-index', newIndex);
const configContainer = item.querySelector('.rule-config-container');
configContainer.setAttribute('data-index', newIndex);
// 更新删除按钮状态
const deleteBtn = item.querySelector('.delete-rule-btn');
if (deleteBtn) {
if (newIndex === 1) {
deleteBtn.disabled = true;
deleteBtn.classList.add('text-gray-400');
deleteBtn.classList.remove('text-red-500');
} else {
deleteBtn.disabled = false;
deleteBtn.classList.remove('text-gray-400');
deleteBtn.classList.add('text-red-500');
}
}
});
// 更新规则计数
updateRuleCount();
}
// 更新规则计数
function updateRuleCount() {
const ruleCount = document.getElementById('logic-count');
const count = document.querySelectorAll('.rule-item').length;
if (ruleCount) {
ruleCount.textContent = `${count}个规则`;
}
}
// 自定义组合逻辑切换
const ruleLogicRadios = document.querySelectorAll('input[name="ruleLogic"]');
const ruleCustomLogic = document.getElementById('rule-custom-logic');
ruleLogicRadios.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'custom') {
ruleCustomLogic.classList.remove('hidden');
} else {
ruleCustomLogic.classList.add('hidden');
}
});
});
// 为已存在的第一个规则设置事件
const firstRuleTypeSelect = document.querySelector('.rule-type-select');
if (firstRuleTypeSelect) {
setupRuleTypeSelect(firstRuleTypeSelect);
}
// 为删除按钮添加事件
document.querySelectorAll('.delete-rule-btn').forEach(btn => {
btn.addEventListener('click', function() {
const ruleItem = this.closest('.rule-item');
const ruleItems = document.querySelectorAll('.rule-item');
// 只有当不是第一个规则时才可以删除
if (ruleItems.length > 1 && !Array.from(ruleItems).indexOf(ruleItem) === 0) {
ruleItem.remove();
reindexRules();
}
});
});
// 初始化添加规则按钮
setupAddRuleBtn();
// 初始化规则计数
updateRuleCount();
// 一致性判断配置 - 添加比较对
function setupConsistencyEvents(container, index) {
const addPairBtn = container.querySelector('.add-consistency-pair-btn');
if (addPairBtn) {
addPairBtn.addEventListener('click', function() {
const consistencyFieldsContainer = document.getElementById(`consistency-fields-container-${index}`);
const firstPair = consistencyFieldsContainer.querySelector('.bg-gray-50').cloneNode(true);
// 重置选择项
firstPair.querySelectorAll('select').forEach(select => {
select.value = '';
});
// 显示移除按钮
const removeBtn = firstPair.querySelector('.consistency-remove-btn');
removeBtn.style.display = 'block';
// 绑定删除事件
removeBtn.addEventListener('click', function() {
firstPair.remove();
// 如果只剩一个对比对,隐藏其移除按钮
const remainingPairs = consistencyFieldsContainer.querySelectorAll('.bg-gray-50');
if (remainingPairs.length === 1) {
remainingPairs[0].querySelector('.consistency-remove-btn').style.display = 'none';
}
});
consistencyFieldsContainer.appendChild(firstPair);
// 显示所有对比对的移除按钮
const allPairs = consistencyFieldsContainer.querySelectorAll('.bg-gray-50');
if (allPairs.length > 1) {
allPairs.forEach(pair => {
pair.querySelector('.consistency-remove-btn').style.display = 'block';
});
}
});
}
// 初始移除按钮事件
container.querySelectorAll('.consistency-remove-btn').forEach(btn => {
btn.addEventListener('click', function() {
const pair = this.closest('.bg-gray-50');
const consistencyFieldsContainer = pair.parentElement;
if (consistencyFieldsContainer.querySelectorAll('.bg-gray-50').length > 1) {
pair.remove();
// 如果只剩一个对比对,隐藏其移除按钮
const remainingPairs = consistencyFieldsContainer.querySelectorAll('.bg-gray-50');
if (remainingPairs.length === 1) {
remainingPairs[0].querySelector('.consistency-remove-btn').style.display = 'none';
}
}
});
});
// 初始状态下,如果只有一个比较对,隐藏其删除按钮
const initialPairs = container.querySelectorAll('.bg-gray-50');
if (initialPairs.length === 1) {
initialPairs[0].querySelector('.consistency-remove-btn').style.display = 'none';
}
}
// 为字段标签添加点击事件
document.querySelectorAll('.field-tag').forEach(tag => {
tag.addEventListener('click', function() {
this.classList.toggle('selected'); // 切换选中状态
});
});
// Tag button functionality
const tagButtons = container.querySelectorAll('.tag-button');
const promptTextarea = container.querySelector('.ai-prompt-textarea');
tagButtons.forEach(button => {
button.addEventListener('click', function() {
const tagText = this.getAttribute('data-tag');
if (promptTextarea) {
promptTextarea.value += `{${tagText}}`;
promptTextarea.focus();
}
});
});
// 不通过提示类别选中效果
const severityOptions = document.querySelectorAll('input[name="errorSeverity"]');
const severityDivs = document.querySelectorAll('.severity-option');
severityOptions.forEach((radio, index) => {
radio.addEventListener('change', function() {
// 移除所有选中效果
severityDivs.forEach(div => {
div.classList.remove('severity-selected');
div.classList.remove('bg-blue-300', 'bg-yellow-300', 'bg-red-300', 'bg-purple-300');
});
// 添加当前选中效果
if (this.checked) {
severityDivs[index].classList.add('severity-selected');
// 根据类型添加更明显的背景色
switch(this.value) {
case 'info':
severityDivs[index].classList.add('bg-blue-300');
break;
case 'warning':
severityDivs[index].classList.add('bg-yellow-300');
break;
case 'error':
severityDivs[index].classList.add('bg-red-300');
break;
case 'critical':
severityDivs[index].classList.add('bg-purple-300');
break;
}
}
});
});
// 大模型抽取 - 提示词类型切换
document.querySelectorAll('input[name="llm-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'system') {
document.getElementById('llm-custom-prompt-container').style.display = 'none';
document.getElementById('llm-system-prompt-info').style.display = 'block';
} else {
document.getElementById('llm-custom-prompt-container').style.display = 'block';
document.getElementById('llm-system-prompt-info').style.display = 'none';
}
});
});
// 多模态抽取 - 提示词类型切换
document.querySelectorAll('input[name="multimodal-prompt-type"]').forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'system') {
document.getElementById('multimodal-custom-prompt-container').style.display = 'none';
document.getElementById('multimodal-system-prompt-info').style.display = 'block';
} else {
document.getElementById('multimodal-custom-prompt-container').style.display = 'block';
document.getElementById('multimodal-system-prompt-info').style.display = 'none';
}
});
});
// 大模型抽取 - 提示词模板选择
document.getElementById('llm-prompt-template').addEventListener('change', function() {
const promptContent = document.getElementById('llm-prompt-content');
if (this.value !== '') {
// 获取模板内容
const templateData = getPromptTemplateById(this.value);
if (templateData) {
// 填充提示词内容
promptContent.value = templateData.template_content;
// 自动填充字段列表
const fieldsContainer = document.getElementById('fields-container-ocr');
if (fieldsContainer) {
const fields = Array.from(fieldsContainer.querySelectorAll('.chip')).map(
chip => chip.textContent.replace('×', '').trim()
);
if (fields.length > 0) {
// 替换{fieldsList}变量
const fieldListStr = fields.map((field, idx) => `${idx+1}. ${field}`).join('\n');
promptContent.value = promptContent.value.replace('{fieldsList}', fieldListStr);
}
}
// 允许编辑
promptContent.removeAttribute('readonly');
}
} else {
promptContent.value = '';
promptContent.setAttribute('readonly', 'readonly');
}
});
// 多模态抽取 - 提示词模板选择
document.getElementById('multimodal-prompt-template').addEventListener('change', function() {
const promptContent = document.getElementById('multimodal-prompt-content');
if (this.value !== '') {
// 获取模板内容
const templateData = getPromptTemplateById(this.value);
if (templateData) {
// 填充提示词内容
promptContent.value = templateData.template_content;
// 自动填充字段列表
const fieldsContainer = document.getElementById('fields-container');
if (fieldsContainer) {
const fields = Array.from(fieldsContainer.querySelectorAll('.chip')).map(chip => {
const fieldName = chip.textContent.split('×')[0].trim();
const fieldType = chip.querySelector('.badge')?.textContent.trim() || '默认';
return `${fieldName} (${fieldType})`;
});
if (fields.length > 0) {
// 替换{fieldsList}变量
const fieldListStr = fields.map((field, idx) => `${idx+1}. ${field}`).join('\n');
promptContent.value = promptContent.value.replace('{fieldsList}', fieldListStr);
}
}
// 允许编辑
promptContent.removeAttribute('readonly');
}
} else {
promptContent.value = '';
promptContent.setAttribute('readonly', 'readonly');
}
});
// 模拟获取提示词模板数据
function getPromptTemplateById(id) {
// 模拟的模板数据,实际应用中应从服务器获取
const templates = {
'1': {
id: 1,
template_name: '行政处罚-抽取通用模板',
template_type: 'Extraction',
template_content: `你是一个专业的文档信息抽取助手。请从以下{docType}文档中抽取关键信息:
{fieldsList}
请将结果以JSON格式输出,包含以上字段。如果某个字段在文档中未找到,则该字段的值设为null。`
},
'4': {
id: 4,
template_name: '采购合同-乙方资质抽取',
template_type: 'Extraction',
template_content: `你是一个专业的合同信息抽取助手。请从以下{docType}中抽取乙方的资质信息:
需要抽取的信息包括:
{fieldsList}
{companyName}要求所有供应商必须提供完整的资质信息。请将结果以JSON格式输出,包含以上字段。`
},
'5': {
id: 5,
template_name: '合同-关键条款抽取',
template_type: 'Extraction',
template_content: `请作为{industry}行业的专业合同审核员,从提供的{docType}中提取以下关键条款信息:
{fieldsList}
文档ID: {documentId}
审核日期: {date}
请以JSON格式输出结果,对于未明确指定的条款需标记为"未明确约定"。`
},
'6': {
id: 6,
template_name: '烟草许可证-信息抽取',
template_type: 'Extraction',
template_content: `请从下列烟草专卖许可证文件中抽取以下关键信息:
{fieldsList}
这些信息将用于{companyName}内部数据库更新。请确保许可证编号和有效期格式准确无误。`
},
'7': {
id: 7,
template_name: '多模态-印章识别模板',
template_type: 'Multimodal',
template_content: `请识别并提取文档中的所有印章信息,包括:
{fieldsList}
文档类型: {docType}
页面范围: {pageRange}
请注意区分公章、法人章和合同专用章,并分析印章的清晰度和完整性。`
},
'8': {
id: 8,
template_name: '多模态-表格抽取模板',
template_type: 'Multimodal',
template_content: `请从文档中的表格提取以下信息:
{fieldsList}
文档类型: {docType}
表格可能跨页,请确保完整提取所有内容。表格中的数值需保留原始精度。`
},
'9': {
id: 9,
template_name: '多模态-手写内容识别模板',
template_type: 'Multimodal',
template_content: `请识别文档中的手写内容,特别关注:
{fieldsList}
文档类型: {docType}
内容类型: {contentType}
对于难以辨认的手写内容,请标注为"[难以辨认]"并尽可能给出可能的解读。`
}
};
return templates[id] || null;
}
});
</script>
<!-- 添加JavaScript实现展开/收起功能和预览更新 -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// 展开/收起功能
const descriptionToggle = document.getElementById('description-toggle');
const descriptionSection = document.getElementById('description-section');
descriptionToggle.addEventListener('click', function() {
this.classList.toggle('expanded');
if (descriptionSection.classList.contains('hidden')) {
descriptionSection.classList.remove('hidden');
} else {
descriptionSection.classList.add('hidden');
}
});
// 法律条文预览更新
const lawName = document.getElementById('law-name');
const lawArticles = document.getElementById('law-articles');
const lawContent = document.getElementById('law-content');
const previewLawName = document.getElementById('preview-law-name');
const previewLawArticles = document.getElementById('preview-law-articles');
const previewLawContent = document.getElementById('preview-law-content');
// 更新法典名称预览
lawName.addEventListener('input', function() {
previewLawName.textContent = this.value || '《中华人民共和国民法典》';
});
// 更新条款号预览
lawArticles.addEventListener('input', function() {
const articlesArray = this.value.split(',').map(item => item.trim()).filter(item => item);
previewLawArticles.innerHTML = '';
if (articlesArray.length > 0) {
articlesArray.forEach(article => {
const span = document.createElement('span');
span.className = 'law-article';
span.textContent = article;
previewLawArticles.appendChild(span);
});
} else {
previewLawArticles.innerHTML = '<span class="law-article">第五百八十五条</span><span class="law-article">第五百八十六条</span>';
}
});
// 更新条款内容预览
lawContent.addEventListener('input', function() {
previewLawContent.textContent = this.value || '当事人应当按照约定全面履行自己的义务。';
});
});
</script>
</body>
</html>