Files
makemd/docs/maiteng-crawler.js
Ansonai 6759d47de4 docs: 新增V30.0版本相关设计文档与指南
新增服务器启动文档、设计说明书、风险清单等核心文档
补充前端集成蓝图、多租户实施清单、上线红线检查清单
添加质量保障文档与早期业务规格书
2026-03-16 01:31:26 +08:00

239 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 麦腾后台菜单爬虫脚本
* 用于自动探索和记录麦腾智能管理平台的所有菜单功能
*
* 使用方法:
* 1. 在浏览器中打开麦腾后台并登录
* 2. 打开浏览器开发者工具 (F12)
* 3. 切换到 Console 标签
* 4. 复制粘贴此脚本并运行
* 5. 等待探索完成后,复制输出的 JSON 数据
*/
class MaitengCrawler {
constructor() {
this.discoveredMenus = [];
this.discoveredPages = [];
this.visitedUrls = new Set();
this.delayBetweenActions = 1000; // 毫秒
}
// 等待函数
async wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 获取当前页面的菜单结构
getMenuStructure() {
const menus = [];
const menuItems = document.querySelectorAll('[role="menuitem"], .ant-menu-item, .menu-item');
menuItems.forEach((item, index) => {
const menuInfo = {
id: index,
text: item.textContent?.trim() || '',
element: item,
href: item.querySelector('a')?.href || '',
isExpanded: item.classList?.contains('expanded') || false,
isCollapsed: item.classList?.contains('collapsed') || false,
hasSubmenu: item.querySelector('ul, [role="menu"]') !== null
};
if (menuInfo.text) {
menus.push(menuInfo);
}
});
return menus;
}
// 获取当前页面信息
getCurrentPageInfo() {
return {
url: window.location.href,
title: document.title,
timestamp: new Date().toISOString(),
pageElements: this.getPageElements()
};
}
// 获取页面关键元素
getPageElements() {
const elements = {
buttons: [],
inputs: [],
tables: [],
filters: []
};
// 获取按钮
document.querySelectorAll('button, [role="button"]').forEach((btn, idx) => {
elements.buttons.push({
id: idx,
text: btn.textContent?.trim() || '',
disabled: btn.disabled || false
});
});
// 获取输入框
document.querySelectorAll('input, textarea, select').forEach((input, idx) => {
elements.inputs.push({
id: idx,
type: input.tagName?.toLowerCase() || '',
placeholder: input.placeholder || '',
name: input.name || ''
});
});
// 获取表格
document.querySelectorAll('table').forEach((table, idx) => {
elements.tables.push({
id: idx,
rowCount: table.querySelectorAll('tr')?.length || 0
});
});
return elements;
}
// 尝试展开菜单
async expandMenu(menuElement) {
try {
menuElement.click();
await this.wait(this.delayBetweenActions);
return true;
} catch (error) {
console.log('展开菜单失败:', error);
return false;
}
}
// 点击菜单项并记录页面
async clickMenuItem(menuItem) {
try {
if (menuItem.href && !menuItem.href.startsWith('javascript:')) {
if (!this.visitedUrls.has(menuItem.href)) {
this.visitedUrls.add(menuItem.href);
window.location.href = menuItem.href;
await this.wait(3000); // 等待页面加载
const pageInfo = this.getCurrentPageInfo();
this.discoveredPages.push(pageInfo);
console.log('发现新页面:', pageInfo.title);
// 返回上一页继续探索
window.history.back();
await this.wait(this.delayBetweenActions);
}
} else if (menuItem.element) {
menuItem.element.click();
await this.wait(this.delayBetweenActions);
const pageInfo = this.getCurrentPageInfo();
if (!this.visitedUrls.has(pageInfo.url)) {
this.visitedUrls.add(pageInfo.url);
this.discoveredPages.push(pageInfo);
console.log('发现新页面:', pageInfo.title);
}
}
return true;
} catch (error) {
console.log('点击菜单失败:', error);
return false;
}
}
// 主爬虫函数
async crawl() {
console.log('🚀 开始麦腾后台菜单爬虫...');
console.log('当前页面:', window.location.href);
// 获取初始菜单结构
const initialMenus = this.getMenuStructure();
console.log('发现菜单数量:', initialMenus.length);
this.discoveredMenus = initialMenus;
// 尝试展开所有可展开的菜单
for (const menu of initialMenus) {
if (menu.isCollapsed && menu.hasSubmenu) {
console.log('尝试展开菜单:', menu.text);
await this.expandMenu(menu.element);
}
}
// 重新获取展开后的菜单结构
const expandedMenus = this.getMenuStructure();
console.log('展开后菜单数量:', expandedMenus.length);
this.discoveredMenus = expandedMenus;
// 记录当前页面信息
const currentPage = this.getCurrentPageInfo();
this.discoveredPages.push(currentPage);
this.visitedUrls.add(currentPage.url);
console.log('\n✅ 爬虫完成!');
console.log('\n📊 发现的菜单:');
this.discoveredMenus.forEach((menu, idx) => {
console.log(`${idx + 1}. ${menu.text} ${menu.isExpanded ? '[展开]' : menu.isCollapsed ? '[折叠]' : ''}`);
});
console.log('\n📄 发现的页面:');
this.discoveredPages.forEach((page, idx) => {
console.log(`${idx + 1}. ${page.title} - ${page.url}`);
});
// 返回完整数据
return {
menus: this.discoveredMenus.map(m => ({
text: m.text,
href: m.href,
isExpanded: m.isExpanded,
isCollapsed: m.isCollapsed
})),
pages: this.discoveredPages.map(p => ({
url: p.url,
title: p.title,
timestamp: p.timestamp,
buttons: p.pageElements.buttons.map(b => b.text),
inputs: p.pageElements.inputs.map(i => i.placeholder || i.name)
}))
};
}
// 导出数据为JSON
exportToJSON() {
const data = {
crawlTime: new Date().toISOString(),
menus: this.discoveredMenus,
pages: this.discoveredPages
};
const jsonStr = JSON.stringify(data, null, 2);
console.log('\n📋 完整JSON数据:');
console.log(jsonStr);
// 下载到文件
const blob = new Blob([jsonStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `maiteng-crawl-${Date.now()}.json`;
a.click();
URL.revokeObjectURL(url);
console.log('\n💾 JSON文件已下载');
}
}
// 使用示例
console.log('=== 麦腾后台菜单爬虫 ===');
console.log('请运行: const crawler = new MaitengCrawler(); const result = await crawler.crawl(); crawler.exportToJSON();');
// 自动运行(可选)
// (async () => {
// const crawler = new MaitengCrawler();
// const result = await crawler.crawl();
// crawler.exportToJSON();
// })();