docs: 新增V30.0版本相关设计文档与指南
新增服务器启动文档、设计说明书、风险清单等核心文档 补充前端集成蓝图、多租户实施清单、上线红线检查清单 添加质量保障文档与早期业务规格书
This commit is contained in:
238
docs/maiteng-crawler.js
Normal file
238
docs/maiteng-crawler.js
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* 麦腾后台菜单爬虫脚本
|
||||
* 用于自动探索和记录麦腾智能管理平台的所有菜单功能
|
||||
*
|
||||
* 使用方法:
|
||||
* 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();
|
||||
// })();
|
||||
|
||||
Reference in New Issue
Block a user