diff --git a/docs/erp-login-debug-guide.md b/docs/erp-login-debug-guide.md new file mode 100644 index 0000000..b5a2c39 --- /dev/null +++ b/docs/erp-login-debug-guide.md @@ -0,0 +1,236 @@ +# ERP 登录调试工具使用说明 + +## 目的 + +用于人工调试 ERP 登录流程,定位主界面特征元素,以便优化登录成功的判定逻辑。 + +## 前置准备 + +### 1. 配置 ERP 登录信息 + +编辑 `src/main/tools/erp-login-debug.ts` 文件,修改以下配置: + +```typescript +const ERP_CONFIG = { + url: 'https://your-erp-server.com', // ← 修改为你的 ERP 地址 + username: 'your_username', // ← 修改为你的用户名 + password: 'your_password' // ← 修改为你的密码 +} +``` + +### 2. 确保 tsx 已安装 + +如果运行时报错提示找不到 `tsx`,请安装: + +```bash +npm install -g tsx +# 或作为项目依赖 +npm install --save-dev tsx +``` + +## 使用方法 + +### 方式一:使用 npm 脚本(推荐) + +```bash +npm run debug:erp-login +``` + +### 方式二:直接运行 + +```bash +npx tsx src/main/tools/erp-login-debug.ts +``` + +## 调试流程 + +### 步骤 1:启动脚本 + +运行命令后,脚本会显示配置信息并等待你确认: + +``` +============================================================ +ERP 登录调试工具 +============================================================ +目标 URL: https://your-erp-server.com +用户名:your_username +密码: *** +============================================================ + +操作步骤: +1. 浏览器将自动打开并尝试登录 +2. 如果登录失败,请检查配置或手动重试 +3. 登录成功后,会自动暂停并打开开发者工具 +4. 使用元素选择器定位主界面特征元素 +5. 记录元素选择器,按 Ctrl+C 退出脚本 + +按 Enter 键开始... +``` + +### 步骤 2:自动登录 + +脚本会自动执行: + +- 打开浏览器 +- 导航到登录页面 +- 输入用户名和密码 +- 点击登录按钮 +- 处理强制登录确认对话框(如果有) + +### 步骤 3:人工元素定位 + +登录成功后,脚本会暂停并显示: + +``` +============================================================ +✓ 登录成功! +============================================================ + +现在进入调试模式,请进行以下操作: + +1. 按 F12 打开浏览器开发者工具 +2. 使用元素选择器 (Ctrl+Shift+C) 点击主界面特征元素 +3. 在 Elements 面板中右键元素 → Copy → Copy selector +4. 或者使用 Playwright Inspector: + - 在控制台输入:await page.pause() + - 使用 Inspector 的元素选择工具 + +建议定位的特征元素: +- 主界面顶部导航栏 +- 侧边菜单栏 +- 主内容区域的唯一标识 +- 用户信息显示区域 +- 任何登录后独有的界面元素 + +============================================================ +``` + +### 步骤 4:记录元素选择器 + +在开发者工具中: + +1. **使用元素选择器** (Ctrl+Shift+C) 点击界面元素 +2. **在 Elements 面板** 查看元素 HTML +3. **右键元素** → Copy → 选择以下之一: + - `Copy selector` - CSS 选择器 + - `Copy XPath` - XPath 路径 + - `Copy JS path` - JavaScript 路径 + +### 步骤 5:更新 locators.ts + +将找到的元素选择器添加到 `src/main/services/erp/locators.ts`: + +```typescript +export const ERP_LOCATORS = { + // ... 现有配置 ... + + // 新增:主界面特征元素(用于登录成功判定) + mainPage: { + topNavigationBar: '#top-nav', // 顶部导航栏 + sideMenu: '.side-menu', // 侧边菜单 + userProfile: '.user-profile', // 用户信息 + welcomeMessage: 'internal:has-text="欢迎"' // 欢迎消息 + } +} +``` + +### 步骤 6:退出脚本 + +按 `Ctrl+C` 终止脚本,浏览器会在 5 秒后自动关闭。 + +## Playwright Inspector 使用技巧 + +### 开启 Inspector + +在脚本暂停时,在浏览器控制台输入: + +```javascript +await page.pause() +``` + +会打开 Playwright Inspector,提供: + +- 元素选择器 +- 实时 locator 测试 +- 代码生成 + +### 测试 Locator + +在 Inspector 控制台测试 locator 是否有效: + +```javascript +// 测试 CSS 选择器 +await page.locator('#top-nav').count() + +// 测试 role-based 选择器 +await page.getByRole('navigation').count() + +// 测试文本选择器 +await page.getByText('欢迎').count() +``` + +如果返回数量 > 0,说明选择器有效。 + +## 推荐的特征元素 + +选择登录成功判定元素时,优先选择: + +1. **唯一性** - 只在登录后出现 +2. **稳定性** - 不易随版本变更 +3. **易定位** - 有明确的 id、class 或文本 + +### 推荐元素示例 + +| 元素类型 | 选择器示例 | 说明 | +| ------------ | ----------------------- | ---------------------- | +| 顶部导航栏 | `#top-nav` | 登录后才会显示的主导航 | +| 用户菜单 | `.user-menu` | 显示当前用户名的菜单 | +| 欢迎消息 | `text=欢迎` | 包含用户名的欢迎语 | +| 工作台标题 | `h1:has-text("工作台")` | 主界面标题 | +| 功能模块网格 | `.module-grid` | 功能模块入口区域 | + +## 常见问题 + +### Q: 登录失败,提示找不到元素 + +**A**: 检查以下几点: + +1. ERP URL 是否正确 +2. 用户名密码是否正确 +3. 网络连接是否正常 +4. ERP 系统是否可访问 +5. 是否需要验证码(如果 ERP 有验证码,需要手动输入) + +### Q: 登录后没有暂停 + +**A**: 检查控制台输出,可能登录流程中抛出了异常。查看错误信息并修复。 + +### Q: 如何调试特定页面? + +**A**: 修改脚本中的登录后逻辑,导航到特定页面: + +```typescript +// 登录后导航到特定页面 +await page.goto(`${ERP_CONFIG.url}/yonbip/sc`) +await page.waitForTimeout(3000) +``` + +### Q: 如何保存调试会话? + +**A**: Playwright 支持录制 trace: + +```typescript +await context.tracing.start({ screenshots: true, snapshots: true }) +// ... 操作 ... +await context.tracing.stop({ path: 'trace.zip' }) +``` + +然后使用 `npx playwright show-trace trace.zip` 查看。 + +## 下一步 + +找到稳定的主界面元素后,修改以下文件优化登录判定: + +1. **更新 locators.ts** - 添加主界面元素定位器 +2. **修改 erp-auth.ts** - 在登录成功后等待主界面元素 +3. **更新测试** - 验证新的登录判定逻辑 diff --git a/docs/erp-login-debug-quickref.md b/docs/erp-login-debug-quickref.md new file mode 100644 index 0000000..c0967a9 --- /dev/null +++ b/docs/erp-login-debug-quickref.md @@ -0,0 +1,241 @@ +# ERP 登录调试工具 - 快速参考 + +## 创建的文件 + +### 1. 调试脚本 + +**路径**: `src/main/tools/erp-login-debug.ts` + +用途:人工调试 ERP 登录流程,定位主界面特征元素 + +### 2. 使用文档 + +**路径**: `docs/erp-login-debug-guide.md` + +详细的调试工具使用说明 + +### 3. package.json 更新 + +添加了新的 npm 脚本和依赖: + +- `debug:erp-login` - 运行调试脚本 +- `debug:config-path` - 运行配置路径调试(已有) +- `tsx` - TypeScript 执行器依赖 + +## 快速开始 + +### 步骤 1:配置登录信息 + +编辑 `src/main/tools/erp-login-debug.ts` 第 19-23 行: + +```typescript +const ERP_CONFIG = { + url: 'https://your-erp-server.com', // ← 修改 + username: 'your_username', // ← 修改 + password: 'your_password' // ← 修改 +} +``` + +### 步骤 2:运行调试 + +```bash +npm run debug:erp-login +``` + +### 步骤 3:定位元素 + +登录成功后: + +1. 按 **F12** 打开开发者工具 +2. 按 **Ctrl+Shift+C** 启用元素选择器 +3. 点击主界面特征元素 +4. 右键 → Copy → Copy selector + +### 步骤 4:更新定位器 + +将找到的元素添加到 `src/main/services/erp/locators.ts`: + +```typescript +export const ERP_LOCATORS = { + // ... 现有配置 ... + + // 新增:主界面特征元素 + mainPage: { + // 在此添加找到的元素 + topNav: '#top-nav', + userMenu: '.user-menu' + } +} +``` + +## 脚本功能 + +### 自动执行 + +- ✅ 启动浏览器(可见窗口,非无头模式) +- ✅ 导航到登录页面 +- ✅ 输入用户名和密码 +- ✅ 点击登录按钮 +- ✅ 处理强制登录确认对话框 + +### 调试支持 + +- ✅ 登录成功后自动暂停 +- ✅ 保持浏览器打开 +- ✅ 支持 F12 开发者工具 +- ✅ 支持 Playwright Inspector + +### 安全特性 + +- ✅ 密码显示为星号 +- ✅ 需要按 Enter 确认后才开始 +- ✅ 退出前 5 秒缓冲时间 + +## 常用命令 + +```bash +# 运行调试脚本 +npm run debug:erp-login + +# 或使用 npx 直接运行 +npx tsx src/main/tools/erp-login-debug.ts + +# 查看帮助 +npx tsx --help +``` + +## 调试技巧 + +### 测试 Locator 有效性 + +在浏览器控制台(登录后暂停时): + +```javascript +// 测试 CSS 选择器 +await page.locator('#top-nav').count() + +// 测试文本选择器 +await page.getByText('欢迎').isVisible() + +// 测试 role 选择器 +await page.getByRole('navigation').count() +``` + +返回值 > 0 或 true 表示选择器有效。 + +### 查看元素详细信息 + +```javascript +// 获取元素 HTML +const element = await page.$('#top-nav') +console.log(await element.innerHTML()) + +// 获取元素属性 +console.log(await element.getAttributes()) +``` + +### 截图保存 + +```javascript +// 全屏截图 +await page.screenshot({ path: 'login-success.png' }) + +// 元素截图 +const element = await page.$('#top-nav') +await element.screenshot({ path: 'top-nav.png' }) +``` + +## 推荐的特征元素 + +选择登录成功判定元素的标准: + +| 标准 | 说明 | 示例 | +| ---------- | -------------- | ------------------- | +| **唯一性** | 只在登录后出现 | 用户菜单、工作台 | +| **稳定性** | 不易随版本变更 | ID 选择器优于 class | +| **易定位** | 有明确的标识 | 有 id、独特文本 | + +### 推荐元素类型 + +1. **顶部导航栏** - `#top-nav`, `.navbar` +2. **用户信息区域** - `.user-info`, `.user-menu` +3. **欢迎消息** - 包含用户名的文本 +4. **功能模块入口** - 主界面的模块网格 +5. **侧边菜单栏** - `.sidebar`, `.menu` + +## 故障排查 + +### 问题:脚本启动后立即退出 + +**原因**: tsx 未安装 + +**解决**: + +```bash +npm install +``` + +### 问题:找不到用户名/密码输入框 + +**原因**: + +1. ERP URL 不正确 +2. 页面结构已变更 +3. 登录页面加载超时 + +**解决**: + +1. 检查 ERP_CONFIG.url 是否正确 +2. 手动打开 URL 确认页面结构 +3. 增加 timeout 值(第 25 行) + +### 问题:登录后没有暂停 + +**原因**: 登录流程抛出异常 + +**解决**: 查看控制台错误信息,检查: + +- 网络连接 +- ERP 系统可用性 +- 用户名密码正确性 + +### 问题:无法定位元素 + +**原因**: + +1. 元素在 iframe 中 +2. 元素动态加载 +3. 选择器不正确 + +**解决**: + +1. 检查元素是否在嵌套 iframe 中 +2. 增加等待时间 `await page.waitForTimeout(2000)` +3. 使用更具体的选择器 + +## 下一步 + +找到稳定的主界面元素后: + +1. **更新 locators.ts** + - 添加 `mainPage` 配置节 + - 定义登录成功判定元素 + +2. **修改 erp-auth.ts** + - 在 `login()` 方法末尾 + - 等待主界面元素出现 + - 作为登录成功的最终判定 + +3. **验证修改** + - 重新运行调试脚本 + - 确认新的判定逻辑有效 + - 更新相关文档 + +## 相关文件 + +| 文件 | 用途 | +| ----------------------------------- | ---------- | +| `src/main/tools/erp-login-debug.ts` | 调试脚本 | +| `src/main/services/erp/locators.ts` | 元素定位器 | +| `src/main/services/erp/erp-auth.ts` | 登录服务 | +| `docs/erp-login-debug-guide.md` | 详细文档 | diff --git a/package-lock.json b/package-lock.json index 456a348..1d17705 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "react": "^19.2.1", "react-dom": "^19.2.1", "tailwindcss": "^4.2.1", + "tsx": "^4.19.3", "typescript": "^5.9.3", "vite": "^7.2.6", "vitest": "^4.0.18" @@ -7149,6 +7150,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -10582,6 +10596,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", @@ -11861,6 +11885,526 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "devOptional": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/tsx/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 6ade0d7..08a0a7d 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ "test:coverage": "vitest run --coverage", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", - "test:e2e:report": "playwright show-report" + "test:e2e:report": "playwright show-report", + "debug:erp-login": "tsx src/main/tools/erp-login-debug.ts", + "debug:config-path": "tsx src/main/tools/config-path-debug.ts" }, "dependencies": { "@electron-toolkit/preload": "^3.0.2", @@ -74,6 +76,7 @@ "tailwindcss": "^4.2.1", "typescript": "^5.9.3", "vite": "^7.2.6", - "vitest": "^4.0.18" + "vitest": "^4.0.18", + "tsx": "^4.19.3" } } diff --git a/src/main/tools/erp-login-debug.ts b/src/main/tools/erp-login-debug.ts new file mode 100644 index 0000000..e4e2526 --- /dev/null +++ b/src/main/tools/erp-login-debug.ts @@ -0,0 +1,232 @@ +/** + * ERP 登录调试脚本 + * + * 用途:人工调试 ERP 登录流程,定位主界面特征元素 + * 使用方式: + * 1. 修改下方的 ERP_CONFIG 配置(URL、用户名、密码) + * 2. 运行:npx ts-node src/main/tools/erp-login-debug.ts + * 3. 登录后会自动暂停,打开 Playwright Inspector 进行元素定位 + * 4. 在 Inspector 中点击主界面元素,获取选择器 + * 5. 将找到的元素选择器添加到 locators.ts + * + * 调试模式说明: + * - headless: false - 显示浏览器窗口 + * - slowMo: 100 - 放慢操作速度便于观察 + * - debugger 语句会暂停执行,打开开发者工具 + */ + +import { chromium } from 'playwright' +import path from 'path' + +// ==================== 配置区域 ==================== +const ERP_CONFIG = { + url: 'https://68.11.34.30:8082/', // ← 修改为你的 ERP 地址 + username: 'BLDpengqiangqiang', // ← 修改为你的用户名 + password: 'Cqbld123456.' // ← 修改为你的密码 +} + +// 调试配置 +const DEBUG_CONFIG = { + headless: false, // 必须为 false,需要看到浏览器 + slowMo: 100, // 操作间隔(毫秒) + timeout: 60000 // 超时时间(毫秒) +} +// ================================================ + +async function debugErpLogin(): Promise { + console.log('='.repeat(60)) + console.log('ERP 登录调试工具') + console.log('='.repeat(60)) + console.log('目标 URL:', ERP_CONFIG.url) + console.log('用户名:', ERP_CONFIG.username) + console.log('密码:', '***'.repeat(ERP_CONFIG.password.length)) + console.log('='.repeat(60)) + console.log() + console.log('操作步骤:') + console.log('1. 浏览器将自动打开并尝试登录') + console.log('2. 如果登录失败,请检查配置或手动重试') + console.log('3. 登录成功后,会自动暂停并打开开发者工具') + console.log('4. 使用元素选择器定位主界面特征元素') + console.log('5. 记录元素选择器,按 Ctrl+C 退出脚本') + console.log() + console.log('按 Enter 键开始...') + + // 等待用户确认 + await new Promise((resolve) => { + process.stdin.resume() + process.stdin.once('data', () => { + process.stdin.pause() + resolve() + }) + }) + + let browser: any = null + let context: any = null + let page: any = null + + try { + console.log('\n正在启动浏览器...') + + // 启动浏览器(带调试配置) + browser = await chromium.launch({ + headless: DEBUG_CONFIG.headless, + slowMo: DEBUG_CONFIG.slowMo, + args: ['--ignore-certificate-errors', '--ignore-ssl-errors', '--disable-web-security'] + }) + + context = await browser.newContext({ + acceptDownloads: true, + viewport: { width: 1920, height: 1080 }, + ignoreHTTPSErrors: true, + javaScriptEnabled: true + }) + + page = await context.newPage() + + // 启用详细的控制台日志 + page.on('console', (msg: any) => { + console.log(`[Page Console] ${msg.type()}: ${msg.text()}`) + }) + + page.on('pageerror', (error: any) => { + console.error(`[Page Error] ${error.message}`) + }) + + // 导航到登录页面 + const loginUrl = `${ERP_CONFIG.url}/yonbip/resources/uap/rbac/login/main/index.html` + console.log(`正在加载登录页面:${loginUrl}`) + await page.goto(loginUrl, { timeout: DEBUG_CONFIG.timeout }) + await page.waitForLoadState('domcontentloaded') + console.log('登录页面已加载') + + // 等待并定位登录表单 iframe + console.log('等待登录表单...') + await page.waitForSelector('#forwardFrame', { state: 'attached', timeout: 15000 }) + const frameLocator = page.locator('#forwardFrame') + const contentFrame = await frameLocator.contentFrame() + + if (!contentFrame) { + throw new Error('无法访问 forwardFrame 内容框架') + } + + const mainFrame = contentFrame + console.log('登录表单框架已就绪') + + // 填充用户名 + console.log('正在输入用户名...') + try { + const usernameInput = mainFrame.getByRole('textbox', { name: '用户名' }) + await usernameInput.waitFor({ state: 'visible', timeout: 5000 }) + await usernameInput.fill(ERP_CONFIG.username) + console.log('用户名已输入') + } catch (e: any) { + console.error('找不到用户名输入框:', e.message) + throw new Error('找不到用户名输入框,请检查页面结构是否改变') + } + + // 填充密码 + console.log('正在输入密码...') + try { + const passwordInput = mainFrame.getByRole('textbox', { name: '密码' }) + await passwordInput.waitFor({ state: 'visible', timeout: 5000 }) + await passwordInput.fill(ERP_CONFIG.password) + console.log('密码已输入') + } catch (e: any) { + console.error('找不到密码输入框:', e.message) + throw new Error('找不到密码输入框,请检查页面结构是否改变') + } + await page.pause() + // 点击登录按钮 + console.log('正在点击登录按钮...') + try { + const loginButton = mainFrame.getByRole('button', { name: '登录' }) + await loginButton.waitFor({ state: 'visible', timeout: 5000 }) + await loginButton.click() + console.log('已点击登录按钮') + } catch (e: any) { + console.error('找不到登录按钮:', e.message) + throw new Error('找不到登录按钮,请检查页面结构是否改变') + } + + // 等待页面加载 + console.log('等待登录响应...') + await page.waitForLoadState('domcontentloaded', { timeout: 10000 }) + await page.waitForTimeout(2000) // 额外等待,确保页面完全加载 + + // 处理强制登录确认对话框 + try { + const confirmBtn = mainFrame.getByRole('button', { name: '确定' }) + const count = await confirmBtn.count() + if (count > 0) { + console.log('检测到强制登录确认对话框,正在确认...') + await confirmBtn.first().click() + await page.waitForTimeout(2000) + console.log('已确认强制登录') + } else { + console.log('普通登录,无需确认') + } + } catch { + console.log('未检测到确认对话框') + } + + // ==================== 登录成功,进入调试模式 ==================== + console.log() + console.log('='.repeat(60)) + console.log('✓ 登录成功!') + console.log('='.repeat(60)) + console.log() + console.log('现在进入调试模式,请进行以下操作:') + console.log() + console.log('1. 按 F12 打开浏览器开发者工具') + console.log('2. 使用元素选择器 (Ctrl+Shift+C) 点击主界面特征元素') + console.log('3. 在 Elements 面板中右键元素 → Copy → Copy selector') + console.log('4. 或者使用 Playwright Inspector:') + console.log(' - 在控制台输入: await page.pause()') + console.log(' - 使用 Inspector 的元素选择工具') + console.log() + console.log('建议定位的特征元素:') + console.log('- 主界面顶部导航栏') + console.log('- 侧边菜单栏') + console.log('- 主内容区域的唯一标识') + console.log('- 用户信息显示区域') + console.log('- 任何登录后独有的界面元素') + console.log() + console.log('按 Ctrl+C 退出脚本') + console.log('='.repeat(60)) + + // 保持浏览器打开,等待用户调试 + // 使用 pause 可以让用户手动恢复或使用 Inspector + await page.pause() + } catch (error: any) { + console.error() + console.error('='.repeat(60)) + console.error('错误:', error.message) + console.error('='.repeat(60)) + console.error() + console.error('调试提示:') + console.error('1. 检查 ERP_CONFIG 配置是否正确') + console.error('2. 检查网络连接') + console.error('3. 确认 ERP 系统可访问') + console.error('4. 如果是元素找不到,可能需要更新 locators.ts') + } finally { + // 清理资源(给用户时间保存信息) + console.log() + console.log('等待 5 秒后关闭浏览器...') + await new Promise((resolve) => setTimeout(resolve, 5000)) + + if (context) { + await context.close() + } + if (browser) { + await browser.close() + } + console.log('浏览器已关闭') + process.exit(0) + } +} + +// 运行调试脚本 +debugErpLogin().catch((err) => { + console.error('脚本执行失败:', err) + process.exit(1) +})