check_navigation_consistency_simple.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 导航一致性检查工具 (简化版)
  5. 版本: 1.0
  6. 描述: 检查所有前端页面的导航元素一致性,确保导航结构统一
  7. """
  8. import os
  9. import sys
  10. import re
  11. from pathlib import Path
  12. # 添加项目根目录到Python路径
  13. project_root = Path(__file__).parent.parent.parent
  14. sys.path.append(str(project_root))
  15. class Colors:
  16. """终端颜色定义"""
  17. RED = '\033[91m'
  18. GREEN = '\033[92m'
  19. YELLOW = '\033[93m'
  20. BLUE = '\033[94m'
  21. MAGENTA = '\033[95m'
  22. CYAN = '\033[96m'
  23. WHITE = '\033[97m'
  24. BOLD = '\033[1m'
  25. UNDERLINE = '\033[4m'
  26. RESET = '\033[0m'
  27. def print_message(color, message):
  28. """打印带颜色的消息"""
  29. print(f"{color}{message}{Colors.RESET}")
  30. def print_success(message):
  31. print_message(Colors.GREEN, f"✓ {message}")
  32. def print_info(message):
  33. print_message(Colors.BLUE, f"ℹ {message}")
  34. def print_warning(message):
  35. print_message(Colors.YELLOW, f"⚠ {message}")
  36. def print_error(message):
  37. print_message(Colors.RED, f"✗ {message}")
  38. class SimpleNavigationChecker:
  39. """简化的导航一致性检查器"""
  40. def __init__(self):
  41. self.project_root = project_root
  42. self.front_dir = self.project_root / "output_sourcecode" / "front"
  43. self.pages_data = {}
  44. def check_all(self):
  45. """执行所有导航一致性检查"""
  46. print_message(Colors.BOLD + Colors.CYAN, "🔍 导航一致性检查工具 (简化版)")
  47. print_message(Colors.CYAN, "=" * 60)
  48. if not self.front_dir.exists():
  49. print_error(f"前端页面目录不存在: {self.front_dir}")
  50. return False
  51. # 获取所有HTML文件
  52. html_files = list(self.front_dir.glob("*.html"))
  53. if not html_files:
  54. print_error("未找到任何HTML页面文件")
  55. return False
  56. print_info(f"找到 {len(html_files)} 个HTML页面文件")
  57. # 解析所有页面
  58. self.parse_all_pages(html_files)
  59. # 执行各项检查
  60. all_passed = True
  61. all_passed &= self.check_header_consistency()
  62. all_passed &= self.check_sidebar_consistency()
  63. all_passed &= self.check_navigation_links()
  64. all_passed &= self.check_css_consistency()
  65. # 输出检查结果
  66. self.print_summary(all_passed)
  67. return all_passed
  68. def parse_all_pages(self, html_files):
  69. """解析所有页面的导航结构"""
  70. print_info("解析页面导航结构...")
  71. for html_file in html_files:
  72. try:
  73. with open(html_file, 'r', encoding='utf-8') as f:
  74. content = f.read()
  75. # 提取导航相关信息
  76. page_data = {
  77. 'file': html_file.name,
  78. 'path': str(html_file),
  79. 'has_header': self.has_header_nav(content),
  80. 'has_sidebar': self.has_sidebar_nav(content),
  81. 'has_breadcrumb': self.has_breadcrumb_nav(content),
  82. 'nav_links': self.extract_nav_links(content),
  83. 'nav_css_classes': self.extract_nav_css_classes(content),
  84. 'nav_structure': self.extract_nav_structure(content)
  85. }
  86. self.pages_data[html_file.name] = page_data
  87. except Exception as e:
  88. print_error(f"解析页面失败 {html_file.name}: {e}")
  89. def has_header_nav(self, content):
  90. """检查是否有头部导航"""
  91. header_patterns = [
  92. r'<header[^>]*>',
  93. r'class=["\'][^"\']*header[^"\']*["\']',
  94. r'id=["\'][^"\']*header[^"\']*["\']',
  95. r'<nav[^>]*>'
  96. ]
  97. return any(re.search(pattern, content, re.IGNORECASE) for pattern in header_patterns)
  98. def has_sidebar_nav(self, content):
  99. """检查是否有侧边栏导航"""
  100. sidebar_patterns = [
  101. r'<aside[^>]*>',
  102. r'class=["\'][^"\']*sidebar[^"\']*["\']',
  103. r'id=["\'][^"\']*sidebar[^"\']*["\']',
  104. r'class=["\'][^"\']*nav[^"\']*["\']'
  105. ]
  106. return any(re.search(pattern, content, re.IGNORECASE) for pattern in sidebar_patterns)
  107. def has_breadcrumb_nav(self, content):
  108. """检查是否有面包屑导航"""
  109. breadcrumb_patterns = [
  110. r'class=["\'][^"\']*breadcrumb[^"\']*["\']',
  111. r'id=["\'][^"\']*breadcrumb[^"\']*["\']',
  112. r'aria-label=["\'][^"\']*breadcrumb[^"\']*["\']'
  113. ]
  114. return any(re.search(pattern, content, re.IGNORECASE) for pattern in breadcrumb_patterns)
  115. def extract_nav_links(self, content):
  116. """提取导航链接"""
  117. # 查找HTML文件链接
  118. link_pattern = r'href=["\']([^"\']*\.html)["\']'
  119. links = re.findall(link_pattern, content)
  120. # 过滤掉外部链接和锚链接
  121. internal_links = []
  122. for link in links:
  123. if not link.startswith('http') and not link.startswith('#'):
  124. # 获取文件名
  125. filename = link.split('/')[-1]
  126. internal_links.append(filename)
  127. return list(set(internal_links)) # 去重
  128. def extract_nav_css_classes(self, content):
  129. """提取导航相关的CSS类"""
  130. nav_keywords = ['nav', 'menu', 'header', 'sidebar', 'breadcrumb']
  131. nav_classes = []
  132. # 查找class属性
  133. class_pattern = r'class=["\']([^"\']*)["\']'
  134. class_matches = re.findall(class_pattern, content)
  135. for classes in class_matches:
  136. class_list = classes.split()
  137. for cls in class_list:
  138. if any(keyword in cls.lower() for keyword in nav_keywords):
  139. nav_classes.append(cls)
  140. return list(set(nav_classes)) # 去重
  141. def extract_nav_structure(self, content):
  142. """提取导航结构特征"""
  143. structure = {
  144. 'header_count': len(re.findall(r'<header[^>]*>', content, re.IGNORECASE)),
  145. 'nav_count': len(re.findall(r'<nav[^>]*>', content, re.IGNORECASE)),
  146. 'aside_count': len(re.findall(r'<aside[^>]*>', content, re.IGNORECASE)),
  147. 'menu_class_count': len(re.findall(r'class=["\'][^"\']*menu[^"\']*["\']', content, re.IGNORECASE))
  148. }
  149. return structure
  150. def check_header_consistency(self):
  151. """检查头部导航一致性"""
  152. print_info("检查头部导航一致性...")
  153. pages_with_header = [name for name, data in self.pages_data.items() if data['has_header']]
  154. pages_without_header = [name for name, data in self.pages_data.items() if not data['has_header']]
  155. if pages_without_header:
  156. for page in pages_without_header:
  157. print_error(f" {page}: 缺少头部导航")
  158. return False
  159. elif pages_with_header:
  160. print_success(f"所有 {len(pages_with_header)} 个页面都包含头部导航")
  161. return True
  162. else:
  163. print_warning("未检测到任何头部导航")
  164. return True
  165. def check_sidebar_consistency(self):
  166. """检查侧边栏一致性"""
  167. print_info("检查侧边栏导航一致性...")
  168. pages_with_sidebar = [name for name, data in self.pages_data.items() if data['has_sidebar']]
  169. pages_without_sidebar = [name for name, data in self.pages_data.items() if not data['has_sidebar']]
  170. # 如果大部分页面都有侧边栏,那么没有侧边栏的页面可能有问题
  171. total_pages = len(self.pages_data)
  172. sidebar_ratio = len(pages_with_sidebar) / total_pages if total_pages > 0 else 0
  173. if sidebar_ratio > 0.5 and pages_without_sidebar:
  174. for page in pages_without_sidebar:
  175. print_warning(f" {page}: 可能缺少侧边栏导航")
  176. return True # 这是警告,不是错误
  177. elif pages_with_sidebar:
  178. print_success(f"{len(pages_with_sidebar)} 个页面包含侧边栏导航")
  179. return True
  180. else:
  181. print_info("未检测到侧边栏导航")
  182. return True
  183. def check_navigation_links(self):
  184. """检查导航链接有效性"""
  185. print_info("检查导航链接有效性...")
  186. all_files = set(data['file'] for data in self.pages_data.values())
  187. issues = []
  188. for page_name, page_data in self.pages_data.items():
  189. for link in page_data['nav_links']:
  190. if link not in all_files:
  191. issues.append(f"{page_name}: 导航链接指向不存在的文件 '{link}'")
  192. if issues:
  193. for issue in issues:
  194. print_error(f" {issue}")
  195. return False
  196. else:
  197. print_success("所有导航链接有效")
  198. return True
  199. def check_css_consistency(self):
  200. """检查CSS类名一致性"""
  201. print_info("检查导航CSS类名一致性...")
  202. # 收集所有导航CSS类
  203. all_nav_classes = set()
  204. for page_data in self.pages_data.values():
  205. all_nav_classes.update(page_data['nav_css_classes'])
  206. if not all_nav_classes:
  207. print_warning("未找到导航相关的CSS类")
  208. return True
  209. # 检查每个页面的CSS类使用情况
  210. reference_classes = all_nav_classes
  211. issues = []
  212. for page_name, page_data in self.pages_data.items():
  213. page_classes = set(page_data['nav_css_classes'])
  214. # 如果页面缺少太多公共导航类,可能有问题
  215. missing_ratio = len(reference_classes - page_classes) / len(reference_classes) if reference_classes else 0
  216. if missing_ratio > 0.7: # 缺少超过70%的导航类
  217. issues.append(f"{page_name}: 导航CSS类可能不完整")
  218. if issues:
  219. for issue in issues:
  220. print_warning(f" {issue}")
  221. return True # 这是警告,不是错误
  222. else:
  223. print_success("导航CSS类名使用一致")
  224. return True
  225. def print_summary(self, all_passed):
  226. """打印检查结果摘要"""
  227. print_message(Colors.CYAN, "=" * 60)
  228. if all_passed:
  229. print_success("🎉 导航一致性检查通过!")
  230. print_info("页面导航结构基本一致,符合基本质量要求")
  231. else:
  232. print_error("❌ 发现导航一致性问题")
  233. print_warning("请修复上述问题以确保导航的一致性")
  234. print_info("建议:")
  235. print(" 1. 确保所有页面使用相同的导航组件")
  236. print(" 2. 检查导航链接的有效性")
  237. print(" 3. 统一导航相关的CSS类名")
  238. print(" 4. 确认导航结构的完整性")
  239. print_message(Colors.CYAN, "=" * 60)
  240. def main():
  241. """主函数"""
  242. try:
  243. checker = SimpleNavigationChecker()
  244. success = checker.check_all()
  245. # 返回适当的退出码
  246. sys.exit(0 if success else 1)
  247. except Exception as e:
  248. print_error(f"检查过程中发生错误: {e}")
  249. import traceback
  250. traceback.print_exc()
  251. sys.exit(1)
  252. if __name__ == "__main__":
  253. main()