From 5c6058609225588e801cf2e2ac7229596b06d712 Mon Sep 17 00:00:00 2001 From: LHD_HY <2872546851@qq.com> Date: Fri, 5 Dec 2025 09:12:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=86=E5=8F=B2=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysisRuleSet/analysisRuleSet.js | 8 + src/api/template/templateInfo/templateInfo.js | 19 + src/router/index.js | 15 + .../enterpriseLibrary/qualification/index.vue | 1 + .../components/AnalysisLabelItem.vue | 6 +- .../child/AnalysisLabelItemForm.vue | 1 - .../templateInfo/components/AnalysisRule.vue | 65 +- .../templateInfo/components/TemplateForm.vue | 47 +- .../components/TemplateHistory.vue | 179 +++++ .../components/child/AnalysisLabel.vue | 4 +- .../components/child/BasicInfo.vue | 5 +- .../components/child/ProjectFile.vue | 56 +- .../components/child/RuleDetailDrawer.vue | 623 +++++++++++++++++ .../components/child/SectionFile.vue | 54 +- .../components/child/TemplateInfo.vue | 127 ++++ .../components/child/VersionContent.vue | 632 ++++++++++++++++++ src/views/template/templateInfo/config.js | 2 +- src/views/template/templateInfo/index.vue | 187 ++++-- 18 files changed, 1931 insertions(+), 100 deletions(-) create mode 100644 src/views/template/templateInfo/components/TemplateHistory.vue create mode 100644 src/views/template/templateInfo/components/child/RuleDetailDrawer.vue create mode 100644 src/views/template/templateInfo/components/child/TemplateInfo.vue create mode 100644 src/views/template/templateInfo/components/child/VersionContent.vue diff --git a/src/api/template/analysisRuleSet/analysisRuleSet.js b/src/api/template/analysisRuleSet/analysisRuleSet.js index 4db2df3..64e248b 100644 --- a/src/api/template/analysisRuleSet/analysisRuleSet.js +++ b/src/api/template/analysisRuleSet/analysisRuleSet.js @@ -13,6 +13,14 @@ export function listAnalysisRuleSet(query) { }) } +export function listAnalysisRuleSetHistory(query) { + return request({ + url: '/smartBid/analysisRuleSet/list/history', + method: 'get', + params: query + }) +} + // 2. 查询解析规则配置详情(携带解析规则ID,包含关联定位查询规则信息) export function getAnalysisRuleSetDetail(params) { return request({ diff --git a/src/api/template/templateInfo/templateInfo.js b/src/api/template/templateInfo/templateInfo.js index 51a0cd6..42e3143 100644 --- a/src/api/template/templateInfo/templateInfo.js +++ b/src/api/template/templateInfo/templateInfo.js @@ -57,3 +57,22 @@ export function checkTemplateNameUnique(params) { params: params }) } + +// 7. 发布模板 +export function publishTemplate(templateId) { + return request({ + url: `/smartBid/templateInfo/publish/${templateId}`, + method: 'post' + }) +} + +// 8. 查询模板历史版本列表 +export function getTemplateHistoryVersions(templateId) { + return request({ + url: '/smartBid/templateInfo/history/versions', + method: 'get', + params: { + templateId: templateId + } + }) +} diff --git a/src/router/index.js b/src/router/index.js index d9f2fe7..7ff6ebd 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -681,6 +681,21 @@ export const dynamicRoutes = [ } ] }, + + { + path: '/templateHistory', + component: Layout, + hidden: true, + permissions: ['analysis:rule:list'], + children: [ + { + path: 'index', + component: () => import('@/views/template/templateInfo/components/TemplateHistory'), + name: 'TemplateHistory', + meta: { title: '模板历史详情', activeMenu: '/template', noCache: true } + } + ] + }, ] // 防止连续点击多次路由报错 diff --git a/src/views/enterpriseLibrary/qualification/index.vue b/src/views/enterpriseLibrary/qualification/index.vue index 4491cdc..48b4a70 100644 --- a/src/views/enterpriseLibrary/qualification/index.vue +++ b/src/views/enterpriseLibrary/qualification/index.vue @@ -160,6 +160,7 @@ size="40%" :before-close="handleDetailDrawerClose" custom-class="custom-qualification-drawer qualification-drawer" + :append-to-body="false" > span:first-child { + font-size: 15px !important; + } + .tree-node-actions { opacity: 0; transition: opacity 0.3s; diff --git a/src/views/template/analysisLabel/components/child/AnalysisLabelItemForm.vue b/src/views/template/analysisLabel/components/child/AnalysisLabelItemForm.vue index 92ef416..fc61ef4 100644 --- a/src/views/template/analysisLabel/components/child/AnalysisLabelItemForm.vue +++ b/src/views/template/analysisLabel/components/child/AnalysisLabelItemForm.vue @@ -74,7 +74,6 @@ maxlength="128" clearable style="width:100%" - :disabled="type === 'edit'" /> diff --git a/src/views/template/templateInfo/components/AnalysisRule.vue b/src/views/template/templateInfo/components/AnalysisRule.vue index 95a956b..ee7b60a 100644 --- a/src/views/template/templateInfo/components/AnalysisRule.vue +++ b/src/views/template/templateInfo/components/AnalysisRule.vue @@ -76,6 +76,9 @@
-
@@ -310,7 +312,7 @@ type="textarea" rows="15" placeholder="请输入提示词内容" - style="width: 100%;" + style="height: 100%;" >
@@ -447,6 +449,9 @@ export default { this.templateName = res.data.templateName || ''; const compositionList = res.data.compositionList || []; + console.log('detail',res.data) + + // 去重处理 const uniqueSources = []; const seenIds = new Set(); @@ -476,7 +481,7 @@ export default { // 加载树形结构数据 loadTreeData() { const allItems = []; // 用于存储所有页的数据 - const pageSize = 10; // 每页数量,根据后端接口定义调整 + const pageSize = 1000; // 每页数量,根据后端接口定义调整 let currentPage = 1; // 当前页码 // 定义一个递归函数来获取每一页的数据 @@ -1120,6 +1125,8 @@ export default { width: 100%; height: 100%; // 确保内容充满节点行高 + > span:first-child {font-size: 15px !important;} + .tree-node-actions { opacity: 0; transition: opacity 0.3s; @@ -1160,6 +1167,56 @@ export default { box-sizing: border-box; } +.rule-config-container { + /* 使用 ::v-deep 穿透 scoped 样式 */ + ::v-deep .el-textarea { + /* 确保 textarea 容器占满可用宽度 */ + height: 100%; + width: 100%; + + &__inner { + /* 1. 基础文本美化 */ + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", "PingFang SC", "Microsoft YaHei", sans-serif; + font-size: 15px; /* slightly larger font for better readability */ + line-height: 1.7; /* 增加行高,提升可读性 */ + letter-spacing: 0.5px; /* 增加字间距,让文字不拥挤 */ + color: #333; /* 文本颜色 */ + + /* 2. 容器样式美化 */ + background-color: #ffffff; + border: 1px solid #e0e6ed; /* 更柔和的边框色 */ + border-radius: 8px; /* 圆润的 corners */ + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); /* subtle outer shadow */ + + /* 3. 内部 Padding */ + padding: 16px; /* 上下左右都有 padding */ + + /* 4. 交互与过渡效果 */ + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* 平滑过渡 */ + + /* Hover 状态 */ + &:hover { + border-color: #c9d2dc; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08); + } + + /* Focus 状态 (这是最重要的交互反馈) */ + &:focus { + outline: none; + border-color: #409EFF; /* 高亮边框色,与主题色一致 */ + box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.15); /* 聚焦发光效果 */ + } + } + } + + /* 5. 美化 Placeholder */ + ::v-deep .el-textarea__inner::placeholder { + color: #b4bcc2; /* 更淡的占位符颜色 */ + font-style: italic; /* 斜体,增加设计感 */ + font-size: 14px; + } +} + .template-info { margin-bottom: 20px; padding-bottom: 10px; @@ -1179,7 +1236,7 @@ export default { } ::v-deep .el-textarea__inner { - min-height: 100px; + min-height: 100%; } } diff --git a/src/views/template/templateInfo/components/TemplateForm.vue b/src/views/template/templateInfo/components/TemplateForm.vue index 43b57dc..7059ffc 100644 --- a/src/views/template/templateInfo/components/TemplateForm.vue +++ b/src/views/template/templateInfo/components/TemplateForm.vue @@ -136,21 +136,24 @@ export default { this.$refs.analysisLabelRef.validate() ]); - console.log('ProjectFileData',projectFileData) + console.log('ProjectFileData', projectFileData) // 2. 初始化提交数据 const formData = { ...basicData, ...analysisLabelData, templateId: this.type === 'edit' ? this.templateId : null, - useState: '0', + useState: '1', publishedStatus: '1', files: [], // 统一存储所有待上传/已上传的文件 - delFiles: [] // 统一存储所有需要删除的文件路径 + delFiles: [], // 统一存储所有需要删除的文件路径 + delComposition: [] // 新增:存储需要删除的compositionId列表 }; // 3. 处理项目文件 (ProjectFile) - projectFileData.forEach(tab => { + // 适配子组件返回的新格式 { formData: [], delComposition: [] } + const projectFormData = projectFileData.formData || projectFileData; + projectFormData.forEach(tab => { // 3.1 处理上传的文件,为每个文件添加 compositionType if (tab.fileList && tab.fileList.length > 0) { const processedFiles = tab.fileList.map(file => { @@ -169,11 +172,10 @@ export default { compositionName: file.compositionName, compositionFileType: file.compositionFileType, compositionType: tab.compositionType, // <-- 核心:添加 Tab 的 compositionType + compositionId: tab.compositionId // 新增:携带compositionId }; }).filter(Boolean); // 过滤掉前面 map 中返回的 null 值 - // console.log('processedFiles', ...processedFiles); // 展开语法在文件多时可能会卡,建议直接打印数组 - console.log('processedFiles', processedFiles); formData.files.push(...processedFiles); } @@ -183,8 +185,15 @@ export default { } }); -// 4. 处理标段/标包文件 (SectionFile) - sectionFileData.forEach(tab => { + // 新增:收集项目文件的delComposition + if (projectFileData.delComposition && projectFileData.delComposition.length > 0) { + formData.delComposition.push(...projectFileData.delComposition); + } + + // 4. 处理标段/标包文件 (SectionFile) + // 适配子组件返回的新格式 { formData: [], delComposition: [] } + const sectionFormData = sectionFileData.formData || sectionFileData; + sectionFormData.forEach(tab => { // 4.1 处理上传的文件,为每个文件添加 compositionType if (tab.fileList && tab.fileList.length > 0) { const processedFiles = tab.fileList.map(file => { @@ -202,10 +211,10 @@ export default { compositionName: file.compositionName, compositionFileType: file.compositionFileType, compositionType: tab.compositionType, // <-- 核心:添加 Tab 的 compositionType + compositionId: tab.compositionId // 新增:携带compositionId }; }).filter(Boolean); // 过滤掉前面 map 中返回的 null 值 - // console.log('123123123', processedFiles); formData.files.push(...processedFiles); } @@ -215,6 +224,14 @@ export default { } }); + // 新增:收集标段文件的delComposition + if (sectionFileData.delComposition && sectionFileData.delComposition.length > 0) { + formData.delComposition.push(...sectionFileData.delComposition); + } + + // 新增:对delComposition去重,避免重复删除 + formData.delComposition = [...new Set(formData.delComposition)]; + // --- 新增逻辑结束 --- // 5. 校验模板名称唯一性(仅编辑时) @@ -228,12 +245,14 @@ export default { // } // } + console.log('最终提交数据', formData); + // 6. 调用接口保存 if (this.type === 'add') { - console.log('formData',formData) + console.log('formData', formData) await addTemplateInfo(formData); } else { - console.log('12345678',formData) + console.log('12345678', formData) await updateTemplateInfo(formData); } @@ -269,9 +288,9 @@ export default { const res = await getTemplateInfoDetail({ templateId: this.templateId }); const detail = res.data || {}; - console.log('detail',detail) + console.log('detail', detail) - // 向子组件传递数据 + // 向子组件传递数据(包含compositionId) this.$refs.basicInfoRef.setFormData(detail); this.$refs.projectFileRef.setFormData(detail.projectFiles || []); this.$refs.sectionFileRef.setFormData(detail.sectionFiles || []); @@ -284,7 +303,7 @@ export default { mounted() { // 监听上传动画事件 this.$bus.$on('startUpload', this.handleStartUpload); - this.$bus.$off('endUpload', this.handleEndUpload); + this.$bus.$off('endUpload', this.handleEndUpload); // 先解绑避免重复绑定 this.$bus.$on('endUpload', this.handleEndUpload); // 从路由参数中解析type和templateId diff --git a/src/views/template/templateInfo/components/TemplateHistory.vue b/src/views/template/templateInfo/components/TemplateHistory.vue new file mode 100644 index 0000000..5f23b58 --- /dev/null +++ b/src/views/template/templateInfo/components/TemplateHistory.vue @@ -0,0 +1,179 @@ + + + + + diff --git a/src/views/template/templateInfo/components/child/AnalysisLabel.vue b/src/views/template/templateInfo/components/child/AnalysisLabel.vue index c1e4c11..68a926f 100644 --- a/src/views/template/templateInfo/components/child/AnalysisLabel.vue +++ b/src/views/template/templateInfo/components/child/AnalysisLabel.vue @@ -18,6 +18,7 @@ @@ -78,7 +79,7 @@ export default { rules: { templateName: [ { required: true, message: '请输入模板名称', trigger: 'blur' }, - { max: 128, message: '模板名称不能超过128个字符', trigger: 'blur' } + { max: 128, message: '模板名称不能超过64个字符', trigger: 'blur' } ], industryType: [ { required: true, message: '请选择行业类型', trigger: 'change' } diff --git a/src/views/template/templateInfo/components/child/ProjectFile.vue b/src/views/template/templateInfo/components/child/ProjectFile.vue index 689e181..2d78153 100644 --- a/src/views/template/templateInfo/components/child/ProjectFile.vue +++ b/src/views/template/templateInfo/components/child/ProjectFile.vue @@ -44,9 +44,10 @@ @@ -111,6 +112,8 @@ export default { }, data() { return { + delComposition: [], // 新增:存储需要删除的compositionId + tabFiles: [], uploadType: 'doc、docx、pdf、xls、xlsx、wps', maxFileTips: '50MB', fileUploadRule: { fileUploadType: 'project_file', suffix: 'template' }, @@ -118,6 +121,7 @@ export default { fileTabs: [ { formData: { + compositionId: '', // 新增:存储compositionId compositionType: '1', fileName: '', compositionFileType: '', @@ -160,6 +164,7 @@ export default { handleAddTab() { this.fileTabs.push({ formData: { + compositionId: '', // 新增:新增Tab默认空ID compositionType: '1', fileName: '', compositionFileType: '', @@ -175,6 +180,15 @@ export default { this.$message.warning('至少保留一个文件') return } + + // 新增:删除Tab时收集compositionId(编辑模式且有ID时) + const tab = this.fileTabs[index] + if (this.isEditMode && tab.formData.compositionId) { + if (!this.delComposition.includes(tab.formData.compositionId)) { + this.delComposition.push(tab.formData.compositionId) + } + } + this.fileTabs.splice(index, 1) // 如果删除的是当前激活的tab,需要重新设置激活态 if (this.activeTabIndex === index.toString()) { @@ -184,8 +198,19 @@ export default { }, handleFileChange(files, index) { + const oldFileList = this.fileTabs[index].formData.fileList this.fileTabs[index].formData.fileList = files; + // 新增:替换文件时收集compositionId(编辑模式且有旧文件和ID时) + if (this.isEditMode && oldFileList.length > 0 && this.fileTabs[index].formData.compositionId) { + const compositionId = this.fileTabs[index].formData.compositionId + if (!this.delComposition.includes(compositionId)) { + this.delComposition.push(compositionId) + } + // 替换文件后重置当前Tab的compositionId(表示新增文件) + this.fileTabs[index].formData.compositionId = '' + } + if (files.length > 0 && files[0].status === 'success') { const currentTab = this.fileTabs[index]; files[0].compositionName = currentTab.formData.fileName; @@ -204,6 +229,17 @@ export default { delFileList.push(filePath) } } + + // 新增:删除文件时收集compositionId(编辑模式且有ID时) + if (this.isEditMode && this.fileTabs[index].formData.compositionId) { + const compositionId = this.fileTabs[index].formData.compositionId + if (!this.delComposition.includes(compositionId)) { + this.delComposition.push(compositionId) + console.log('ttt',this.delComposition) + } + // 删除文件后重置当前Tab的compositionId + this.fileTabs[index].formData.compositionId = '' + } }, handleFileNameChange(index) { @@ -227,15 +263,20 @@ export default { // 可以保留日志用于调试 const index = tab.index; console.log(`切换到了第 ${index + 1} 个 tab`); + this.setFormData(this.tabFiles) }, setFormData(projectFiles) { + this.tabFiles = projectFiles + // 重置删除集合 + this.delComposition = [] // 直接清空并重建 fileTabs this.fileTabs = []; if (!projectFiles || projectFiles.length === 0) { this.fileTabs.push({ formData: { + compositionId: '', // 新增 compositionType: '1', fileName: '', compositionFileType: '', @@ -265,6 +306,7 @@ export default { this.fileTabs.push({ formData: { + compositionId: file.compositionId || '', // 新增:解析后端返回的compositionId compositionType: '1', fileName: formattedFile.compositionName, compositionFileType: formattedFile.compositionFileType, @@ -295,9 +337,17 @@ export default { } }) }) - allFormData.push({ ...this.fileTabs[i].formData }) + allFormData.push({ + ...this.fileTabs[i].formData, + // 携带删除标识(可选,根据后端需求) + isDeleted: false + }) } - resolve(allFormData) + // 返回数据包含需要删除的compositionId + resolve({ + formData: allFormData, + delComposition: [...new Set(this.delComposition)] // 去重 + }) } catch (error) { reject(error) } diff --git a/src/views/template/templateInfo/components/child/RuleDetailDrawer.vue b/src/views/template/templateInfo/components/child/RuleDetailDrawer.vue new file mode 100644 index 0000000..637e280 --- /dev/null +++ b/src/views/template/templateInfo/components/child/RuleDetailDrawer.vue @@ -0,0 +1,623 @@ + + + + + diff --git a/src/views/template/templateInfo/components/child/SectionFile.vue b/src/views/template/templateInfo/components/child/SectionFile.vue index 4ba1d27..ac63a8c 100644 --- a/src/views/template/templateInfo/components/child/SectionFile.vue +++ b/src/views/template/templateInfo/components/child/SectionFile.vue @@ -44,9 +44,10 @@ @@ -112,6 +113,8 @@ export default { }, data() { return { + delComposition: [], // 新增:存储需要删除的compositionId + tabFiles: [], uploadType: 'doc、docx、pdf、xls、xlsx、wps', maxFileTips: '50MB', fileUploadRule: { fileUploadType: 'section_file', suffix: 'template' }, @@ -119,6 +122,7 @@ export default { fileTabs: [ { formData: { + compositionId: '', // 新增:存储compositionId compositionType: '2', fileName: '', compositionFileType: '', @@ -161,6 +165,7 @@ export default { handleAddTab() { this.fileTabs.push({ formData: { + compositionId: '', // 新增:新增Tab默认空ID compositionType: '2', fileName: '', compositionFileType: '', @@ -176,6 +181,15 @@ export default { this.$message.warning('至少保留一个文件') return } + + // 新增:删除Tab时收集compositionId(编辑模式且有ID时) + const tab = this.fileTabs[index] + if (this.isEditMode && tab.formData.compositionId) { + if (!this.delComposition.includes(tab.formData.compositionId)) { + this.delComposition.push(tab.formData.compositionId) + } + } + this.fileTabs.splice(index, 1) // 如果删除的是当前激活的tab,需要重新设置激活态 if (this.activeTabIndex === index.toString()) { @@ -184,8 +198,19 @@ export default { }, handleFileChange(files, index) { + const oldFileList = this.fileTabs[index].formData.fileList this.fileTabs[index].formData.fileList = files; + // 新增:替换文件时收集compositionId(编辑模式且有旧文件和ID时) + if (this.isEditMode && oldFileList.length > 0 && this.fileTabs[index].formData.compositionId) { + const compositionId = this.fileTabs[index].formData.compositionId + if (!this.delComposition.includes(compositionId)) { + this.delComposition.push(compositionId) + } + // 替换文件后重置当前Tab的compositionId + this.fileTabs[index].formData.compositionId = '' + } + if (files.length > 0 && files[0].status === 'success') { const currentTab = this.fileTabs[index]; files[0].compositionName = currentTab.formData.fileName; @@ -205,6 +230,16 @@ export default { delFileList.push(filePath) } } + + // 新增:删除文件时收集compositionId(编辑模式且有ID时) + if (this.isEditMode && this.fileTabs[index].formData.compositionId) { + const compositionId = this.fileTabs[index].formData.compositionId + if (!this.delComposition.includes(compositionId)) { + this.delComposition.push(compositionId) + } + // 删除文件后重置当前Tab的compositionId + this.fileTabs[index].formData.compositionId = '' + } }, handleFileNameChange(index) { @@ -228,15 +263,20 @@ export default { // 可以保留日志用于调试 const index = tab.index; console.log(`切换到了第 ${index + 1} 个 tab`); + this.setFormData(this.tabFiles) }, setFormData(sectionFiles) { + this.tabFiles = sectionFiles + // 重置删除集合 + this.delComposition = [] // 直接清空并重建 fileTabs this.fileTabs = []; if (!sectionFiles || sectionFiles.length === 0) { this.fileTabs.push({ formData: { + compositionId: '', // 新增 compositionType: '2', fileName: '', compositionFileType: '', @@ -265,6 +305,7 @@ export default { this.fileTabs.push({ formData: { + compositionId: file.compositionId || '', // 新增:解析后端返回的compositionId compositionType: '2', fileName: formattedFile.compositionName, compositionFileType: formattedFile.compositionFileType, @@ -295,9 +336,16 @@ export default { } }) }) - allFormData.push({ ...this.fileTabs[i].formData }) + allFormData.push({ + ...this.fileTabs[i].formData, + isDeleted: false + }) } - resolve(allFormData) + // 返回数据包含需要删除的compositionId + resolve({ + formData: allFormData, + delComposition: [...new Set(this.delComposition)] // 去重 + }) } catch (error) { reject(error) } diff --git a/src/views/template/templateInfo/components/child/TemplateInfo.vue b/src/views/template/templateInfo/components/child/TemplateInfo.vue new file mode 100644 index 0000000..0f174d5 --- /dev/null +++ b/src/views/template/templateInfo/components/child/TemplateInfo.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/src/views/template/templateInfo/components/child/VersionContent.vue b/src/views/template/templateInfo/components/child/VersionContent.vue new file mode 100644 index 0000000..75aeb42 --- /dev/null +++ b/src/views/template/templateInfo/components/child/VersionContent.vue @@ -0,0 +1,632 @@ + + + + + diff --git a/src/views/template/templateInfo/config.js b/src/views/template/templateInfo/config.js index 44deba3..693a34e 100644 --- a/src/views/template/templateInfo/config.js +++ b/src/views/template/templateInfo/config.js @@ -45,7 +45,7 @@ export const columnsList = [ }, { t_props: 'industryType', t_label: '行业类型' }, { t_props: 'compositionName', t_label: '模板组成' }, - { t_props: 'currentVersion', t_label: '当前版本' }, + { t_props: 'currentVersion', t_label: '当前版本', t_slot: 'versionLink' }, { t_props: 'useState', t_label: '状态', t_slot: 'useState' }, { t_props: 'updateTime', t_label: '更新时间', format: 'yyyy/MM/dd HH:mm' }, ]; diff --git a/src/views/template/templateInfo/index.vue b/src/views/template/templateInfo/index.vue index 561214d..d2e6e34 100644 --- a/src/views/template/templateInfo/index.vue +++ b/src/views/template/templateInfo/index.vue @@ -41,6 +41,17 @@ /> + +