diff --git a/src/bidmaster/agents/toc_generator.py b/src/bidmaster/agents/toc_generator.py index f28880e..598e252 100644 --- a/src/bidmaster/agents/toc_generator.py +++ b/src/bidmaster/agents/toc_generator.py @@ -138,7 +138,7 @@ def generate_first_level_node(state: TocGeneratorState) -> TocGeneratorState: total_score = sum(c.max_score for c in criteria_list) chapter_id = f"chapter_{chapter_index:02d}_{category}" category_name = CATEGORY_NAMES.get(category, category) - chapter_title = f"{chapter_index}. {category_name}" + chapter_title = category_name if total_score > 0: chapter_title += f" ({total_score}分)" @@ -306,20 +306,12 @@ def finalize_chapters_node(state: TocGeneratorState) -> TocGeneratorState: if not has_index: index_chapter = DocumentChapter( id="evaluation_index", - title="1. 评标索引表(技术评分完全对应)", + title="评标索引表(技术评分完全对应)", # 不包含编号 level=1, template_placeholder="{{evaluation_index_content}}" ) final_chapters.insert(0, index_chapter) - # 重新编号 - for i, chapter in enumerate(final_chapters[1:], 2): - # 安全处理章节编号 - title_parts = chapter.title.split(".") - if len(title_parts) >= 2: - old_number = title_parts[0] - chapter.title = chapter.title.replace(f"{old_number}.", f"{i}.", 1) - state["final_chapters"] = final_chapters state["current_step"] = "finalize" state["should_continue"] = False @@ -352,9 +344,9 @@ def _generate_ai_sub_chapters(criteria_list: List[ScoringCriteria], {chr(10).join(criteria_info)} 生成要求: -1. 为每个评分项生成对应的二级标题 -2. 重要评分项可添加三级子标题 -3. 格式规范,符合标书要求 +1. 为每个评分项生成对应的子标题名称(不要包含编号) +2. 重要评分项可添加三级子标题(不要包含编号) +3. 只返回标题文本,编号由Word自动管理 返回JSON格式: {{ @@ -381,23 +373,14 @@ def _generate_ai_sub_chapters(criteria_list: List[ScoringCriteria], sub_chapters_data = result_data.get("sub_chapters", []) sub_chapters = [] - # 安全处理章节号提取 - parent_parts = parent_chapter.title.split(".") - parent_number = parent_parts[0] if parent_parts else "1" for i, sub_data in enumerate(sub_chapters_data, 1): - # 清理标题,移除可能的错误编号 + # 直接使用标题文本,不添加编号 title = sub_data.get("title", f"子标题{i}") - if "." in title and title[0].isdigit(): - parts = title.split(" ", 1) - if len(parts) > 1: - title = parts[1] - - correct_title = f"{parent_number}.{i} {title}" sub_chapter = DocumentChapter( id=f"{parent_chapter.id}_sub_{i:02d}", - title=correct_title, + title=title, # 不添加编号 level=sub_data.get("level", 2), score=sub_data.get("score", 0), template_placeholder=f"{{{{{parent_chapter.id}_sub_{i:02d}_content}}}}" @@ -406,16 +389,10 @@ def _generate_ai_sub_chapters(criteria_list: List[ScoringCriteria], # 处理三级标题 for j, child_data in enumerate(sub_data.get("children", []), 1): child_title = child_data.get("title", f"三级标题{j}") - if "." in child_title and child_title[0].isdigit(): - parts = child_title.split(" ", 1) - if len(parts) > 1: - child_title = parts[1] - - correct_child_title = f"{parent_number}.{i}.{j} {child_title}" child_chapter = DocumentChapter( id=f"{parent_chapter.id}_sub_{i:02d}_{j:02d}", - title=correct_child_title, + title=child_title, # 不添加编号 level=child_data.get("level", 3), template_placeholder=f"{{{{{parent_chapter.id}_sub_{i:02d}_{j:02d}_content}}}}" ) @@ -438,30 +415,23 @@ def _generate_template_sub_chapters(criteria: ScoringCriteria, parent_chapter: DocumentChapter, template_file: Optional[str]) -> List[DocumentChapter]: """基于模板生成子标题""" - # 提供默认结构 - # 从父章节标题中提取章节号,处理 "2. 技术方案 (10分)" 这种格式 - parent_title = parent_chapter.title - if "." in parent_title: - parent_number = parent_title.split(".")[0].strip() - else: - parent_number = "1" - + # 提供默认结构(不包含编号) default_sub_chapters = [ DocumentChapter( id=f"{parent_chapter.id}_def_01", - title=f"{parent_number}.1 方案概述", + title="方案概述", # 不包含编号 level=2, template_placeholder=f"{{{{{parent_chapter.id}_def_01_content}}}}" ), DocumentChapter( id=f"{parent_chapter.id}_def_02", - title=f"{parent_number}.2 具体实施", + title="具体实施", # 不包含编号 level=2, template_placeholder=f"{{{{{parent_chapter.id}_def_02_content}}}}" ), DocumentChapter( id=f"{parent_chapter.id}_def_03", - title=f"{parent_number}.3 保障措施", + title="保障措施", # 不包含编号 level=2, template_placeholder=f"{{{{{parent_chapter.id}_def_03_content}}}}" ) diff --git a/src/bidmaster/tools/word.py b/src/bidmaster/tools/word.py index fb74065..5130f9f 100644 --- a/src/bidmaster/tools/word.py +++ b/src/bidmaster/tools/word.py @@ -26,28 +26,28 @@ class WordProcessor: def create_template_from_chapters(self, chapters: List[DocumentChapter], output_path: str, project_name: str = "标书项目") -> bool: """根据章节结构创建Word模板""" try: - # 在生成Word之前,重新编号所有章节 - self._renumber_chapters_for_word(chapters) - doc = Document() + # 配置多级编号样式 + self._setup_numbering_styles(doc) + # 设置文档标题 title = doc.add_heading(project_name, 0) title.alignment = WD_ALIGN_PARAGRAPH.CENTER # 添加目录占位符 doc.add_paragraph() - toc_para = doc.add_paragraph("【此处为目录,请在Word中按Ctrl+A全选后按F9更新】") + toc_para = doc.add_paragraph("【此处为目录,请在Word中插入目录:引用→目录→自动目录】") toc_para.alignment = WD_ALIGN_PARAGRAPH.CENTER doc.add_page_break() - # 递归添加章节 + # 递归添加章节(不进行手动编号) self._add_chapters_to_doc(doc, chapters) # 保存文档 doc.save(output_path) - logger.info(f"Word模板已生成: {output_path}") + logger.info(f"Word模板已生成(使用原生编号): {output_path}") return True except Exception as e: @@ -55,15 +55,18 @@ class WordProcessor: return False def _add_chapters_to_doc(self, doc: Document, chapters: List[DocumentChapter]): - """递归添加章节到文档""" + """递归添加章节到文档(不含编号)""" for chapter in chapters: - # 添加标题 - if chapter.level <= 3: - heading = doc.add_heading(chapter.title, level=chapter.level) + # 提取标题文本(去除现有编号) + title_text = self._extract_title_text(chapter.title) + + # 使用Word的标题样式,让Word自动编号 + if chapter.level <= 9: # Word支持Heading 1-9 + heading = doc.add_heading(title_text, level=chapter.level) else: - # 超过3级的用普通段落加粗 + # 超过9级用普通段落加粗 para = doc.add_paragraph() - run = para.add_run(chapter.title) + run = para.add_run(title_text) run.bold = True # 为有内容的章节添加占位符 @@ -91,44 +94,28 @@ class WordProcessor: if chapter.level == 1: doc.add_paragraph() # 添加空行 - def _renumber_chapters_for_word(self, chapters: List[DocumentChapter]): - """为Word生成重新编号所有章节""" - logger.info("开始为Word生成重新编号章节...") - - for i, chapter in enumerate(chapters, 1): - # 重新编号一级章节 - chapter.title = self._update_chapter_number(chapter.title, str(i)) - logger.info(f"一级章节重编号: {chapter.title}") - - # 重新编号子章节 - for j, sub_chapter in enumerate(chapter.children, 1): - sub_chapter.title = self._update_chapter_number(sub_chapter.title, f"{i}.{j}") - logger.info(f" 子章节重编号: {sub_chapter.title}") - - # 重新编号三级章节 - for k, child_chapter in enumerate(sub_chapter.children, 1): - child_chapter.title = self._update_chapter_number(child_chapter.title, f"{i}.{j}.{k}") - logger.info(f" 三级章节重编号: {child_chapter.title}") - - logger.info("章节重编号完成") - - def _update_chapter_number(self, title: str, new_number: str) -> str: - """更新章节标题中的编号,保持标题内容不变""" + def _extract_title_text(self, title: str) -> str: + """提取标题文本,去除编号""" import re + # 匹配并移除开头的编号(如 "1. ", "1.1 ", "1.1.1 ") + pattern = r'^(\d+(?:\.\d+)*\.?\s+)' + return re.sub(pattern, '', title.strip()) - # 使用正则表达式匹配各种编号格式 - # 匹配: 1. | 1.1 | 1.1.1 | 1.1.1. 等开头的编号格式 - number_pattern = r'^(\d+(?:\.\d+)*\.?)\s*(.*)$' - match = re.match(number_pattern, title.strip()) + def _setup_numbering_styles(self, doc: Document): + """配置标题的多级编号样式""" + # 确保标题样式存在并配置编号格式 + styles = doc.styles - if match: - # 提取原有内容(去掉编号) - content = match.group(2).strip() - new_title = f"{new_number} {content}" - logger.info(f"编号转换: '{title}' → '{new_title}'") - return new_title - - # 没有找到编号格式,直接添加编号 - new_title = f"{new_number} {title.strip()}" - logger.info(f"编号添加: '{title}' → '{new_title}'") - return new_title \ No newline at end of file + # 为Heading 1-3配置编号格式提示 + # 注意:python-docx不直接支持编号格式设置, + # 但通过使用标题样式,Word会自动应用其默认的多级列表 + for i in range(1, 4): + style_name = f'Heading {i}' + try: + if style_name in styles: + style = styles[style_name] + # 确保样式不被隐藏 + style.hidden = False + style.quick_style = True + except Exception as e: + logger.debug(f"配置样式 {style_name} 时出错: {e}") \ No newline at end of file