diff --git a/src/api/tenderReview/TenderTask/index.ts b/src/api/tenderReview/TenderTask/index.ts index 035de7c..83b897d 100644 --- a/src/api/tenderReview/TenderTask/index.ts +++ b/src/api/tenderReview/TenderTask/index.ts @@ -1,6 +1,8 @@ import { defHttp } from '@/utils/http/axios'; import { ID, IDS, commonExport } from '@/api/base'; import { TenderTaskVO, TenderTaskForm, TenderTaskQuery } from './model'; +import { UploadFileParams } from '#/axios'; +import { AxiosProgressEvent } from 'axios'; /** * 查询招标摘要任务列表 @@ -8,7 +10,7 @@ import { TenderTaskVO, TenderTaskForm, TenderTaskQuery } from './model'; * @returns */ export function TenderTaskList(params?: TenderTaskQuery) { - return defHttp.get({ url: '/productManagement/TenderTask/list', params }); + return defHttp.get({ url: '/tenderTask/tenderTasks/list', params }); } /** @@ -17,7 +19,7 @@ export function TenderTaskList(params?: TenderTaskQuery) { * @returns */ export function TenderTaskExport(params?: TenderTaskQuery) { - return commonExport('/productManagement/TenderTask/export', params ?? {}); + return commonExport('/tenderTask/tenderTasks/export', params ?? {}); } /** @@ -26,7 +28,7 @@ export function TenderTaskExport(params?: TenderTaskQuery) { * @returns */ export function TenderTaskInfo(id: ID) { - return defHttp.get({ url: '/productManagement/TenderTask/' + id }); + return defHttp.get({ url: '/tenderTask/tenderTasks/' + id }); } /** @@ -35,7 +37,7 @@ export function TenderTaskInfo(id: ID) { * @returns */ export function TenderTaskAdd(data: TenderTaskForm) { - return defHttp.postWithMsg({ url: '/productManagement/TenderTask', data }); + return defHttp.postWithMsg({ url: '/tenderTask/tenderTasks', data, timeout: 1000 * 60 * 10 }); } /** @@ -44,7 +46,7 @@ export function TenderTaskAdd(data: TenderTaskForm) { * @returns */ export function TenderTaskUpdate(data: TenderTaskForm) { - return defHttp.putWithMsg({ url: '/productManagement/TenderTask', data }); + return defHttp.putWithMsg({ url: '/tenderTask/tenderTasks', data }); } /** @@ -53,5 +55,43 @@ export function TenderTaskUpdate(data: TenderTaskForm) { * @returns */ export function TenderTaskRemove(id: ID | IDS) { - return defHttp.deleteWithMsg({ url: '/productManagement/TenderTask/' + id },); + return defHttp.deleteWithMsg({ url: '/tenderTask/tenderTasks/' + id }); +} + +/** + * 停止招标任务 + * @param id id + * @returns + */ +export function TenderTaskStop(id: ID | IDS) { + return defHttp.putWithMsg({ url: '/tenderTask/tenderTasks/stopTask/' + id }); +} + +/** + * 删除招标任务文件 + * @param ossId ossId + * @returns + */ +export function TenderTaskDeleteFile(ossId: string) { + return defHttp.deleteWithMsg({ url: '/tenderTask/tenderTasks/ossRemoveById/' + ossId }); +} + +/** + * 上传招标文档 + * @param params + * @param onUploadProgress + * @returns + */ +export function uploadTenderDocument( + params: UploadFileParams, + onUploadProgress?: (progressEvent: AxiosProgressEvent) => void, +) { + return defHttp.uploadFile( + { + url: '/tenderTask/tenderTasks/back/upload', + onUploadProgress, + timeout: 1000 * 60 * 10, + }, + params, + ); } diff --git a/src/api/tenderReview/TenderTask/model.ts b/src/api/tenderReview/TenderTask/model.ts index ba05040..e9615be 100644 --- a/src/api/tenderReview/TenderTask/model.ts +++ b/src/api/tenderReview/TenderTask/model.ts @@ -1,6 +1,11 @@ import { BaseEntity, PageQuery } from '@/api/base'; export interface TenderTaskVO { + /** + * 任务ID + */ + id: string | number; + /** * 模型所属行业 */ @@ -16,6 +21,16 @@ export interface TenderTaskVO { */ taskName: string; + /** + * 招标文件名称 + */ + tenderDocumentName: string; + + /** + * 投标文件名称 + */ + bidDocumentName: string; + /** * 文档名称 */ @@ -28,10 +43,75 @@ export interface TenderTaskVO { /** * 当前状态 + */ + progressStatus: string; + + /** + * 创建时间 + */ + createTime: string; + + /** + * 创建人 + */ + createUser: string; + + /** + * 进度 + */ + progress: string; + + /** + * 文件是否已删除 + */ + delFile: string; + + /** + * 子任务列表 + */ + childrenTasks: TenderChildTaskVO[]; + + /** + * 任务耗时 + */ + taskDuration?: string; + + /** + * 任务类型 + */ + taskType?: string; +} + +export interface TenderChildTaskVO { + /** + * 任务ID + */ + id: string | number; + + /** + * 任务名称 + */ + taskName: string; + + /** + * 任务类型 + */ + taskType: string; + /** + * 任务耗时 + */ + taskDuration: string; + + /** + * 状态 */ progressStatus: string; + /** + * ossId + */ + ossId: string | number; } export interface TenderTaskForm extends BaseEntity { @@ -50,6 +130,16 @@ export interface TenderTaskForm extends BaseEntity { */ taskName?: string; + /** + * 招标文件名称 + */ + tenderDocumentName?: string; + + /** + * 投标文件名称 + */ + bidDocumentName?: string; + /** * 文档名称 */ @@ -62,14 +152,21 @@ export interface TenderTaskForm extends BaseEntity { /** * 当前状态 - */ progressStatus?: string; + /** + * 招标文件OSS ID + */ + tenderDocOssId?: string | number; + + /** + * 投标文件集OSS ID + */ + bidDocZipOssId?: string | number; } export interface TenderTaskQuery extends PageQuery { - /** * 模型所属行业 */ @@ -85,6 +182,16 @@ export interface TenderTaskQuery extends PageQuery { */ taskName?: string; + /** + * 招标文件名称 + */ + tenderDocumentName?: string; + + /** + * 投标文件名称 + */ + bidDocumentName?: string; + /** * 文档名称 */ @@ -97,12 +204,16 @@ export interface TenderTaskQuery extends PageQuery { /** * 当前状态 - */ progressStatus?: string; - /** - * 日期范围参数 - */ - params?: any; + /** + * 文件是否已删除 + */ + deleteFlag?: string; + + /** + * 日期范围参数 + */ + params?: any; } diff --git a/src/api/tenderReview/TenderTaskResults/index.ts b/src/api/tenderReview/TenderTaskResults/index.ts new file mode 100644 index 0000000..510eb8c --- /dev/null +++ b/src/api/tenderReview/TenderTaskResults/index.ts @@ -0,0 +1,192 @@ +import { defHttp } from '@/utils/http/axios'; +import { ID, IDS, commonExport } from '@/api/base'; +import { TenderTaskResultsVO, TenderTaskResultsForm, TenderTaskResultsQuery, TenderTaskResultDetailVO } from './model'; +import { message } from 'ant-design-vue'; + +interface DownloadOptions { + filename?: string; + mimeType?: string; +} + +/** + * 通用文件下载函数 + * @param url 下载地址 + * @param onError 错误处理回调 + * @returns Promise 下载是否成功 + */ +export async function useDownload( + url: string, + onError?: (error: any) => void +): Promise { + try { + const response = await defHttp.get( + { + url, + responseType: 'blob', + timeout: 60000, // 设置较长的超时时间 + }, + { + isReturnNativeResponse: true, + // 自定义错误处理 + errorMessageMode: 'none', + } + ); + + // 检查响应类型 + const contentType = response.headers['content-type']; + if (contentType && contentType.includes('application/json')) { + // 如果返回的是JSON(通常是错误信息),转换并抛出 + const reader = new FileReader(); + reader.onload = () => { + const error = JSON.parse(reader.result as string); + message.error(error.message || '下载失败'); + onError?.(error); + }; + reader.readAsText(response.data); + return false; + } + + // 获取文件名 + const contentDisposition = response.headers['content-disposition']; + let fileName = ''; + + if (contentDisposition) { + const matches = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/); + if (matches && matches[1]) { + fileName = decodeURIComponent(matches[1].replace(/['"]/g, '')); + } + } + + // 创建Blob对象 + const blob = new Blob([response.data], { + type: contentType || 'application/octet-stream' + }); + + if (window.navigator && (window.navigator as any).msSaveOrOpenBlob) { + // 针对IE的处理 + (window.navigator as any).msSaveOrOpenBlob(blob, fileName); + } else { + // 现代浏览器的处理 + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = fileName; + link.style.display = 'none'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + } + + return true; + } catch (error: any) { + console.error('下载失败:', error); + message.error(error.message || '下载失败,请稍后重试'); + onError?.(error); + return false; + } +} + +/** + * 查询招投标任务结果列表 + * @param params + * @returns + */ +export function TenderTaskResultsList(params?: TenderTaskResultsQuery) { + return defHttp.get({ url: '/tenderTask/tenderTaskResults/list', params }); +} + +/** + * 导出招投标任务结果列表 + * @param params + * @returns + */ +export function TenderTaskResultsExport(params?: TenderTaskResultsQuery) { + return commonExport('/tenderTask/tenderTaskResults/export', params ?? {}); +} + +/** + * 查询招投标任务结果详细 + * @param id id + * @returns + */ +export function TenderTaskResultsInfo(id: ID) { + return defHttp.get({ url: '/tenderTask/tenderTaskResults/' + id }); +} + +/** + * 根据任务id查询详细分类的招投标任务结果 + * @param taskId 任务ID + * @returns 详细的招投标任务结果列表 + */ +export function getDetailResultsByTaskId(taskId: ID) { + return defHttp.get({ url: '/tenderTask/tenderTaskResults/taskDetail/' + taskId }); +} + +/** + * 新增招投标任务结果 + * @param data + * @returns + */ +export function TenderTaskResultsAdd(data: TenderTaskResultsForm) { + return defHttp.postWithMsg({ url: '/tenderTask/tenderTaskResults', data }); +} + +/** + * 更新招投标任务结果 + * @param data + * @returns + */ +export function TenderTaskResultsUpdate(data: TenderTaskResultsForm) { + return defHttp.putWithMsg({ url: '/tenderTask/tenderTaskResults', data }); +} + +/** + * 删除招投标任务结果 + * @param id id + * @returns + */ +export function TenderTaskResultsRemove(id: ID | IDS) { + return defHttp.deleteWithMsg({ url: '/tenderTask/tenderTaskResults/' + id }); +} + +/** + * 更新招投标任务结果项的状态(已读/采纳) + * @param id 结果项ID + * @param field 字段名(isRead/isAdopted) + * @param value 值(0/1) + * @returns + */ +export function updateResultItemStatus(id: ID, field: 'isRead' | 'isAdopted', value: '0' | '1') { + return defHttp.putWithMsg({ + url: `/tenderTask/tenderTaskResults/updateResultItemStatus/${id}/${field}/${value}` + }); +} + +/** + * 下载招投标任务结果 + * @param id id + * @returns + */ +export function TenderTaskResultDownload(id: ID | IDS) { + return useDownload(`/tenderTask/tenderTaskResults/downloadResult/${id}`); +} + +/** + * 获取PDF文件流 + * @param taskId 任务ID + * @returns Promise + */ +export function getPdfStream(taskId: ID): Promise { + return defHttp.get( + { + url: `/tenderTask/tenderTaskResults/getPdfStream/${taskId}`, + responseType: 'blob', + timeout:600000 + }, + { + isReturnNativeResponse: true, + errorMessageMode: 'none', + } + ).then(response => response.data); +} \ No newline at end of file diff --git a/src/api/tenderReview/TenderTaskResults/model.ts b/src/api/tenderReview/TenderTaskResults/model.ts new file mode 100644 index 0000000..9f85441 --- /dev/null +++ b/src/api/tenderReview/TenderTaskResults/model.ts @@ -0,0 +1,99 @@ +import { BaseEntity, PageQuery } from '@/api/base'; + +export interface TenderTaskResultsVO { + /** + * 任务结果 + */ + result?: string; +} + +export interface TenderTaskResultDetailVO { + /** + * 分类名称 + */ + name: string; + /** + * 结果列表 + */ + results: { + /** + * 唯一标识ID + */ + id?: string; + /** + * 序号 + */ + serialNumber?: number; + /** + * 问题点名称 + */ + issueName?: string; + /** + * 原文 + */ + originalText?: string; + /** + * 比对原文 + */ + comparedText?: string; + /** + * 修改后的内容 + */ + modifiedContent?: string; + /** + * 展示修改情况 + */ + modificationDisplay?: string; + /** + * 存在的问题 + */ + existingIssues?: string; + /** + * 审查依据 + */ + reviewBasis?: { + /** + * 审查内容 + */ + reviewContent?: string; + /** + * 审查点 + */ + reviewPoints?: string[]; + }; + /** + * 是否已读 + */ + isRead?: string; + /** + * 是否采纳 + */ + isAdopted?: string; + }[]; +} + +export interface TenderTaskResultsForm extends BaseEntity { +} + +export interface TenderTaskResultsQuery extends PageQuery { + + /** + * id + */ + id?: string | number; + + /** + * 任务id + */ + tenderTaskId?: string | number; + + /** + * 任务结果 + */ + result?: string; + + /** + * 日期范围参数 + */ + params?: any; +} \ No newline at end of file diff --git a/src/components/UniversalResultDrawer.vue b/src/components/UniversalResultDrawer.vue index fb69e9d..021f3a4 100644 --- a/src/components/UniversalResultDrawer.vue +++ b/src/components/UniversalResultDrawer.vue @@ -465,10 +465,10 @@ return []; } - // 在tabs模式下,跳过第一个"全部"数据,只显示实际的分类 - const dataToProcess = props.taskResultDetail.slice(1); // 跳过第一个数据 + // 直接使用所有数据生成标签页 + const dataToProcess = props.taskResultDetail; - // 根据剩余数据生成标签页 + // 根据数据生成标签页 return dataToProcess.map((category, index) => { let label = `${category.name} (${category.results?.length || 0})`; @@ -480,7 +480,7 @@ return { key: category.name, // 使用数据的name作为key label: label, - dataIndex: index + 1, // 记录在原数据中的索引(+1因为跳过了第一个) + dataIndex: index, // 记录在原数据中的索引 name: category.name }; }); @@ -492,8 +492,8 @@ return []; } - // 在tabs模式下,跳过第一个"全部"数据,只从实际分类中查找 - const dataToSearch = props.taskResultDetail.slice(1); // 跳过第一个数据 + // 直接从所有数据中查找对应的分类 + const dataToSearch = props.taskResultDetail; // 根据当前选中的tab name,找到对应的数据类别 const targetCategory = dataToSearch.find(category => category.name === currentTab.value); diff --git a/src/configs/contractTaskConfigs.ts b/src/configs/contractTaskConfigs.ts index e522012..a41213a 100644 --- a/src/configs/contractTaskConfigs.ts +++ b/src/configs/contractTaskConfigs.ts @@ -4,16 +4,15 @@ import type { TaskConfig } from './taskConfigTypes'; // 合同审核任务配置 export const CONTRACT_TASK_CONFIGS: Record = { - // 合同审核(多标签模式) - "contractReview": { - taskType: "contractReview", - name: "合同审核", + // 实质性审查 + "contractSubstantiveReview": { + taskType: "contractSubstantiveReview", + name: "实质性审查", mode: "tabs", pdfConfig: { - layout: "split", + layout: "single", sources: [ - { id: "contract", title: "合同文件", apiField: "id" }, - { id: "bid", title: "招标文件", apiField: "id" } + { id: "contract", title: "合同文件", apiField: "id" } ] }, tabs: [ @@ -58,7 +57,22 @@ export const CONTRACT_TASK_CONFIGS: Record = { } } ] - }, + } + ] + }, + + // 合规性审查 + "contractComplianceReview": { + taskType: "contractComplianceReview", + name: "合规性审查", + mode: "tabs", + pdfConfig: { + layout: "single", + sources: [ + { id: "contract", title: "合同文件", apiField: "id" } + ] + }, + tabs: [ { key: "compliance", label: "合规性审查", @@ -77,24 +91,6 @@ export const CONTRACT_TASK_CONFIGS: Record = { 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: '法规依据', @@ -109,7 +105,23 @@ export const CONTRACT_TASK_CONFIGS: Record = { } } ] - }, + } + ] + }, + + // 一致性审查 + "contractConsistencyReview": { + taskType: "contractConsistencyReview", + name: "一致性审查", + mode: "tabs", + pdfConfig: { + layout: "split", + sources: [ + { id: "contract", title: "合同文件", apiField: "id" }, + { id: "bid", title: "招标文件", apiField: "id" } + ] + }, + tabs: [ { key: "consistency", label: "一致性审查", @@ -136,22 +148,61 @@ export const CONTRACT_TASK_CONFIGS: Record = { displayType: 'markdown', pdfSource: 'bid', required: true - }, - // { - // field: 'modifiedContent', - // title: '修改建议', - // dataType: 'string', - // displayType: 'markdown', - // showComparison: true, - // comparisonConfig: { - // comparisonField: 'modificationDisplay', - // comparisonTitle: '修改情况展示', - // showButton: 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: ':' + } + } + ] } }; diff --git a/src/configs/documentTaskConfigs.ts b/src/configs/documentTaskConfigs.ts index 8e11ce5..f828bc5 100644 --- a/src/configs/documentTaskConfigs.ts +++ b/src/configs/documentTaskConfigs.ts @@ -215,7 +215,65 @@ export const DOCUMENT_TASK_CONFIGS: Record = { } } ] - } + }, + // 全文重复检查 + "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': (value) => Array.isArray(value) ? value.join('\n') : value + } + } + }, + { + field: 'modifiedContent', + title: '修改建议', + dataType: 'string', + displayType: 'markdown', + pdfSource: 'document' + }, + { + field: 'reviewBasis', + title: '风险等级(红色预警:直接废标项,橙色风险:可能引发投诉项)', + dataType: 'json', + displayType: 'markdown', + jsonConfig: { + extractFields: ['risk_level'], + separator: ':' + } + } + ] + }, }; // 获取文档任务配置 diff --git a/src/configs/taskConfigs.ts b/src/configs/taskConfigs.ts index 2aba4ef..183cfcb 100644 --- a/src/configs/taskConfigs.ts +++ b/src/configs/taskConfigs.ts @@ -3,11 +3,13 @@ import type { TaskConfig, FieldConfig } from './taskConfigTypes'; import { DOCUMENT_TASK_CONFIGS, getDocumentTaskConfig } from './documentTaskConfigs'; import { CONTRACT_TASK_CONFIGS, getContractTaskConfig } from './contractTaskConfigs'; +import { TENDER_TASK_CONFIGS, getTenderTaskConfig } from './tenderTaskConfigs'; // 合并所有任务配置 const ALL_TASK_CONFIGS: Record = { ...DOCUMENT_TASK_CONFIGS, - ...CONTRACT_TASK_CONFIGS + ...CONTRACT_TASK_CONFIGS, + ...TENDER_TASK_CONFIGS }; // 统一的获取任务配置函数 @@ -20,6 +22,10 @@ export function getTaskConfig(taskType: string): TaskConfig | null { config = getContractTaskConfig(taskType); if (config) return config; + // 最后尝试从招投标任务配置中获取 + config = getTenderTaskConfig(taskType); + if (config) return config; + return null; } diff --git a/src/configs/tenderTaskConfigs.ts b/src/configs/tenderTaskConfigs.ts new file mode 100644 index 0000000..afaf860 --- /dev/null +++ b/src/configs/tenderTaskConfigs.ts @@ -0,0 +1,156 @@ +// 招投标审核任务配置文件 + +import type { TaskConfig } from './taskConfigTypes'; + +// 招投标审核任务配置 +export const TENDER_TASK_CONFIGS: Record = { + // 招投标文件审核(多标签模式) + "sjjbidAnalysis": { + taskType: "sjjbidAnalysis", + name: "招投标文件审核", + mode: "tabs", + pdfConfig: { + layout: "split", + sources: [ + { id: "bid", title: "投标文件", apiField: "id" }, + { id: "tender", title: "招标文件", apiField: "id" } + ] + }, + tabs: [ + { + key: "两两相似", + label: "两两相似", + pdfConfig: { + layout: "split", + sources: [ + { id: "bid", title: "投标文件", apiField: "id" }, + { id: "tender", title: "招标文件", apiField: "id" } + ] + }, + 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: 'modificationDisplay', + title: '相似情况分析', + dataType: 'string', + displayType: 'markdown' + }, + { + field: 'reviewBasis', + title: '分析详情', + dataType: 'json', + displayType: 'markdown', + jsonConfig: { + extractFields: ['review_content', 'review_points'], + separator: ':' + } + } + ] + }, + { + key: "错别字", + label: "错别字", + pdfConfig: { + layout: "split", + sources: [ + { id: "bid", title: "投标文件", apiField: "id" }, + { id: "tender", title: "招标文件", apiField: "id" } + ] + }, + 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: 'modificationDisplay', + title: '错误分析与修改建议', + dataType: 'string', + displayType: 'markdown' + }, + { + field: 'reviewBasis', + title: '错误详情', + dataType: 'json', + displayType: 'markdown', + jsonConfig: { + extractFields: ['review_content', 'review_points'], + separator: ':', + fieldProcessors: { + 'review_points': (value) => Array.isArray(value) ? value.join('\n') : value + } + } + } + ] + }, + { + key: "多家相同内容", + label: "多家相同内容", + pdfConfig: { + layout: "single", + sources: [ + { id: "bid", title: "投标文件", apiField: "id" } + ] + }, + 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': (value) => Array.isArray(value) ? value.join('\n\n') : value + } + } + } + ] + } + ] + } +}; + +// 获取招投标任务配置 +export function getTenderTaskConfig(taskType: string): TaskConfig | null { + return TENDER_TASK_CONFIGS[taskType] || null; +} + +// 获取所有招投标任务配置 +export function getAllTenderTaskConfigs(): TaskConfig[] { + return Object.values(TENDER_TASK_CONFIGS); +} \ No newline at end of file diff --git a/src/enums/taskEnum.ts b/src/enums/taskEnum.ts index 0118fe9..447f3bc 100644 --- a/src/enums/taskEnum.ts +++ b/src/enums/taskEnum.ts @@ -74,11 +74,23 @@ export const SchemeTask = { // 合同审核子任务 export const ContractTask = { - CONTRACT_REVIEW: { - value: 'contractReview', - label: '合同审核', - icon: 'fluent-mdl2:document-approval', - color: '#722ED1' + CONTRACT_SUBSTANTIVE_REVIEW: { + value: 'contractSubstantiveReview', + label: '实质性审查', + icon: 'mdi:file-document-check-outline', + color: '#E6A23C' + }, + CONTRACT_COMPLIANCE_REVIEW: { + value: 'contractComplianceReview', + label: '合规性审查', + icon: 'mdi:file-document-check', + color: '#67C23A' + }, + CONTRACT_CONSISTENCY_REVIEW: { + value: 'contractConsistencyReview', + label: '一致性审查', + icon: 'mdi:file-compare', + color: '#409EFF' } } as const; diff --git a/src/views/contractReview/ContractualTasks/ContractualResultDetailDrawer.vue b/src/views/contractReview/ContractualTasks/ContractualResultDetailDrawer.vue index 7710b88..cbd1338 100644 --- a/src/views/contractReview/ContractualTasks/ContractualResultDetailDrawer.vue +++ b/src/views/contractReview/ContractualTasks/ContractualResultDetailDrawer.vue @@ -3,7 +3,7 @@ :visible="visible" :title="title" :width="width" - :taskType="'contractReview'" + :taskType="computedTaskType" :taskResultDetail="taskResultDetail as any" :taskInfo="taskInfo" :getPdfStream="getPdfStream" @@ -14,7 +14,7 @@ diff --git a/src/views/tenderReview/TenderTask/TenderDocsDrawer.vue b/src/views/tenderReview/TenderTask/TenderDocsDrawer.vue new file mode 100644 index 0000000..07007f2 --- /dev/null +++ b/src/views/tenderReview/TenderTask/TenderDocsDrawer.vue @@ -0,0 +1,55 @@ + + + + + \ No newline at end of file diff --git a/src/views/tenderReview/TenderTask/TenderResultDetailDrawer.vue b/src/views/tenderReview/TenderTask/TenderResultDetailDrawer.vue new file mode 100644 index 0000000..2b2a123 --- /dev/null +++ b/src/views/tenderReview/TenderTask/TenderResultDetailDrawer.vue @@ -0,0 +1,56 @@ + + + \ No newline at end of file diff --git a/src/views/tenderReview/TenderTask/TenderTask.data.ts b/src/views/tenderReview/TenderTask/TenderTask.data.ts index f71228b..c34f51d 100644 --- a/src/views/tenderReview/TenderTask/TenderTask.data.ts +++ b/src/views/tenderReview/TenderTask/TenderTask.data.ts @@ -2,74 +2,64 @@ import { BasicColumn } from '@/components/Table'; import { FormSchema } from '@/components/Form'; import { getDictOptions } from '@/utils/dict'; import { useRender } from '@/hooks/component/useRender'; -import { uploadDocument } from '@/api/documentReview/DocumentTasks'; -import { useUserStore } from '@/store/modules/user'; -import { RoleEnum } from '@/enums/roleEnum'; +import { uploadTenderDocument } from '@/api/tenderReview/TenderTask'; -const { roleList } = useUserStore(); export const formSchemas: FormSchema[] = [ - // { - // label: '模型所属行业', - // field: 'taskIndustry', - // component: 'Select', - // componentProps: { - // options: getDictOptions('model_industry') - // }, - // }, - // { - // label: '模型所属区域', - // field: 'taskRegion', - // component: 'Select', - // componentProps: { - // options: getDictOptions('model_region') - // }, - // }, { label: '任务名称', field: 'taskNameList', component: 'Select', componentProps: { - options: getDictOptions('tender_review'), + options: [{ + label: '招标文件文本审核', + value: 'sjjbidAnalysis', + }, + { + label: '招标文件图片审核', + value: 'sjjbidImageAnalysis', + }], mode: 'multiple', - defaultValue:"tenderSummary" }, }, { - label: '文档名称', - field: 'documentName', + label: '招标文件名称', + field: 'tenderDocumentName', component: 'Input', }, { - label: '状态', + label: '投标文件名称', + field: 'bidDocumentName', + component: 'Input', + }, + { + label: '进度状态', field: 'progressStatus', component: 'Select', componentProps: { - options: getDictOptions('document_task_status'), + options: getDictOptions('document_task_status') + }, + }, + { + label: '文件是否已删除', + field: 'deleteFlag', + component: 'Select', + componentProps: { + options: getDictOptions('sys_yes_no') }, }, ]; const { renderDict } = useRender(); -export const columns: BasicColumn[] = [ - { - title: '任务名称', - dataIndex: 'taskName', - customRender: ({ value }) => renderDict(value, 'tender_review'), - }, - { - title: '文档名称', - dataIndex: 'documentName', - }, +// 父表列配置 +export const columns: BasicColumn[] = [ { - title: '模型所属区域', - dataIndex: 'taskRegion', - customRender: ({ value }) => renderDict(value, 'model_region'), + title: '招标文件名称', + dataIndex: 'tenderDocumentName', }, { - title: '模型所属行业', - dataIndex: 'taskIndustry', - customRender: ({ value }) => renderDict(value, 'model_industry'), + title: '投标文件名称', + dataIndex: 'bidDocumentName', }, { title: '上传时间', @@ -78,7 +68,32 @@ export const columns: BasicColumn[] = [ { title: '提交人', dataIndex: 'createUser', - auth: 'documentReview:DocumentTasks:tableShow', + }, + { + title: '进度', + dataIndex: 'progress', + }, +]; + +// 子表列配置 - 与文档审核任务保持一致 +export const childColumns: BasicColumn[] = [ + { + title: '任务名称', + dataIndex: 'taskName', + customRender: ({ value, record }) => { + if (value === 'sjjbidAnalysis') { + return '招标文件文本审核'; + } else if (value === 'sjjbidImageAnalysis') { + return '招标文件图片审核'; + } + return value; + }, + }, + { + title: '任务类型', + dataIndex: 'taskType', + ifShow: false, + customRender: ({ value }) => renderDict(value, 'task_type'), }, { title: '任务耗时', @@ -93,59 +108,47 @@ export const columns: BasicColumn[] = [ export const modalSchemas: FormSchema[] = [ { - label: '模型所属区域', - field: 'taskRegion', - required: true, - component: 'Select', - // componentProps: { - // options: taskRegionPermission(), - // defaultValue:"normal", - // }, - componentProps: () => { - const isSuperAdmin = roleList.includes(RoleEnum.SUPER_ADMIN); - let options = getDictOptions('model_region'); - if (!isSuperAdmin) { - // 如果不是超级管理员,移除 label 带有 '#' 的项 - options = options.filter((option) => !option.label.includes('#')); - } - return { - options: options, - defaultValue: 'normal', - }; - }, - }, - { - label: '模型所属行业', - field: 'taskIndustry', + label: '任务名称', + field: 'taskNameList', required: true, component: 'Select', componentProps: { - options: getDictOptions('model_industry'), - defaultValue: 'normal', + options: [{ + label: '招标文件文本审核', + value: 'sjjbidAnalysis', + }, + { + label: '招标文件图片审核', + value: 'sjjbidImageAnalysis', + }], + mode: 'multiple', }, }, { - label: '任务名称', - field: 'taskNameList', - required: true, - component: 'Select', + label: '招标文件', + field: 'tenderDocOssId', + component: 'Upload', componentProps: { - options: getDictOptions('tender_review') , - mode: 'multiple', + accept: ['.docx', '.pdf'], + maxSize: 1000, + multiple: false, + resultField: 'ossId', + // api: uploadTenderDocument, + beforeUploadPrompt:"严禁在本互联网非涉密平台处理、传输国家秘密。请再次确认您上传的文件资料不涉及国家秘密。" }, }, { - label: '文档名称', - field: 'ossId', + label: '投标文件集', + field: 'bidDocZipOssId', required: true, component: 'Upload', componentProps: { - accept: ['.docx', '.doc', '.wps'], - maxSize: 500, + accept: ['.zip'], + maxSize: 1000, multiple: false, resultField: 'ossId', - api: uploadDocument, + // api: uploadTenderDocument, beforeUploadPrompt:"严禁在本互联网非涉密平台处理、传输国家秘密。请再次确认您上传的文件资料不涉及国家秘密。" }, }, -]; +]; \ No newline at end of file diff --git a/src/views/tenderReview/TenderTask/TenderTaskModal.vue b/src/views/tenderReview/TenderTask/TenderTaskModal.vue index bd1f4f1..b5c611f 100644 --- a/src/views/tenderReview/TenderTask/TenderTaskModal.vue +++ b/src/views/tenderReview/TenderTask/TenderTaskModal.vue @@ -17,7 +17,6 @@ import { computed, ref, unref } from 'vue'; import { TenderTaskInfo, TenderTaskAdd, TenderTaskUpdate } from '@/api/tenderReview/TenderTask'; import { modalSchemas } from './TenderTask.data'; - import { ModelUserPromptssettingInfoByUserId } from '@/api/modelConfiguration/ModelUserPromptssetting/index'; defineOptions({ name: 'TenderTaskModal' }); @@ -31,8 +30,6 @@ const [registerInnerModal, { modalLoading, closeModal }] = useModalInner( async (data: { record?: Recordable; update: boolean }) => { modalLoading(true); - const settings = await ModelUserPromptssettingInfoByUserId(); - await setFieldsValue(settings); const { record, update } = data; isUpdate.value = update; if (update && record) { @@ -53,10 +50,16 @@ try { modalLoading(true); const data = await validate(); - data['ossId'] = data['ossId'][0]; + if (unref(isUpdate)) { await TenderTaskUpdate(data); } else { + if(data['tenderDocOssId']){ + data['tenderDocOssId'] = data['tenderDocOssId'][0]; + } + if(data['bidDocZipOssId']){ + data['bidDocZipOssId'] = data['bidDocZipOssId'][0]; + } await TenderTaskAdd(data); } emit('reload'); diff --git a/src/views/tenderReview/TenderTask/index.vue b/src/views/tenderReview/TenderTask/index.vue index f163a8c..cf8ad13 100644 --- a/src/views/tenderReview/TenderTask/index.vue +++ b/src/views/tenderReview/TenderTask/index.vue @@ -1,28 +1,37 @@ @@ -102,19 +164,29 @@ TenderTaskList, TenderTaskExport, TenderTaskRemove, + TenderTaskStop, + TenderTaskDeleteFile, } from '@/api/tenderReview/TenderTask'; import { downloadExcel } from '@/utils/file/download'; import { useModal } from '@/components/Modal'; import TenderTaskModal from './TenderTaskModal.vue'; - import { formSchemas, columns } from './TenderTask.data'; + import { formSchemas, columns, childColumns } from './TenderTask.data'; import { IconEnum } from '@/enums/appEnum'; - import DocsDrawer from '@/views/documentReview/DocumentTasks/DocsDrawer.vue'; + import TenderDocsDrawer from './TenderDocsDrawer.vue'; import { useDrawer } from '@/components/Drawer'; - import { DocumentTasksStop } from '@/api/documentReview/DocumentTasks'; import { - DocumentTaskResultDownload, - } from '@/api/documentReview/DocumentTaskResults'; + TenderTaskResultDownload, + getDetailResultsByTaskId, + } from '@/api/tenderReview/TenderTaskResults'; + import TenderResultDetailDrawer from './TenderResultDetailDrawer.vue'; + import { onMounted, ref } from 'vue'; + import { TenderTaskResultDetailVO } from '@/api/tenderReview/TenderTaskResults/model'; + const [registerDrawer, { openDrawer }] = useDrawer(); + const resultDetailDrawerVisible = ref(false); + const childTableData = ref([]); + const taskResultDetail = ref([]); + const currentTaskInfo = ref({}); defineOptions({ name: 'TenderTask' }); @@ -125,7 +197,9 @@ title: '招标审核任务列表', api: TenderTaskList, showIndexColumn: false, + clickToRowSelect: false, rowKey: 'id', + expandRowByClick: false, useSearchForm: true, formConfig: { schemas: formSchemas, @@ -145,32 +219,91 @@ }, }); + const [registerChildTable, { setProps: setChildProps }] = useTable({ + size: 'small', //紧凑型表格 + api: getchildTableData, + columns: childColumns, + rowKey: 'id', + useSearchForm: false, + showIndexColumn: false, + showTableSetting: false, + pagination: false, + maxHeight: 50, + actionColumn: { + width: 200, + title: '操作', + key: 'action', + fixed: 'right', + }, + }); + const [registerModal, { openModal }] = useModal(); + onMounted(async () => { + }); + + const handleResultDetailDrawerClose = () => { + resultDetailDrawerVisible.value = false; + }; + async function handleDetail(record: Recordable) { - // try { - // let res = await DocumentTaskResultsInfoByTaskId(record.id); - - // openDrawer(true, { value: res.result, type: 'markdown' }); - // console.log('res', res); - // } catch (ex) { - // openDrawer(true, { value: '加载失败,请刷新页面', type: 'markdown' }); - // } - //根据record.id查询结果详情 + try { + const detailRes = await getDetailResultsByTaskId(record.id); + if (detailRes && detailRes.length > 0) { + taskResultDetail.value = detailRes; + currentTaskInfo.value = record; + resultDetailDrawerVisible.value = true; + return; + } + } catch (detailEx) { + console.error('获取详细结果失败', detailEx); + } } async function handleStop(record: Recordable) { - await DocumentTasksStop(record.id); + await TenderTaskStop(record.id); await reload(); } function handleAdd() { openModal(true, { update: false }); } - async function handleDownload(record: Recordable) { - await DocumentTaskResultDownload([record.id]); + + function tableExpand(expanded, record) { + if (expanded) { + childTableData.value = record.childrenTasks; + console.log('expanded, record', expanded, record); + } + } + + async function handleDeleteFile(record: Recordable) { + await TenderTaskDeleteFile(record.childrenTasks[0].ossId); await reload(); } + + // 修改后的 getchildTableData 函数 + function getchildTableData() { + console.log('childTableData', childTableData.value); + const height = 50 * childTableData.value.length; + setChildProps({ maxHeight: height }); + + // 返回一个 Promise 对象,包含预期的数据结构 + return Promise.resolve(childTableData.value); + } + + async function handleDownload(record: Recordable) { + if (record.childrenTasks?.length > 1) { + await TenderTaskResultDownload(record.childrenTasks.map((item) => item.id)); + await reload(); + } else if (record.childrenTasks?.length == 1) { + await TenderTaskResultDownload([record.childrenTasks[0].id]); + await reload(); + } else { + await TenderTaskResultDownload([record.id]); + await reload(); + } + } + async function handleDelete(record: Recordable) { await TenderTaskRemove([record.id]); await reload(); diff --git a/src/views/tenderReview/TenderTask/tender-caozuomiaoshu.md b/src/views/tenderReview/TenderTask/tender-caozuomiaoshu.md new file mode 100644 index 0000000..1279517 --- /dev/null +++ b/src/views/tenderReview/TenderTask/tender-caozuomiaoshu.md @@ -0,0 +1,55 @@ +# 招投标文件审核操作说明 + +## 🎯 功能概述 + +招投标文件审核系统帮助您快速对招标文件和投标文件进行智能分析和审核,提高工作效率。 + +## 📋 操作步骤 + +### 1. 新增审核任务 + +1. **点击"新增"按钮** +2. **选择任务类型**: + - 招标文件文本审核 + - 招标文件图片审核 +3. **上传文件**: + - 招标文件:支持.docx、.pdf格式 + - 投标文件集:支持.zip格式 +4. **提交任务** + +### 2. 查看任务进度 + +- 在任务列表中可以实时查看任务进度 +- 任务状态: + - 🟡 **待处理**:任务已提交,等待处理 + - 🔵 **处理中**:任务正在执行 + - 🟢 **已完成**:任务处理完成 + - 🔴 **已终止**:任务被手动终止 + +### 3. 下载结果 + +- 任务完成后,点击"下载"按钮获取审核结果 +- 支持批量下载多个任务结果 + +### 4. 查看详细结果 + +- 点击"详情"按钮查看详细的审核报告 +- 包含问题点分析、修改建议等 + +## ⚠️ 注意事项 + +1. **文件大小限制**:单个文件不超过1GB +2. **文件格式**:请确保上传正确的文件格式 +3. **安全提醒**:严禁上传涉及国家秘密的文件 +4. **任务管理**:可随时终止正在处理的任务 + +## 🔧 常见问题 + +**Q: 任务一直处于"处理中"状态怎么办?** +A: 请检查网络连接,如果问题持续存在,可以终止任务后重新提交。 + +**Q: 支持哪些文件格式?** +A: 招标文件支持.docx、.pdf格式;投标文件需要压缩为.zip格式上传。 + +**Q: 可以同时处理多个任务吗?** +A: 是的,系统支持并发处理多个任务。 \ No newline at end of file