From f48971cf67b61227fc379145db2d79aed001e516 Mon Sep 17 00:00:00 2001 From: sladro Date: Thu, 25 Sep 2025 14:33:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E4=B8=89=E7=BA=A7=E6=A0=87=E9=A2=98=E7=94=9F=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 扩展DocumentChapter模型支持嵌套结构和评分值 - 新增智能分组算法,按关键词和前缀对评分项进行分类 - 重写章节生成逻辑,支持一级、二级、三级标题完整结构 - 优化显示系统,支持递归显示和颜色区分 - 增强表格解析能力,处理复杂的合并单元格结构 - 改进AI识别逻辑,更好地识别评分表格类型 - 完善.gitignore文件,添加项目相关忽略规则 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .gitignore | 59 ++++++++- data/README.md | 4 + doc/江南造船厂前端.md | 128 ++++++++++++++++++++ src/bidmaster/cli/project.py | 27 ++++- src/bidmaster/tools/parser.py | 218 +++++++++++++++++++++++++++------- 5 files changed, 385 insertions(+), 51 deletions(-) create mode 100644 data/README.md create mode 100644 doc/江南造船厂前端.md diff --git a/.gitignore b/.gitignore index 505a3b1..abf0d33 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,63 @@ -# Python-generated files +# Python __pycache__/ -*.py[oc] +*.py[cod] +*$py.class +*.so +.Python build/ +develop-eggs/ dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ wheels/ -*.egg-info +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST # Virtual environments .venv +.env +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Testing +.pytest_cache/ +.coverage +.hypothesis/ +htmlcov/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# Claude Code +.claude/ + +# Project specific +data/kb/ +*.docx +*.xlsx +*.csv +*.pdf +logs/ +temp/ + +# OS +.DS_Store +Thumbs.db + +# Ruff cache +.ruff_cache/ diff --git a/data/README.md b/data/README.md new file mode 100644 index 0000000..7576720 --- /dev/null +++ b/data/README.md @@ -0,0 +1,4 @@ +# 数据存储目录 + +- kb/: 知识库数据(向量数据库) +- projects/: 项目数据存储 \ No newline at end of file diff --git a/doc/江南造船厂前端.md b/doc/江南造船厂前端.md new file mode 100644 index 0000000..6c39c07 --- /dev/null +++ b/doc/江南造船厂前端.md @@ -0,0 +1,128 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## 项目概述 + +这是一个基于Three.js的3D模型查看器和船厂三维数据管理展示系统。项目包含两个主要页面: +- `index.html`: 主要的3D模型查看器界面,支持模型加载、选择、测量、标注等功能 +- `1.html`: 船舶建造精度数字化平台的数据分析界面,通过iframe嵌入3D查看器 + +## 技术架构 + +### 前端技术栈 +- **Three.js**: 核心3D渲染引擎 +- **原生JavaScript**: ES6+ 模块化架构,无框架依赖 +- **HTML5/CSS3**: 响应式布局,使用Flexbox和Grid +- **Tailwind CSS**: 样式框架(仅在1.html中使用) +- **ECharts**: 数据可视化图表(仅在1.html中使用) + +### 核心模块结构 + +所有JavaScript模块位于 `js/` 目录下,采用模块化设计: + +#### 核心管理器 +- **ModelViewer** (`main.js`): 主应用入口,管理整个3D场景 +- **ModelManager** (`ModelManager.js`): 模型管理,处理模型的加载和切换 +- **ModelLoader** (`ModelLoader.js`): 模型加载器,支持GLTF格式 +- **ModelAPI** (`ModelAPI.js`): 模型API接口,提供程序化控制 + +#### 功能管理器 +- **SelectionManager** (`SelectionManager.js`): 零件选择和高亮 +- **MeasurementManager** (`MeasurementManager.js`): 3D测量工具(距离、角度、面积) +- **ClippingManager** (`ClippingManager.js`): 切面控制 +- **AnnotationManager** (`AnnotationManager.js`): 3D标注功能 +- **ModelTreeManager** (`ModelTreeManager.js`): 模型结构树管理 +- **AnomalyDisplayManager** (`AnomalyDisplayManager.js`): 异常状态显示 +- **LayerDisplayManager** (`LayerDisplayManager.js`): 分层显示管理 + +#### UI和状态管理 +- **UIController** (`UIController.js`): UI控制器,管理所有界面交互 +- **ObjectStateManager** (`ObjectStateManager.js`): 对象状态管理 +- **StorageManager** (`StorageManager.js`): 本地存储管理 +- **OutlineManager** (`OutlineManager.js`): 边缘高亮效果 + +### 资源结构 +- **models/**: 3D模型文件(.glb格式) +- **css/**: 样式文件(主要是style.css) +- **image/**: UI图标和图片资源 +- **three/**: Three.js库文件 + +## 开发环境 + +### 启动开发服务器 +由于项目使用ES6模块和本地文件引用,需要通过HTTP服务器运行: + +```bash +# 使用Live Server(推荐) +# VS Code安装Live Server扩展,配置端口5501 +# 右键点击index.html选择"Open with Live Server" + +# 或使用Python内置服务器 +python -m http.server 5501 + +# 或使用Node.js http-server +npx http-server -p 5501 +``` + +### 访问页面 +- 3D模型查看器: `http://localhost:5501/index.html` +- 数据分析平台: `http://localhost:5501/1.html` + +## 开发约定 + +### 模型文件管理 +- 支持的格式: .glb (推荐) +- 模型文件存放在 `models/` 目录 +- 默认加载模型: `CX0856_CB01C.glb` + +### 配置和状态管理 +- 使用 `StorageManager` 进行本地存储 +- 配置项通过 `ObjectStateManager` 管理 +- 模型状态通过事件系统同步 + +### 代码风格 +- 使用ES6+语法和模块 +- 采用面向对象设计模式 +- 每个功能模块独立文件 +- 使用驼峰命名法 +- 保持代码注释的完整性 + +### 事件通信 +项目使用自定义事件系统进行模块间通信: +- 模型加载完成事件 +- 选择状态变化事件 +- UI状态更新事件 +- 测量结果事件 + +### 浏览器兼容性 +- 支持现代浏览器(Chrome 88+, Firefox 85+, Safari 14+) +- 依赖ES6模块支持 +- 需要WebGL 2.0支持 + +## 调试和测试 + +### 控制台调试 +- 使用 `window.modelViewer` 访问主应用实例 +- 各管理器实例可通过modelViewer实例访问 +- 开启浏览器开发者工具查看Three.js性能面板 + +### 性能优化 +- 大模型文件建议压缩优化 +- 使用LOD (Level of Detail) 技术 +- 合理使用材质和纹理 +- 避免过度的实时计算 + +## 常见问题 + +### CORS错误 +确保通过HTTP服务器访问,而不是直接打开HTML文件 + +### 模型加载失败 +检查模型文件路径和格式,确保.glb文件完整性 + +### Three.js版本兼容 +项目基于Three.js r150+,更新版本时注意API变更 + +### 内存泄漏 +及时清理不需要的几何体、材质和纹理对象 \ No newline at end of file diff --git a/src/bidmaster/cli/project.py b/src/bidmaster/cli/project.py index e04cdf2..19b44ca 100644 --- a/src/bidmaster/cli/project.py +++ b/src/bidmaster/cli/project.py @@ -17,6 +17,26 @@ console = Console() logger = logging.getLogger(__name__) +def _display_chapters_recursive(chapters, indent_level=0): + """递归显示章节结构""" + for chapter in chapters: + indent = " " * indent_level + + # 根据层级设置不同的样式 + if chapter.level == 1: + style = "bold blue" + elif chapter.level == 2: + style = "green" + else: # level 3 + style = "yellow" + + console.print(f"{indent}{chapter.title}", style=style) + + # 递归显示子章节 + if chapter.children: + _display_chapters_recursive(chapter.children, indent_level + 1) + + @click.group() def project(): """项目管理命令""" @@ -90,9 +110,7 @@ def parse(scoring_file: str, deviation_file: str | None, template_file: str | No # 显示模板章节(如果有) if bid_structure.chapters: console.print("\n📚 模板章节:") - for chapter in bid_structure.chapters: - indent = " " * chapter.level - console.print(f"{indent}• {chapter.title}") + _display_chapters_recursive(bid_structure.chapters) # 显示统计信息 stats_panel = Panel( @@ -193,8 +211,7 @@ def smart_parse(word_file: str): # 显示生成的目录结构 console.print("\n📚 生成的目录结构:") - for chapter in bid_structure.chapters: - console.print(f" {chapter.title}") + _display_chapters_recursive(bid_structure.chapters) # 显示统计信息 stats_panel = Panel( diff --git a/src/bidmaster/tools/parser.py b/src/bidmaster/tools/parser.py index 8db3a53..0e98e25 100644 --- a/src/bidmaster/tools/parser.py +++ b/src/bidmaster/tools/parser.py @@ -56,6 +56,8 @@ class DocumentChapter(BaseModel): id: str = Field(..., description="章节ID") title: str = Field(..., description="章节标题") level: int = Field(..., description="章节层级") + score: float | None = Field(default=None, description="评分值") + children: List['DocumentChapter'] = Field(default_factory=list, description="子章节") template_placeholder: str | None = Field(default=None, description="模板占位符") @@ -297,11 +299,24 @@ class BidParser: return criteria def _extract_table_text(self, table) -> str: - """提取表格内容为文本格式""" + """提取表格内容为文本格式,处理合并单元格""" lines = [] + # 获取表格的基本信息 + max_cols = max(len(row.cells) for row in table.rows) if table.rows else 0 + for i, row in enumerate(table.rows): - cells = [cell.text.strip() for cell in row.cells] + cells = [] + for j in range(max_cols): + if j < len(row.cells): + cell_text = row.cells[j].text.strip() + # 处理空单元格 + if not cell_text: + cell_text = "[空]" + cells.append(cell_text) + else: + cells.append("[空]") + # 使用制表符分隔,便于AI理解 line = "\t".join(cells) lines.append(f"行{i+1}: {line}") @@ -312,37 +327,41 @@ class BidParser: """使用AI解析评分表格""" try: prompt = f""" -请提取表格中的评分项和分值,并智能分类,返回JSON。 +请从复杂的评分表格中提取评分项和分值,并智能分类,返回JSON。 表格内容: {table_text} 要求: -1. 提取评分项名称和分值 -2. 描述字段用简短概括(不超过30字) -3. 根据评分项内容智能分类,重点区分商务和技术: +1. 仔细分析表格结构,即使有合并单元格也要正确提取 +2. 从"评审内容"、"评分因素"、"评分标准"等列中提取评分项名称 +3. 从"分值"列或评分标准描述中提取具体分值(如3分、5分、40分) +4. 忽略总分构成等汇总信息,只提取具体评分项 +5. 智能分类各评分项: **技术类别:** - - technical_solution: 技术方案、技术完整性、技术先进性、技术路线 + - technical_solution: 技术方案、技术条款、技术完整性、技术先进性 - equipment_spec: 设备规格、产品参数、设备可靠性、技术指标 - implementation: 项目实施、施工方案、进度计划、实施能力 - quality_safety: 质量管理、安全管理、环境管理、质量体系 - - after_sales: 售后服务、维保服务、培训服务、技术支持 - - compliance: 技术资质、认证证书、技术合规性 + - after_sales: 售后服务、维保服务、培训服务、技术支持、服务部分 + - compliance: 技术资质、认证证书、技术合规性、技术条款应答 **商务类别:** - - commercial: 价格评分、报价、商务条件、企业资质、财务状况、业绩、投标保证金、商务合规性 + - commercial: 价格评分、报价、商务条件、企业资质、财务状况、业绩、商务条款应答、响应文件制作质量、同类项目业绩、商务部分 -**其他:** - - other: 无法明确分类的项目 +示例输入分析: +- "响应文件制作质量 3分" → 商务类别 +- "同类项目业绩 5分" → 商务类别 +- "技术条款应答 2分" → 合规类别 +- "商务条款应答 2分" → 商务类别 格式: {{ "scoring_criteria": [ - {{"item_name": "报价", "max_score": 30, "description": "价格评分标准", "category": "commercial"}}, - {{"item_name": "技术方案", "max_score": 40, "description": "技术方案评分", "category": "technical_solution"}}, - {{"item_name": "企业资质", "max_score": 10, "description": "企业资质证明", "category": "commercial"}}, - {{"item_name": "设备参数", "max_score": 20, "description": "设备技术指标", "category": "equipment_spec"}} + {{"item_name": "响应文件制作质量", "max_score": 3, "description": "文件格式内容要求", "category": "commercial"}}, + {{"item_name": "同类项目业绩", "max_score": 5, "description": "项目经验证明", "category": "commercial"}}, + {{"item_name": "技术条款应答", "max_score": 2, "description": "技术要求符合性", "category": "compliance"}} ] }} @@ -356,8 +375,21 @@ class BidParser: # 解析AI响应 try: + + if not response or response.strip() == "": + logger.error("AI返回空响应") + return [] + + # 尝试清理响应内容 + clean_response = response.strip() + if clean_response.startswith("```json"): + clean_response = clean_response[7:] + if clean_response.endswith("```"): + clean_response = clean_response[:-3] + clean_response = clean_response.strip() + # 直接解析JSON,失败就抛出异常 - result_data = json.loads(response) + result_data = json.loads(clean_response) scoring_data = result_data.get("scoring_criteria", []) @@ -426,10 +458,17 @@ class BidParser: {table_text} 请判断这个表格属于以下哪种类型: -1. scoring - 评分表:包含评分项、分值、评分标准等 +1. scoring - 评分表:包含评分项、分值、评分标准等内容,例如: + - 有"分值"、"评分标准"、"得分"等列 + - 有具体的分值数字(如3分、5分、40分等) + - 有"商务部分"、"技术部分"、"服务部分"等分类 + - 有评审内容和评分因素 + 2. deviation - 偏离表:包含技术要求、响应类型、偏离说明等 3. other - 其他表格:不是评分表也不是偏离表 +注意:即使表格结构复杂、有合并单元格,只要包含评分标准和分值信息就是评分表。 + 只返回一个单词:scoring 或 deviation 或 other""" response = self._call_llm_api(prompt) @@ -684,40 +723,133 @@ class BidParser: return chapters - def _generate_professional_chapters(self, scoring_criteria: List[ScoringCriteria]) -> List[DocumentChapter]: - """基于评分标准生成专业目录结构""" - chapters = [] + def _group_scoring_items(self, scoring_criteria: List[ScoringCriteria], category: TechnicalCategory) -> dict[str, List[ScoringCriteria]]: + """对同一类别下的评分项进行智能分组""" + # 过滤出指定类别的评分项 + category_items = [item for item in scoring_criteria if item.category == category] - # 获取涉及的技术类别 - categories_used = set() - for criteria in scoring_criteria: - categories_used.add(criteria.category.value) + if not category_items: + return {} + + groups = {} + + for item in category_items: + item_name = item.item_name + group_key = None + + # 规则1:识别前缀模式(如"平台要求-xxx") + if "-" in item_name: + prefix = item_name.split("-")[0].strip() + if len(prefix) <= 10: # 前缀不能太长 + group_key = f"{prefix}要求" + + # 规则2:识别关键词分组 + if not group_key: + keywords_mapping = { + "原型": "系统原型设计", + "展示": "系统原型设计", + "方案": "方案设计", + "投标": "方案设计", + "人员": "人员配置方案", + "配置": "人员配置方案", + "测试": "测试质量保障", + "工具": "测试质量保障", + "质保": "质保服务方案", + "售后": "售后服务方案", + "服务": "售后服务方案", + "企业": "企业资质证明", + "实力": "企业资质证明", + "认证": "技术认证证书", + "证书": "技术认证证书", + "适配": "技术认证证书" + } + + for keyword, group_name in keywords_mapping.items(): + if keyword in item_name: + group_key = group_name + break + + # 规则3:默认分组 + if not group_key: + group_key = "其他要求" + + if group_key not in groups: + groups[group_key] = [] + groups[group_key].append(item) + + return groups + + def _add_sub_chapters(self, parent_chapter: DocumentChapter, groups: dict[str, List[ScoringCriteria]], chapter_num: str) -> None: + """为父章节添加二三级子章节""" + if not groups: + return + + sub_chapter_index = 1 + for group_name, scoring_items in groups.items(): + # 创建二级标题 + sub_chapter_id = f"{parent_chapter.id}_{sub_chapter_index:02d}" + sub_chapter_title = f"{chapter_num}.{sub_chapter_index} {group_name}" + + sub_chapter = DocumentChapter( + id=sub_chapter_id, + title=sub_chapter_title, + level=2, + template_placeholder=f"{{{{{sub_chapter_id}_content}}}}" + ) + + # 为二级标题添加三级标题(具体评分项) + item_index = 1 + for scoring_item in scoring_items: + item_chapter_id = f"{sub_chapter_id}_{item_index:02d}" + item_title = f"{chapter_num}.{sub_chapter_index}.{item_index} {scoring_item.item_name}" + if scoring_item.max_score > 0: + item_title += f" ({scoring_item.max_score}分)" + + item_chapter = DocumentChapter( + id=item_chapter_id, + title=item_title, + level=3, + score=scoring_item.max_score, + template_placeholder=f"{{{{{item_chapter_id}_content}}}}" + ) + + sub_chapter.children.append(item_chapter) + item_index += 1 + + parent_chapter.children.append(sub_chapter) + sub_chapter_index += 1 + + def _generate_professional_chapters(self, scoring_criteria: List[ScoringCriteria]) -> List[DocumentChapter]: + """基于评分标准生成专业三级目录结构""" + chapters = [] # 1. 评标索引表(始终包含) chapters.append(self._create_standard_chapter("evaluation_index")) - # 2. 合规响应表(如果有偏离项或合规类评分项) - if any(c.category == TechnicalCategory.COMPLIANCE for c in scoring_criteria): - chapters.append(self._create_standard_chapter("compliance_response")) - else: - # 即使没有合规类评分项,也添加偏离表章节(招投标标准要求) - chapters.append(self._create_standard_chapter("compliance_response")) + # 2. 合规响应表(始终包含) + compliance_chapter = self._create_standard_chapter("compliance_response") + # 为合规章节添加二三级标题 + compliance_groups = self._group_scoring_items(scoring_criteria, TechnicalCategory.COMPLIANCE) + self._add_sub_chapters(compliance_chapter, compliance_groups, "2") + chapters.append(compliance_chapter) - # 3. 根据评分项类别添加相应章节 - category_order = [ - "technical_solution", - "equipment_spec", - "implementation", - "quality_system", - "after_sales" + # 3-7. 技术章节 + technical_chapters = [ + ("technical_solution", TechnicalCategory.TECHNICAL_SOLUTION, "3"), + ("equipment_spec", TechnicalCategory.EQUIPMENT_SPEC, "4"), + ("implementation", TechnicalCategory.IMPLEMENTATION, "5"), + ("quality_system", TechnicalCategory.QUALITY_SAFETY, "6"), + ("after_sales", TechnicalCategory.AFTER_SALES, "7") ] - for category_key in category_order: - if category_key in categories_used or category_key == "technical_solution": - # 技术方案章节始终包含(作为兜底章节) - chapters.append(self._create_standard_chapter(category_key)) + for chapter_key, category, chapter_num in technical_chapters: + chapter = self._create_standard_chapter(chapter_key) + # 为每个技术章节添加二三级标题 + category_groups = self._group_scoring_items(scoring_criteria, category) + self._add_sub_chapters(chapter, category_groups, chapter_num) + chapters.append(chapter) - # 4. 验收与绩效考核对应表(标准履约要求,始终包含) + # 8. 验收与绩效考核对应表(始终包含) chapters.append(self._create_standard_chapter("contract_delivery")) return chapters