16 changed files with 2923 additions and 227 deletions
@ -0,0 +1,195 @@ |
|||
{ |
|||
"contractSubstantiveReview": { |
|||
"taskType": "contractSubstantiveReview", |
|||
"name": "实质性审查", |
|||
"mode": "tabs", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "contract", "title": "合同文件", "apiField": "id" } |
|||
] |
|||
}, |
|||
"tabs": [ |
|||
{ |
|||
"key": "substantive", |
|||
"label": "实质性审查", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "contract", "title": "合同文件", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "contract", |
|||
"required": true |
|||
}, |
|||
{ |
|||
"field": "modifiedContent", |
|||
"title": "修改建议", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"showComparison": true, |
|||
"comparisonConfig": { |
|||
"comparisonField": "modificationDisplay", |
|||
"comparisonTitle": "修改情况展示", |
|||
"showButton": true |
|||
} |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "审查依据", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_points", "review_content"], |
|||
"separator": ":" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"contractComplianceReview": { |
|||
"taskType": "contractComplianceReview", |
|||
"name": "合规性审查", |
|||
"mode": "tabs", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "contract", "title": "合同文件", "apiField": "id" } |
|||
] |
|||
}, |
|||
"tabs": [ |
|||
{ |
|||
"key": "compliance", |
|||
"label": "合规性审查", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "contract", "title": "合同文件", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "contract", |
|||
"required": true |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "法规依据", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_points"], |
|||
"separator": ":", |
|||
"fieldProcessors": { |
|||
"review_points": "arrayJoin" |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"contractConsistencyReview": { |
|||
"taskType": "contractConsistencyReview", |
|||
"name": "一致性审查", |
|||
"mode": "tabs", |
|||
"pdfConfig": { |
|||
"layout": "split", |
|||
"sources": [ |
|||
{ "id": "contract", "title": "合同文件", "apiField": "id" }, |
|||
{ "id": "bid", "title": "招标文件", "apiField": "id" } |
|||
] |
|||
}, |
|||
"tabs": [ |
|||
{ |
|||
"key": "consistency", |
|||
"label": "一致性审查", |
|||
"pdfConfig": { |
|||
"layout": "split", |
|||
"sources": [ |
|||
{ "id": "contract", "title": "合同文件", "apiField": "id" }, |
|||
{ "id": "bid", "title": "招标文件", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "合同原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "contract", |
|||
"required": true |
|||
}, |
|||
{ |
|||
"field": "comparedText", |
|||
"title": "招标文件对应内容", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "bid", |
|||
"required": true |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"contractReview": { |
|||
"taskType": "contractReview", |
|||
"name": "合同审查", |
|||
"mode": "single", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "contract", "title": "合同文件", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "contract", |
|||
"required": true |
|||
}, |
|||
{ |
|||
"field": "existingIssues", |
|||
"title": "存在问题", |
|||
"dataType": "string", |
|||
"displayType": "markdown" |
|||
}, |
|||
{ |
|||
"field": "modifiedContent", |
|||
"title": "修改建议", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"showComparison": true, |
|||
"comparisonConfig": { |
|||
"comparisonField": "modificationDisplay", |
|||
"comparisonTitle": "修改情况展示", |
|||
"showButton": true |
|||
} |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "审查依据", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_points", "review_content"], |
|||
"separator": ":" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,250 @@ |
|||
{ |
|||
"checkDocumentError": { |
|||
"taskType": "checkDocumentError", |
|||
"name": "文字错误检查", |
|||
"mode": "single", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "document", "title": "文档", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document", |
|||
"required": true |
|||
}, |
|||
{ |
|||
"field": "modifiedContent", |
|||
"title": "修改建议", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"showComparison": true, |
|||
"comparisonConfig": { |
|||
"comparisonField": "modificationDisplay", |
|||
"comparisonTitle": "修改情况展示", |
|||
"showButton": true |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
"checkRepeatText": { |
|||
"taskType": "checkRepeatText", |
|||
"name": "文档相似性检查", |
|||
"mode": "single", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "document", "title": "文档", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "第一段原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
}, |
|||
{ |
|||
"field": "comparedText", |
|||
"title": "第二段原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
}, |
|||
{ |
|||
"field": "modificationDisplay", |
|||
"title": "相似情况", |
|||
"dataType": "string", |
|||
"displayType": "markdown" |
|||
} |
|||
] |
|||
}, |
|||
"allCheckRepeatText": { |
|||
"taskType": "allCheckRepeatText", |
|||
"name": "全文档相似性检查", |
|||
"mode": "single", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "document", "title": "文档", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "第一段原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
}, |
|||
{ |
|||
"field": "comparedText", |
|||
"title": "第二段原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
}, |
|||
{ |
|||
"field": "modificationDisplay", |
|||
"title": "相似情况", |
|||
"dataType": "string", |
|||
"displayType": "markdown" |
|||
} |
|||
] |
|||
}, |
|||
"checkCompanyName": { |
|||
"taskType": "checkCompanyName", |
|||
"name": "公司名称检查", |
|||
"mode": "single", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "document", "title": "文档", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "modificationDisplay", |
|||
"title": "相关原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
} |
|||
] |
|||
}, |
|||
"checkTitleName": { |
|||
"taskType": "checkTitleName", |
|||
"name": "文档结构检查", |
|||
"mode": "single", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "document", "title": "文档", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "modificationDisplay", |
|||
"title": "结构检查情况", |
|||
"dataType": "string", |
|||
"displayType": "markdown" |
|||
} |
|||
] |
|||
}, |
|||
"checkPlaceName": { |
|||
"taskType": "checkPlaceName", |
|||
"name": "地名检查", |
|||
"mode": "single", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "document", "title": "文档", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "modificationDisplay", |
|||
"title": "相关原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
} |
|||
] |
|||
}, |
|||
"policyBases": { |
|||
"taskType": "policyBases", |
|||
"name": "建设依据检查", |
|||
"mode": "single", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "document", "title": "文档", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "系统名称", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "系统描述", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_points", "review_content"], |
|||
"separator": ":", |
|||
"fieldProcessors": { |
|||
"review_points": "extractFirst" |
|||
} |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
"tenderReview": { |
|||
"taskType": "tenderReview", |
|||
"name": "投标文件审核", |
|||
"mode": "single", |
|||
"pdfConfig": { |
|||
"layout": "single", |
|||
"sources": [ |
|||
{ "id": "document", "title": "文档", "apiField": "id" } |
|||
] |
|||
}, |
|||
"fields": [ |
|||
{ |
|||
"field": "issueName", |
|||
"title": "合规性分类-具体问题(示例:资质审查-业绩要求)", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
}, |
|||
{ |
|||
"field": "originalText", |
|||
"title": "原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "相关法律法规政策", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_points"], |
|||
"separator": ":", |
|||
"fieldProcessors": { |
|||
"review_points": "arrayJoinNewLine" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"field": "modifiedContent", |
|||
"title": "修改建议", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "document" |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "风险等级(红色预警:直接废标项,橙色风险:可能引发投诉项)", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["risk_level"], |
|||
"separator": ":" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,186 @@ |
|||
{ |
|||
"bidConsistencyReview": { |
|||
"taskType": "bidConsistencyReview", |
|||
"name": "投标文件综合分析", |
|||
"mode": "tabs", |
|||
"tabs": [ |
|||
{ |
|||
"key": "文档元数据", |
|||
"label": "文档元数据", |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "文档名称", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "bid", |
|||
"required": true |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "详细元数据信息", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_content", "review_points"], |
|||
"separator": ":", |
|||
"fieldProcessors": { |
|||
"review_points": "arrayJoinNewLine" |
|||
} |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"key": "重点关注相似内容", |
|||
"label": "重点关注相似内容", |
|||
"fields": [ |
|||
|
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "各文档具体内容", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_points"], |
|||
"separator": ":", |
|||
"fieldProcessors": { |
|||
"review_points": "arrayJoinNewLine" |
|||
} |
|||
}, |
|||
"showComparison": true, |
|||
"comparisonConfig": { |
|||
"comparisonField": "modificationDisplay", |
|||
"comparisonTitle": "修改情况展示", |
|||
"showButton": true |
|||
} |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "错别字", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["error_review_points"], |
|||
"separator": ":", |
|||
"fieldProcessors": { |
|||
"error_review_points": "arrayJoinNewLine" |
|||
} |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"key": "错别字", |
|||
"label": "错别字", |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "文档1原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "bid", |
|||
"required": true |
|||
}, |
|||
{ |
|||
"field": "comparedText", |
|||
"title": "文档2原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "bid", |
|||
"required": true |
|||
}, |
|||
|
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "错误详情", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_content", "review_points"], |
|||
"separator": ":", |
|||
"fieldProcessors": { |
|||
"review_points": "arrayJoin" |
|||
} |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"key": "一般相似内容", |
|||
"label": "一般相似内容", |
|||
"fields": [ |
|||
{ |
|||
"field": "modificationDisplay", |
|||
"title": "涉及文档", |
|||
"dataType": "string", |
|||
"displayType": "markdown" |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "各文档具体内容", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_content", "review_points"], |
|||
"separator": ":", |
|||
"fieldProcessors": { |
|||
"review_points": "arrayJoinNewLine" |
|||
} |
|||
} |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
"tenderComplianceReview": { |
|||
"taskType": "tenderComplianceReview", |
|||
"name": "招投标合规性审查", |
|||
"mode": "single", |
|||
"fields": [ |
|||
{ |
|||
"field": "originalText", |
|||
"title": "原文", |
|||
"dataType": "string", |
|||
"displayType": "markdown", |
|||
"pdfSource": "bid", |
|||
"required": true |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "审查意见", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_content"], |
|||
"separator": ":" |
|||
} |
|||
}, |
|||
{ |
|||
"field": "reviewBasis", |
|||
"title": "审查要点", |
|||
"dataType": "json", |
|||
"displayType": "markdown", |
|||
"jsonConfig": { |
|||
"extractFields": ["review_points"], |
|||
"separator": ":", |
|||
"fieldProcessors": { |
|||
"review_points": "arrayJoinNewLine" |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
"field": "modifiedContent", |
|||
"title": "修改建议", |
|||
"dataType": "string", |
|||
"displayType": "markdown" |
|||
}, |
|||
{ |
|||
"field": "modificationDisplay", |
|||
"title": "修改情况", |
|||
"dataType": "string", |
|||
"displayType": "markdown" |
|||
} |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,57 @@ |
|||
import { defHttp } from '@/utils/http/axios'; |
|||
import { ID, IDS, commonExport } from '@/api/base'; |
|||
import { ContractualClauseConfigVO, ContractualClauseConfigForm, ContractualClauseConfigQuery } from './model'; |
|||
|
|||
/** |
|||
* 查询合同条款配置列表 |
|||
* @param params |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseConfigList(params?: ContractualClauseConfigQuery) { |
|||
return defHttp.get<ContractualClauseConfigVO[]>({ url: '/contractreview/contractualClauseConfig/list', params }); |
|||
} |
|||
|
|||
/** |
|||
* 导出合同条款配置列表 |
|||
* @param params |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseConfigExport(params?: ContractualClauseConfigQuery) { |
|||
return commonExport('/contractreview/contractualClauseConfig/export', params ?? {}); |
|||
} |
|||
|
|||
/** |
|||
* 查询合同条款配置详细 |
|||
* @param id id |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseConfigInfo(id: ID) { |
|||
return defHttp.get<ContractualClauseConfigVO>({ url: '/contractreview/contractualClauseConfig/' + id }); |
|||
} |
|||
|
|||
/** |
|||
* 新增合同条款配置 |
|||
* @param data |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseConfigAdd(data: ContractualClauseConfigForm) { |
|||
return defHttp.postWithMsg<void>({ url: '/contractreview/contractualClauseConfig', data }); |
|||
} |
|||
|
|||
/** |
|||
* 更新合同条款配置 |
|||
* @param data |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseConfigUpdate(data: ContractualClauseConfigForm) { |
|||
return defHttp.putWithMsg<void>({ url: '/contractreview/contractualClauseConfig', data }); |
|||
} |
|||
|
|||
/** |
|||
* 删除合同条款配置 |
|||
* @param id id |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseConfigRemove(id: ID | IDS) { |
|||
return defHttp.deleteWithMsg<void>({ url: '/contractreview/contractualClauseConfig/' + id }); |
|||
} |
@ -0,0 +1,48 @@ |
|||
/** |
|||
* 合同条款配置查询对象类型 |
|||
*/ |
|||
export interface ContractualClauseConfigQuery { |
|||
pageNum?: number; |
|||
pageSize?: number; |
|||
contractualClauseTypeId?: number; |
|||
clauseName?: string; |
|||
isRequired?: boolean; |
|||
status?: number; |
|||
orderByColumn?: string; |
|||
isAsc?: string; |
|||
} |
|||
|
|||
/** |
|||
* 合同条款配置视图对象类型 |
|||
*/ |
|||
export interface ContractualClauseConfigVO { |
|||
id?: number; |
|||
contractualClauseTypeId?: number; |
|||
clauseName?: string; |
|||
clauseDescription?: string; |
|||
isRequired?: boolean; |
|||
keywords?: string; |
|||
sortOrder?: number; |
|||
status?: number; |
|||
tenantId?: string; |
|||
delFlag?: string; |
|||
createDept?: number; |
|||
createBy?: number; |
|||
createTime?: string; |
|||
updateBy?: number; |
|||
updateTime?: string; |
|||
} |
|||
|
|||
/** |
|||
* 合同条款配置表单类型 |
|||
*/ |
|||
export interface ContractualClauseConfigForm { |
|||
id?: number; |
|||
contractualClauseTypeId?: number; |
|||
clauseName?: string; |
|||
clauseDescription?: string; |
|||
isRequired?: boolean; |
|||
keywords?: string; |
|||
sortOrder?: number; |
|||
status?: number; |
|||
} |
@ -0,0 +1,57 @@ |
|||
import { defHttp } from '@/utils/http/axios'; |
|||
import { ID, IDS, commonExport } from '@/api/base'; |
|||
import { ContractualClauseTypeVO, ContractualClauseTypeForm, ContractualClauseTypeQuery } from './model'; |
|||
|
|||
/** |
|||
* 查询合同类型列表 |
|||
* @param params |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseTypeList(params?: ContractualClauseTypeQuery) { |
|||
return defHttp.get<ContractualClauseTypeVO[]>({ url: '/contractreview/contractualClauseType/list', params }); |
|||
} |
|||
|
|||
/** |
|||
* 导出合同类型列表 |
|||
* @param params |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseTypeExport(params?: ContractualClauseTypeQuery) { |
|||
return commonExport('/contractreview/contractualClauseType/export', params ?? {}); |
|||
} |
|||
|
|||
/** |
|||
* 查询合同类型详细 |
|||
* @param id id |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseTypeInfo(id: ID) { |
|||
return defHttp.get<ContractualClauseTypeVO>({ url: '/contractreview/contractualClauseType/' + id }); |
|||
} |
|||
|
|||
/** |
|||
* 新增合同类型 |
|||
* @param data |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseTypeAdd(data: ContractualClauseTypeForm) { |
|||
return defHttp.postWithMsg<void>({ url: '/contractreview/contractualClauseType', data }); |
|||
} |
|||
|
|||
/** |
|||
* 更新合同类型 |
|||
* @param data |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseTypeUpdate(data: ContractualClauseTypeForm) { |
|||
return defHttp.putWithMsg<void>({ url: '/contractreview/contractualClauseType', data }); |
|||
} |
|||
|
|||
/** |
|||
* 删除合同类型 |
|||
* @param id id |
|||
* @returns |
|||
*/ |
|||
export function ContractualClauseTypeRemove(id: ID | IDS) { |
|||
return defHttp.deleteWithMsg<void>({ url: '/contractreview/contractualClauseType/' + id }); |
|||
} |
@ -0,0 +1,43 @@ |
|||
/** |
|||
* 合同类型查询对象类型 |
|||
*/ |
|||
export interface ContractualClauseTypeQuery { |
|||
pageNum?: number; |
|||
pageSize?: number; |
|||
typeName?: string; |
|||
typeCode?: string; |
|||
status?: number; |
|||
orderByColumn?: string; |
|||
isAsc?: string; |
|||
} |
|||
|
|||
/** |
|||
* 合同类型视图对象类型 |
|||
*/ |
|||
export interface ContractualClauseTypeVO { |
|||
id?: number; |
|||
typeName?: string; |
|||
typeCode?: string; |
|||
description?: string; |
|||
sortOrder?: number; |
|||
status?: number; |
|||
tenantId?: string; |
|||
delFlag?: string; |
|||
createDept?: number; |
|||
createBy?: number; |
|||
createTime?: string; |
|||
updateBy?: number; |
|||
updateTime?: string; |
|||
} |
|||
|
|||
/** |
|||
* 合同类型表单类型 |
|||
*/ |
|||
export interface ContractualClauseTypeForm { |
|||
id?: number; |
|||
typeName?: string; |
|||
typeCode?: string; |
|||
description?: string; |
|||
sortOrder?: number; |
|||
status?: number; |
|||
} |
@ -0,0 +1,157 @@ |
|||
// JSON配置加载器 - 支持从public目录动态加载配置文件
|
|||
|
|||
import type { TaskConfig } from './taskConfigTypes'; |
|||
|
|||
// 缓存已加载的配置
|
|||
const configCache = new Map<string, Record<string, TaskConfig>>(); |
|||
|
|||
/** |
|||
* 字段处理器映射 - 将字符串类型的处理器转换为实际函数 |
|||
*/ |
|||
const FIELD_PROCESSORS: Record<string, (value: any) => string> = { |
|||
arrayFirst: (value) => Array.isArray(value) && value.length > 0 ? value[0] : value, |
|||
arrayJoin: (value) => Array.isArray(value) ? value.join(', ') : value, |
|||
arrayJoinNewLine: (value) => Array.isArray(value) ? value.join('\n') : value, |
|||
extractFirst: (value) => Array.isArray(value) && value.length > 0 ? value[0] : value, |
|||
}; |
|||
|
|||
/** |
|||
* 处理JSON配置中的字段处理器 |
|||
*/ |
|||
function processFieldProcessors(config: any): any { |
|||
if (!config) return config; |
|||
|
|||
if (Array.isArray(config)) { |
|||
return config.map(processFieldProcessors); |
|||
} |
|||
|
|||
if (typeof config === 'object') { |
|||
const processed: any = {}; |
|||
|
|||
for (const [key, value] of Object.entries(config)) { |
|||
if (key === 'fieldProcessors' && typeof value === 'object') { |
|||
// 转换字符串处理器为实际函数
|
|||
const processors: Record<string, (value: any) => string> = {}; |
|||
for (const [fieldName, processorName] of Object.entries(value as Record<string, string>)) { |
|||
if (typeof processorName === 'string' && FIELD_PROCESSORS[processorName]) { |
|||
processors[fieldName] = FIELD_PROCESSORS[processorName]; |
|||
} |
|||
} |
|||
processed[key] = processors; |
|||
} else { |
|||
processed[key] = processFieldProcessors(value); |
|||
} |
|||
} |
|||
|
|||
return processed; |
|||
} |
|||
|
|||
return config; |
|||
} |
|||
|
|||
/** |
|||
* 从public目录加载JSON配置文件 |
|||
*/ |
|||
async function loadJsonConfig(fileName: string): Promise<Record<string, TaskConfig> | null> { |
|||
try { |
|||
// 检查缓存
|
|||
if (configCache.has(fileName)) { |
|||
return configCache.get(fileName)!; |
|||
} |
|||
|
|||
// 从public目录加载配置文件
|
|||
const response = await fetch(`/configs/${fileName}`); |
|||
|
|||
if (!response.ok) { |
|||
console.warn(`配置文件 ${fileName} 加载失败:`, response.status, response.statusText); |
|||
return null; |
|||
} |
|||
|
|||
const rawConfig = await response.json(); |
|||
|
|||
// 处理字段处理器
|
|||
const processedConfig = processFieldProcessors(rawConfig); |
|||
|
|||
// 缓存配置
|
|||
configCache.set(fileName, processedConfig); |
|||
|
|||
console.log(`成功加载JSON配置文件: ${fileName}`); |
|||
return processedConfig; |
|||
|
|||
} catch (error) { |
|||
console.warn(`加载JSON配置文件 ${fileName} 时出错:`, error); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 清空配置缓存 - 用于强制重新加载配置 |
|||
*/ |
|||
export function clearConfigCache(): void { |
|||
configCache.clear(); |
|||
console.log('配置缓存已清空'); |
|||
} |
|||
|
|||
/** |
|||
* 重新加载指定配置文件 |
|||
*/ |
|||
export async function reloadConfig(fileName: string): Promise<Record<string, TaskConfig> | null> { |
|||
configCache.delete(fileName); |
|||
return await loadJsonConfig(fileName); |
|||
} |
|||
|
|||
/** |
|||
* 加载文档任务配置 |
|||
*/ |
|||
export async function loadDocumentTaskConfigs(): Promise<Record<string, TaskConfig> | null> { |
|||
return await loadJsonConfig('documentTaskConfigs.json'); |
|||
} |
|||
|
|||
/** |
|||
* 加载合同任务配置 |
|||
*/ |
|||
export async function loadContractTaskConfigs(): Promise<Record<string, TaskConfig> | null> { |
|||
return await loadJsonConfig('contractTaskConfigs.json'); |
|||
} |
|||
|
|||
/** |
|||
* 加载招投标任务配置 |
|||
*/ |
|||
export async function loadTenderTaskConfigs(): Promise<Record<string, TaskConfig> | null> { |
|||
return await loadJsonConfig('tenderTaskConfigs.json'); |
|||
} |
|||
|
|||
/** |
|||
* 验证配置格式是否正确 |
|||
*/ |
|||
function validateTaskConfig(config: any): boolean { |
|||
if (!config || typeof config !== 'object') return false; |
|||
|
|||
const requiredFields = ['taskType', 'name', 'mode']; |
|||
for (const field of requiredFields) { |
|||
if (!config[field]) return false; |
|||
} |
|||
|
|||
if (config.mode === 'tabs' && !config.tabs) return false; |
|||
if (config.mode === 'single' && !config.fields) return false; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 检查JSON配置是否可用 |
|||
*/ |
|||
export async function isJsonConfigAvailable(): Promise<boolean> { |
|||
try { |
|||
const response = await fetch('/configs/documentTaskConfigs.json', { method: 'HEAD' }); |
|||
return response.ok; |
|||
} catch { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
// 导出配置加载函数
|
|||
export { |
|||
loadJsonConfig, |
|||
validateTaskConfig |
|||
}; |
@ -0,0 +1,924 @@ |
|||
<template> |
|||
<PageWrapper dense> |
|||
<div class="container"> |
|||
<!-- 左侧合同类型 --> |
|||
<div class="contract-type-sidebar w-64 border-r border-gray-200 p-4"> |
|||
<div class="sidebar-title font-bold text-lg mb-4">合同类型</div> |
|||
<Button type="primary" class="w-full mb-4" @click="handleCreateType"> |
|||
<template #icon><PlusOutlined /></template> |
|||
新建类型 |
|||
</Button> |
|||
<div class="type-list overflow-y-auto"> |
|||
<div |
|||
v-for="contractType in contractTypes" |
|||
:key="contractType.id" |
|||
:class="['type-item p-3 cursor-pointer rounded mb-2 flex justify-between', |
|||
{ 'selected': selectedTypeId === contractType.id }]" |
|||
> |
|||
<div class="flex-1" @click="selectContractType(contractType.id)"> |
|||
<div class="flex justify-between items-center"> |
|||
<div class="type-name font-medium">{{ contractType.typeName }}</div> |
|||
<div class="text-xs text-gray-400" v-if="clauseCounts.get(contractType.id || 0)"> |
|||
{{ clauseCounts.get(contractType.id || 0) }}条款 |
|||
</div> |
|||
</div> |
|||
<div class="type-code text-sm text-gray-500">{{ contractType.typeCode }}</div> |
|||
</div> |
|||
<div class="flex space-x-1"> |
|||
<Button |
|||
type="text" |
|||
size="small" |
|||
@click.stop="handleEditType(contractType)" |
|||
> |
|||
<template #icon><EditOutlined /></template> |
|||
</Button> |
|||
<Button |
|||
type="text" |
|||
size="small" |
|||
danger |
|||
@click.stop="confirmDeleteType(contractType)" |
|||
v-if="contractTypes.length > 1" |
|||
> |
|||
<template #icon><DeleteOutlined /></template> |
|||
</Button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 右侧条款配置表格 --> |
|||
<div class="clause-config-content flex-1 p-4"> |
|||
<div class="content-header flex justify-between items-center mb-4"> |
|||
<div> |
|||
<h2 class="text-xl font-bold">合同条款配置</h2> |
|||
<div class="text-sm text-gray-500 mt-1" v-if="selectedTypeName"> |
|||
当前类型:{{ selectedTypeName }} |
|||
</div> |
|||
</div> |
|||
<div class="actions flex space-x-2"> |
|||
<Button type="primary" danger @click="handleBatchDelete" :disabled="!hasSelectedClauses"> |
|||
<template #icon><DeleteOutlined /></template> |
|||
批量删除 |
|||
</Button> |
|||
<Button type="primary" @click="handleAddClause"> |
|||
<template #icon><PlusOutlined /></template> |
|||
新建条款 |
|||
</Button> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 条款配置表格 --> |
|||
<div class="clause-table-wrapper" v-if="selectedTypeId"> |
|||
<Table |
|||
:columns="columns" |
|||
:data-source="currentClauses" |
|||
:row-selection="rowSelection" |
|||
:pagination="pagination" |
|||
:scroll="{ y: 'calc(100vh - 400px)' }" |
|||
:row-key="(record) => record.id" |
|||
size="middle" |
|||
bordered |
|||
> |
|||
<!-- 条款名称列 --> |
|||
<template #clauseName="{ record }"> |
|||
<div class="clause-name-cell"> |
|||
<div class="clause-title font-medium">{{ record.clauseName }}</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<!-- 条款描述列 --> |
|||
<template #clauseDescription="{ record }"> |
|||
<div class="description-cell"> |
|||
<Tooltip :title="record.clauseDescription" placement="topLeft"> |
|||
<div class="description-text">{{ record.clauseDescription }}</div> |
|||
</Tooltip> |
|||
</div> |
|||
</template> |
|||
|
|||
<!-- 是否必查列 --> |
|||
<template #isRequired="{ record }"> |
|||
<Tag :color="record.isRequired ? 'success' : 'default'"> |
|||
{{ record.isRequired ? '必查' : '可选' }} |
|||
</Tag> |
|||
</template> |
|||
|
|||
<!-- 关键词列 --> |
|||
<template #keywords="{ record }"> |
|||
<div class="keywords-cell"> |
|||
<Tag |
|||
v-for="keyword in record.keywords.split(',').slice(0, 3)" |
|||
:key="keyword" |
|||
size="small" |
|||
class="keyword-tag" |
|||
> |
|||
{{ keyword }} |
|||
</Tag> |
|||
<Tooltip v-if="record.keywords.split(',').length > 3" :title="record.keywords"> |
|||
<Tag size="small" color="blue"> |
|||
+{{ record.keywords.split(',').length - 3 }} |
|||
</Tag> |
|||
</Tooltip> |
|||
</div> |
|||
</template> |
|||
|
|||
<!-- 状态列 --> |
|||
<template #status="{ record }"> |
|||
<Tag :color="record.status === 1 ? 'success' : 'error'"> |
|||
{{ record.status === 1 ? '启用' : '禁用' }} |
|||
</Tag> |
|||
</template> |
|||
|
|||
<!-- 操作列 --> |
|||
<template #action="{ record }"> |
|||
<div class="action-buttons flex space-x-2"> |
|||
<Button type="link" size="small" @click="handleEditClause(record)"> |
|||
编辑 |
|||
</Button> |
|||
<Button type="link" size="small" danger @click="confirmDeleteClause(record)"> |
|||
删除 |
|||
</Button> |
|||
</div> |
|||
</template> |
|||
</Table> |
|||
</div> |
|||
|
|||
<!-- 空状态 --> |
|||
<div v-else class="empty-state text-center py-12"> |
|||
<div class="text-gray-400 text-6xl mb-4">📋</div> |
|||
<div class="text-gray-500 text-lg">请选择一个合同类型查看条款配置</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 删除确认模态框 --> |
|||
<Modal |
|||
v-model:visible="deleteModalVisible" |
|||
:title="deleteModalTitle" |
|||
@ok="handleDeleteConfirm" |
|||
@cancel="deleteModalVisible = false" |
|||
okText="确认删除" |
|||
cancelText="取消" |
|||
okType="danger" |
|||
> |
|||
<p>{{ deleteModalContent }}</p> |
|||
</Modal> |
|||
|
|||
<!-- 合同类型表单模态框 --> |
|||
<Modal |
|||
v-model:visible="typeModalVisible" |
|||
:title="typeModalTitle" |
|||
@ok="handleTypeSave" |
|||
@cancel="typeModalVisible = false" |
|||
okText="保存" |
|||
cancelText="取消" |
|||
width="600px" |
|||
> |
|||
<Form |
|||
:model="typeForm" |
|||
ref="typeFormRef" |
|||
:label-col="{ span: 6 }" |
|||
:wrapper-col="{ span: 18 }" |
|||
> |
|||
<FormItem label="类型名称" name="type_name" :rules="[{ required: true, message: '请输入类型名称' }]"> |
|||
<Input v-model:value="typeForm.type_name" placeholder="请输入类型名称" /> |
|||
</FormItem> |
|||
|
|||
<FormItem label="类型编码" name="type_code" :rules="[{ required: true, message: '请输入类型编码' }]"> |
|||
<Input v-model:value="typeForm.type_code" placeholder="请输入类型编码" /> |
|||
</FormItem> |
|||
|
|||
<FormItem label="描述" name="description"> |
|||
<Textarea v-model:value="typeForm.description" placeholder="请输入描述信息" :rows="3" /> |
|||
</FormItem> |
|||
|
|||
<FormItem label="排序" name="sort_order"> |
|||
<InputNumber v-model:value="typeForm.sort_order" placeholder="请输入排序" style="width: 100%" /> |
|||
</FormItem> |
|||
|
|||
<FormItem label="状态" name="status"> |
|||
<Select v-model:value="typeForm.status" placeholder="请选择状态"> |
|||
<SelectOption :value="1">启用</SelectOption> |
|||
<SelectOption :value="0">禁用</SelectOption> |
|||
</Select> |
|||
</FormItem> |
|||
</Form> |
|||
</Modal> |
|||
|
|||
<!-- 条款配置表单模态框 --> |
|||
<Modal |
|||
v-model:visible="clauseModalVisible" |
|||
:title="clauseModalTitle" |
|||
@ok="handleClauseSave" |
|||
@cancel="clauseModalVisible = false" |
|||
okText="保存" |
|||
cancelText="取消" |
|||
width="800px" |
|||
> |
|||
<Form |
|||
:model="clauseForm" |
|||
ref="clauseFormRef" |
|||
:label-col="{ span: 6 }" |
|||
:wrapper-col="{ span: 18 }" |
|||
> |
|||
<FormItem label="条款名称" name="clause_name" :rules="[{ required: true, message: '请输入条款名称' }]"> |
|||
<Input v-model:value="clauseForm.clause_name" placeholder="请输入条款名称" /> |
|||
</FormItem> |
|||
|
|||
<FormItem label="条款描述" name="clause_description" :rules="[{ required: true, message: '请输入条款描述' }]"> |
|||
<Textarea v-model:value="clauseForm.clause_description" placeholder="请输入条款内容描述" :rows="4" /> |
|||
</FormItem> |
|||
|
|||
<FormItem label="是否必查" name="is_required"> |
|||
<Select v-model:value="clauseForm.is_required" placeholder="请选择是否必查"> |
|||
<SelectOption :value="true">必查</SelectOption> |
|||
<SelectOption :value="false">可选</SelectOption> |
|||
</Select> |
|||
</FormItem> |
|||
|
|||
<FormItem label="关键词" name="keywords" :rules="[{ required: true, message: '请输入关键词' }]"> |
|||
<Textarea |
|||
v-model:value="clauseForm.keywords" |
|||
placeholder="请输入关键词,多个关键词用英文逗号分隔" |
|||
:rows="3" |
|||
/> |
|||
</FormItem> |
|||
|
|||
<FormItem label="排序" name="sort_order"> |
|||
<InputNumber v-model:value="clauseForm.sort_order" placeholder="请输入排序" style="width: 100%" /> |
|||
</FormItem> |
|||
|
|||
<FormItem label="状态" name="status"> |
|||
<Select v-model:value="clauseForm.status" placeholder="请选择状态"> |
|||
<SelectOption :value="1">启用</SelectOption> |
|||
<SelectOption :value="0">禁用</SelectOption> |
|||
</Select> |
|||
</FormItem> |
|||
</Form> |
|||
</Modal> |
|||
</PageWrapper> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, computed, onMounted, reactive } from 'vue'; |
|||
import { PlusOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons-vue'; |
|||
import { |
|||
Button, Modal, message, Form, FormItem, Input, InputNumber, |
|||
Select, SelectOption, Textarea, Table, Tag, Tooltip |
|||
} from 'ant-design-vue'; |
|||
import { FormInstance } from 'ant-design-vue/es/form'; |
|||
import { |
|||
ContractualClauseTypeList, ContractualClauseTypeAdd, ContractualClauseTypeUpdate, ContractualClauseTypeRemove |
|||
} from '@/api/contractReview/ContractualClauseType'; |
|||
import { |
|||
ContractualClauseConfigList, ContractualClauseConfigAdd, ContractualClauseConfigUpdate, ContractualClauseConfigRemove |
|||
} from '@/api/contractReview/ContractualClauseConfig'; |
|||
import { ContractualClauseTypeVO, ContractualClauseTypeForm } from '@/api/contractReview/ContractualClauseType/model'; |
|||
import { ContractualClauseConfigVO, ContractualClauseConfigForm } from '@/api/contractReview/ContractualClauseConfig/model'; |
|||
|
|||
defineOptions({ name: 'ContractClauseConfig' }); |
|||
|
|||
// 数据变量 |
|||
const contractTypes = ref<ContractualClauseTypeVO[]>([]); |
|||
const clauseConfigs = ref<ContractualClauseConfigVO[]>([]); |
|||
const clauseCounts = ref<Map<number, number>>(new Map()); |
|||
|
|||
// 状态变量 |
|||
const selectedTypeId = ref<number | null>(null); |
|||
const selectedClauseIds = ref<number[]>([]); |
|||
|
|||
// 表格列定义 |
|||
const columns = [ |
|||
{ |
|||
title: '条款名称', |
|||
dataIndex: 'clauseName', |
|||
key: 'clauseName', |
|||
width: 150, |
|||
fixed: 'left' as const, |
|||
slots: { customRender: 'clauseName' } |
|||
}, |
|||
{ |
|||
title: '条款描述', |
|||
dataIndex: 'clauseDescription', |
|||
key: 'clauseDescription', |
|||
width: 300, |
|||
slots: { customRender: 'clauseDescription' } |
|||
}, |
|||
{ |
|||
title: '是否必查', |
|||
dataIndex: 'isRequired', |
|||
key: 'isRequired', |
|||
width: 100, |
|||
align: 'center' as const, |
|||
slots: { customRender: 'isRequired' } |
|||
}, |
|||
{ |
|||
title: '关键词', |
|||
dataIndex: 'keywords', |
|||
key: 'keywords', |
|||
width: 250, |
|||
slots: { customRender: 'keywords' } |
|||
}, |
|||
{ |
|||
title: '排序', |
|||
dataIndex: 'sortOrder', |
|||
key: 'sortOrder', |
|||
width: 80, |
|||
align: 'center' as const |
|||
}, |
|||
{ |
|||
title: '状态', |
|||
dataIndex: 'status', |
|||
key: 'status', |
|||
width: 80, |
|||
align: 'center' as const, |
|||
slots: { customRender: 'status' } |
|||
}, |
|||
{ |
|||
title: '操作', |
|||
key: 'action', |
|||
width: 120, |
|||
fixed: 'right' as const, |
|||
align: 'center' as const, |
|||
slots: { customRender: 'action' } |
|||
} |
|||
]; |
|||
|
|||
// 表格行选择配置 |
|||
const rowSelection = computed(() => ({ |
|||
selectedRowKeys: selectedClauseIds.value, |
|||
onChange: (selectedRowKeys: any[], selectedRows: any[]) => { |
|||
selectedClauseIds.value = selectedRowKeys as number[]; |
|||
}, |
|||
getCheckboxProps: (record: ContractualClauseConfigVO) => ({ |
|||
disabled: false, |
|||
name: record.clauseName || '', |
|||
}), |
|||
})); |
|||
|
|||
// 分页状态 |
|||
const pagination = reactive({ |
|||
current: 1, |
|||
pageSize: 10, |
|||
total: 0, |
|||
showSizeChanger: true, |
|||
showQuickJumper: true, |
|||
pageSizeOptions: ['10', '20', '50', '100'], |
|||
showTotal: (total: number, range: [number, number]) => `共 ${total} 条记录`, |
|||
onChange: (page: number, size: number) => { |
|||
pagination.current = page; |
|||
pagination.pageSize = size; |
|||
selectedClauseIds.value = []; // 清空选中状态 |
|||
loadClauseConfigs(); |
|||
}, |
|||
onShowSizeChange: (current: number, size: number) => { |
|||
pagination.current = 1; |
|||
pagination.pageSize = size; |
|||
selectedClauseIds.value = []; // 清空选中状态 |
|||
loadClauseConfigs(); |
|||
} |
|||
}); |
|||
|
|||
// 模态框状态 |
|||
const deleteModalVisible = ref(false); |
|||
const deleteModalTitle = ref(''); |
|||
const deleteModalContent = ref(''); |
|||
const deleteType = ref<'type' | 'clause'>('clause'); |
|||
const itemToDelete = ref<any>(null); |
|||
|
|||
const typeModalVisible = ref(false); |
|||
const typeModalTitle = ref('新建合同类型'); |
|||
const typeForm = reactive({ |
|||
id: undefined, |
|||
type_name: '', |
|||
type_code: '', |
|||
description: '', |
|||
sort_order: 0, |
|||
status: 1 |
|||
}); |
|||
const typeFormRef = ref<FormInstance>(); |
|||
|
|||
const clauseModalVisible = ref(false); |
|||
const clauseModalTitle = ref('新建条款'); |
|||
const clauseForm = reactive({ |
|||
id: undefined, |
|||
contractual_clause_type_id: undefined, |
|||
clause_name: '', |
|||
clause_description: '', |
|||
is_required: true, |
|||
keywords: '', |
|||
sort_order: 0, |
|||
status: 1 |
|||
}); |
|||
const clauseFormRef = ref<FormInstance>(); |
|||
|
|||
// 计算属性 |
|||
const selectedTypeName = computed(() => { |
|||
if (!selectedTypeId.value) return ''; |
|||
const type = contractTypes.value.find(t => t.id === selectedTypeId.value); |
|||
return type ? type.typeName : ''; |
|||
}); |
|||
|
|||
const currentClauses = computed(() => { |
|||
return clauseConfigs.value; |
|||
}); |
|||
|
|||
const hasSelectedClauses = computed(() => { |
|||
return selectedClauseIds.value.length > 0; |
|||
}); |
|||
|
|||
// 加载数据方法 |
|||
const loadContractTypes = async () => { |
|||
try { |
|||
const res = await ContractualClauseTypeList({}); |
|||
const data = res as any; |
|||
if (data && Array.isArray(data)) { |
|||
contractTypes.value = data; |
|||
// 加载每个类型的条款数量 |
|||
await loadClauseCounts(); |
|||
// 默认选择第一个类型 |
|||
if (data.length > 0 && data[0].id) { |
|||
selectedTypeId.value = data[0].id; |
|||
await loadClauseConfigs(); |
|||
} |
|||
} |
|||
} catch (error) { |
|||
console.error('加载合同类型失败', error); |
|||
message.error('加载合同类型失败'); |
|||
} |
|||
}; |
|||
|
|||
const loadClauseCounts = async () => { |
|||
const counts = new Map<number, number>(); |
|||
|
|||
for (const type of contractTypes.value) { |
|||
if (type.id) { |
|||
try { |
|||
const res = await ContractualClauseConfigList({ contractualClauseTypeId: type.id }); |
|||
const data = res as any; |
|||
|
|||
let count = 0; |
|||
if (data && typeof data === 'object') { |
|||
if (data.rows && Array.isArray(data.rows)) { |
|||
count = data.total || data.rows.length; |
|||
} else if (Array.isArray(data)) { |
|||
count = data.length; |
|||
} |
|||
} |
|||
counts.set(type.id, count); |
|||
} catch (error) { |
|||
console.warn(`加载类型 ${type.typeName} 的条款数量失败`, error); |
|||
counts.set(type.id, 0); |
|||
} |
|||
} |
|||
} |
|||
|
|||
clauseCounts.value = counts; |
|||
}; |
|||
|
|||
const loadClauseConfigs = async () => { |
|||
if (!selectedTypeId.value) { |
|||
clauseConfigs.value = []; |
|||
pagination.total = 0; |
|||
return; |
|||
} |
|||
try { |
|||
const params = { |
|||
contractualClauseTypeId: selectedTypeId.value, |
|||
pageNum: pagination.current, |
|||
pageSize: pagination.pageSize |
|||
}; |
|||
const res = await ContractualClauseConfigList(params); |
|||
const data = res as any; |
|||
|
|||
// 处理分页数据 |
|||
if (data && typeof data === 'object') { |
|||
if (data.rows && Array.isArray(data.rows)) { |
|||
// 标准分页响应格式 { rows: [], total: number } |
|||
clauseConfigs.value = data.rows; |
|||
pagination.total = data.total || 0; |
|||
} else if (Array.isArray(data)) { |
|||
// 简单数组格式(可能需要前端分页) |
|||
clauseConfigs.value = data; |
|||
pagination.total = data.length; |
|||
} else { |
|||
clauseConfigs.value = []; |
|||
pagination.total = 0; |
|||
} |
|||
} else { |
|||
clauseConfigs.value = []; |
|||
pagination.total = 0; |
|||
} |
|||
} catch (error) { |
|||
console.error('加载条款配置失败', error); |
|||
message.error('加载条款配置失败'); |
|||
clauseConfigs.value = []; |
|||
pagination.total = 0; |
|||
} |
|||
}; |
|||
|
|||
// 方法定义 |
|||
const selectContractType = async (typeId: number) => { |
|||
selectedTypeId.value = typeId; |
|||
selectedClauseIds.value = []; |
|||
// 重置分页状态 |
|||
pagination.current = 1; |
|||
await loadClauseConfigs(); |
|||
}; |
|||
|
|||
const handleCreateType = () => { |
|||
Object.assign(typeForm, { |
|||
id: undefined, |
|||
type_name: '', |
|||
type_code: '', |
|||
description: '', |
|||
sort_order: 0, |
|||
status: 1 |
|||
}); |
|||
typeModalTitle.value = '新建合同类型'; |
|||
typeModalVisible.value = true; |
|||
}; |
|||
|
|||
const handleEditType = (record: ContractualClauseTypeVO) => { |
|||
// 字段名映射 |
|||
Object.assign(typeForm, { |
|||
id: record.id, |
|||
type_name: record.typeName, |
|||
type_code: record.typeCode, |
|||
description: record.description, |
|||
sort_order: record.sortOrder, |
|||
status: record.status |
|||
}); |
|||
typeModalTitle.value = '编辑合同类型'; |
|||
typeModalVisible.value = true; |
|||
}; |
|||
|
|||
const handleAddClause = () => { |
|||
if (!selectedTypeId.value) { |
|||
message.warning('请先选择合同类型'); |
|||
return; |
|||
} |
|||
Object.assign(clauseForm, { |
|||
id: undefined, |
|||
contractual_clause_type_id: selectedTypeId.value, |
|||
clause_name: '', |
|||
clause_description: '', |
|||
is_required: true, |
|||
keywords: '', |
|||
sort_order: 0, |
|||
status: 1 |
|||
}); |
|||
clauseModalTitle.value = '新建条款'; |
|||
clauseModalVisible.value = true; |
|||
}; |
|||
|
|||
const handleEditClause = (record: ContractualClauseConfigVO) => { |
|||
// 字段名映射 |
|||
Object.assign(clauseForm, { |
|||
id: record.id, |
|||
contractual_clause_type_id: record.contractualClauseTypeId, |
|||
clause_name: record.clauseName, |
|||
clause_description: record.clauseDescription, |
|||
is_required: record.isRequired, |
|||
keywords: record.keywords, |
|||
sort_order: record.sortOrder, |
|||
status: record.status |
|||
}); |
|||
clauseModalTitle.value = '编辑条款'; |
|||
clauseModalVisible.value = true; |
|||
}; |
|||
|
|||
const confirmDeleteType = async (type: ContractualClauseTypeVO) => { |
|||
try { |
|||
// 检查是否存在关联的条款配置 |
|||
const res = await ContractualClauseConfigList({ contractualClauseTypeId: type.id }); |
|||
const data = res as any; |
|||
|
|||
let hasClause = false; |
|||
if (data && typeof data === 'object') { |
|||
if (data.rows && Array.isArray(data.rows)) { |
|||
hasClause = data.rows.length > 0; |
|||
} else if (Array.isArray(data)) { |
|||
hasClause = data.length > 0; |
|||
} |
|||
} |
|||
|
|||
if (hasClause) { |
|||
message.warning(`合同类型 "${type.typeName}" 下还存在条款配置,无法删除!请先删除相关条款。`); |
|||
return; |
|||
} |
|||
|
|||
// 没有关联条款,可以删除 |
|||
deleteModalTitle.value = '删除合同类型'; |
|||
deleteModalContent.value = `确定要删除合同类型 "${type.typeName}" 吗?此操作不可恢复。`; |
|||
itemToDelete.value = type; |
|||
deleteType.value = 'type'; |
|||
deleteModalVisible.value = true; |
|||
} catch (error) { |
|||
console.error('检查关联条款失败', error); |
|||
message.error('检查关联条款失败,无法删除'); |
|||
} |
|||
}; |
|||
|
|||
const confirmDeleteClause = (clause: ContractualClauseConfigVO) => { |
|||
deleteModalTitle.value = '删除条款'; |
|||
deleteModalContent.value = `确定要删除条款 "${clause.clauseName}" 吗?此操作不可恢复。`; |
|||
itemToDelete.value = clause; |
|||
deleteType.value = 'clause'; |
|||
deleteModalVisible.value = true; |
|||
}; |
|||
|
|||
const handleBatchDelete = () => { |
|||
if (selectedClauseIds.value.length === 0) { |
|||
message.warning('请先选择要删除的条款'); |
|||
return; |
|||
} |
|||
deleteModalTitle.value = '批量删除条款'; |
|||
deleteModalContent.value = `确定要删除选中的 ${selectedClauseIds.value.length} 个条款吗?此操作不可恢复。`; |
|||
deleteType.value = 'clause'; |
|||
deleteModalVisible.value = true; |
|||
itemToDelete.value = { batchDelete: true }; |
|||
}; |
|||
|
|||
const handleDeleteConfirm = async () => { |
|||
try { |
|||
if (deleteType.value === 'type') { |
|||
// 删除合同类型 |
|||
if (itemToDelete.value?.id) { |
|||
await ContractualClauseTypeRemove(itemToDelete.value.id); |
|||
message.success('删除合同类型成功'); |
|||
await loadContractTypes(); |
|||
} |
|||
} else { |
|||
// 删除条款 |
|||
if (itemToDelete.value?.batchDelete) { |
|||
// 批量删除 |
|||
if (selectedClauseIds.value.length > 0) { |
|||
await ContractualClauseConfigRemove(selectedClauseIds.value.join(',')); |
|||
message.success(`成功删除 ${selectedClauseIds.value.length} 个条款`); |
|||
selectedClauseIds.value = []; |
|||
} |
|||
} else { |
|||
// 单个删除 |
|||
if (itemToDelete.value?.id) { |
|||
await ContractualClauseConfigRemove(itemToDelete.value.id); |
|||
message.success('删除条款成功'); |
|||
} |
|||
} |
|||
await loadClauseConfigs(); |
|||
await loadClauseCounts(); |
|||
} |
|||
} catch (error) { |
|||
console.error('删除失败', error); |
|||
message.error('删除失败'); |
|||
} |
|||
deleteModalVisible.value = false; |
|||
}; |
|||
|
|||
const handleTypeSave = async () => { |
|||
try { |
|||
await typeFormRef.value?.validate(); |
|||
|
|||
// 准备API数据,字段名映射 |
|||
const apiData = { |
|||
id: typeForm.id, |
|||
typeName: typeForm.type_name, |
|||
typeCode: typeForm.type_code, |
|||
description: typeForm.description, |
|||
sortOrder: typeForm.sort_order, |
|||
status: typeForm.status |
|||
}; |
|||
|
|||
if (typeForm.id) { |
|||
await ContractualClauseTypeUpdate(apiData); |
|||
message.success('更新合同类型成功'); |
|||
} else { |
|||
await ContractualClauseTypeAdd(apiData); |
|||
message.success('新增合同类型成功'); |
|||
} |
|||
|
|||
typeModalVisible.value = false; |
|||
await loadContractTypes(); |
|||
} catch (error) { |
|||
console.error('保存合同类型失败', error); |
|||
message.error('保存合同类型失败'); |
|||
} |
|||
}; |
|||
|
|||
const handleClauseSave = async () => { |
|||
try { |
|||
await clauseFormRef.value?.validate(); |
|||
|
|||
// 准备API数据,字段名映射 |
|||
const apiData = { |
|||
id: clauseForm.id, |
|||
contractualClauseTypeId: clauseForm.contractual_clause_type_id, |
|||
clauseName: clauseForm.clause_name, |
|||
clauseDescription: clauseForm.clause_description, |
|||
isRequired: clauseForm.is_required, |
|||
keywords: clauseForm.keywords, |
|||
sortOrder: clauseForm.sort_order, |
|||
status: clauseForm.status |
|||
}; |
|||
|
|||
if (clauseForm.id) { |
|||
await ContractualClauseConfigUpdate(apiData); |
|||
message.success('更新条款成功'); |
|||
} else { |
|||
await ContractualClauseConfigAdd(apiData); |
|||
message.success('新增条款成功'); |
|||
} |
|||
|
|||
clauseModalVisible.value = false; |
|||
await loadClauseConfigs(); |
|||
await loadClauseCounts(); |
|||
} catch (error) { |
|||
console.error('保存条款失败', error); |
|||
message.error('保存条款失败'); |
|||
} |
|||
}; |
|||
|
|||
// 初始化 |
|||
onMounted(async () => { |
|||
await loadContractTypes(); |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.container { |
|||
min-height: calc(100vh - 120px); |
|||
height: calc(100vh - 120px); |
|||
background-color: #fff; |
|||
display: flex; |
|||
width: 100%; |
|||
max-width: none; |
|||
} |
|||
|
|||
.contract-type-sidebar { |
|||
min-width: 240px; |
|||
width: 240px; |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.type-list { |
|||
flex: 1; |
|||
overflow-y: auto; |
|||
padding-right: 4px; |
|||
} |
|||
|
|||
.clause-config-content { |
|||
flex: 1; |
|||
height: 100%; |
|||
min-width: 0; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.clause-table-wrapper { |
|||
flex: 1; |
|||
overflow: hidden; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.clause-table-wrapper :deep(.ant-table-wrapper) { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.clause-table-wrapper :deep(.ant-table-container) { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.clause-table-wrapper :deep(.ant-table-body) { |
|||
flex: 1; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.clause-table-wrapper :deep(.ant-pagination) { |
|||
margin-top: 16px; |
|||
text-align: right; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.type-item { |
|||
transition: all 0.3s; |
|||
border-radius: 6px; |
|||
margin-bottom: 4px; |
|||
} |
|||
|
|||
.type-item:hover { |
|||
background-color: #f0f7ff !important; |
|||
} |
|||
|
|||
.type-item.selected { |
|||
background-color: #e6f7ff !important; |
|||
border-left: 3px solid #1890ff; |
|||
} |
|||
|
|||
.type-name { |
|||
font-size: 14px; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
.type-code { |
|||
font-size: 12px; |
|||
margin-top: 2px; |
|||
} |
|||
|
|||
.clause-name-cell .clause-title { |
|||
font-size: 14px; |
|||
color: #1890ff; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.clause-name-cell .clause-title:hover { |
|||
text-decoration: underline; |
|||
} |
|||
|
|||
.description-cell { |
|||
max-width: 300px; |
|||
} |
|||
|
|||
.description-text { |
|||
display: -webkit-box; |
|||
-webkit-line-clamp: 2; |
|||
-webkit-box-orient: vertical; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
.keywords-cell { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 4px; |
|||
max-width: 250px; |
|||
} |
|||
|
|||
.keyword-tag { |
|||
margin: 0; |
|||
} |
|||
|
|||
.action-buttons { |
|||
justify-content: center; |
|||
} |
|||
|
|||
.empty-state { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
height: 100%; |
|||
background-color: #fafafa; |
|||
border-radius: 8px; |
|||
margin: 16px; |
|||
} |
|||
|
|||
/* 滚动条样式 */ |
|||
.type-list::-webkit-scrollbar { |
|||
width: 6px; |
|||
} |
|||
|
|||
.type-list::-webkit-scrollbar-track { |
|||
background: #f1f1f1; |
|||
border-radius: 3px; |
|||
} |
|||
|
|||
.type-list::-webkit-scrollbar-thumb { |
|||
background: #c1c1c1; |
|||
border-radius: 3px; |
|||
} |
|||
|
|||
.type-list::-webkit-scrollbar-thumb:hover { |
|||
background: #a1a1a1; |
|||
} |
|||
|
|||
/* 确保PageWrapper不限制宽度 */ |
|||
:deep(.ant-page-wrapper) { |
|||
padding: 0 !important; |
|||
} |
|||
|
|||
:deep(.ant-page-wrapper-content) { |
|||
margin: 0 !important; |
|||
padding: 16px !important; |
|||
} |
|||
|
|||
/* 表格样式优化 */ |
|||
:deep(.ant-table-tbody > tr > td) { |
|||
padding: 12px 8px; |
|||
} |
|||
|
|||
:deep(.ant-table-thead > tr > th) { |
|||
background-color: #fafafa; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
:deep(.ant-table-row:hover > td) { |
|||
background-color: #f5f5f5; |
|||
} |
|||
</style> |
Loading…
Reference in new issue