From e26589bc543dce1a627015b3f5213003aab03dfa Mon Sep 17 00:00:00 2001 From: Zou-Seay <81679448+Zou-Seay@users.noreply.github.com> Date: Wed, 24 Jun 2026 16:02:08 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20ERP=20=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E8=87=AA=E5=8A=A8=E5=8C=96=E6=8A=93=E5=8F=96=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=B7=A5=E5=85=B7=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=BC=80=E5=8F=91=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/custom-tools-guide.md | 113 +++++ package-lock.json | 442 ------------------ .../extension/src/agent/MultiPageAgent.ts | 8 +- packages/extension/src/agent/customTools.ts | 64 +++ packages/extension/wxt.config.js | 2 +- 5 files changed, 185 insertions(+), 444 deletions(-) create mode 100644 docs/custom-tools-guide.md create mode 100644 packages/extension/src/agent/customTools.ts diff --git a/docs/custom-tools-guide.md b/docs/custom-tools-guide.md new file mode 100644 index 0000000..879d9e1 --- /dev/null +++ b/docs/custom-tools-guide.md @@ -0,0 +1,113 @@ +# Page Agent 自定义工具注册指南 + +在 Page Agent 体系中,你可以通过注册自定义工具(Custom Tools),让大模型能够执行你编写的特定 JavaScript 代码(如:复杂的批量爬虫、与系统底层的原生交互等)。 + +## 核心架构原理 +Page Agent 的 Extension 版本使用了 `MultiPageAgent` 类来初始化。大模型与浏览器的交互是通过 `PageController` 实现的。 +要增加新的工具,我们只需两步:**编写工具逻辑** -> **在实例化时注入配置**。 + +## 步骤一:创建你的自定义工具 + +在 `packages/extension/src/agent/` 目录下(或者你自己的工具管理目录),创建一个新的工具文件,例如 `customTools.ts`。 + +你需要引入 `@page-agent/core` 中的 `tool` 辅助函数,以及 `zod` 来定义参数格式: + +```typescript +// 文件路径:packages/extension/src/agent/customTools.ts +import { tool } from '@page-agent/core' +import * as z from 'zod/v4' + +export const myCustomTool = tool({ + // 【必填】Description: 给大模型看的自然语言说明书。写得越清楚,大模型调用的时机越准确。 + description: '当需要执行特定的复杂业务逻辑(如静默抓取数据)时,调用此工具。', + + // 【必填】InputSchema: 如果工具需要参数(比如你要抓取的目标页数),在这里定义。不需要参数写 z.object({}) + inputSchema: z.object({ + targetCount: z.number().optional().describe("目标抓取数量") + }), + + // 【必填】Execute: 工具被大模型触发时执行的后台代码 + execute: async function (input, { signal }) { + // 由于这段代码跑在 Chrome Extension 的 Background Worker 中,不能直接操纵网页 DOM。 + // 同时出于安全限制,插件环境禁用了底层的 executeJavascript 方法。 + // 我们必须使用 Chrome 原生的 chrome.scripting API 来向目标网页注入脚本。 + + // 1. 获取当前大模型正在操作的页面 Tab ID + const tabId = (this.pageController as any).currentTabId; + if (!tabId) return "报错:未找到激活的页面"; + + // 2. 将代码注入目标网页执行 + const results = await chrome.scripting.executeScript({ + target: { tabId: tabId }, + func: async () => { + // ... 这段代码将直接跑在网页(如金蝶 ERP)的控制台内部 + return new Promise((resolve) => { + const data = "抓取完成的数据"; + resolve(data); // 务必把数据 resolve 回后台 + }); + } + }); + + // 3. 取出网页传回来的数据 + const resultData = results[0]?.result; + + // 4. 将数据发送给后端的数据库 API (边抓边存,或最后存) + // await fetch('https://api.yourdomain.com/save', { method: 'POST', body: JSON.stringify(resultData) }); + + // 5. 返回给大模型汇报工作 + return \`工具执行成功,已处理数据:\${resultData}\`; + } +}); +``` + +> **🚨 极其关键的安全权限配置:** +> 使用 `chrome.scripting` API 前,你**必须**在插件配置文件 `packages/extension/wxt.config.js` 的 `permissions` 数组中添加 `'scripting'` 权限,否则会一直报错! +> +> ```javascript +> // wxt.config.js +> permissions: ['tabs', 'tabGroups', 'sidePanel', 'storage', 'scripting'], +> ``` + +## 步骤二:将工具注册到 Agent 实例中 + +编写完工具后,你必须告诉 Page Agent 存在这个工具。 + +打开 `packages/extension/src/agent/MultiPageAgent.ts`(或者你实例化 `PageAgent` 的地方)。在 `customTools` 属性中注册你的工具。 + +```typescript +// 文件路径:packages/extension/src/agent/MultiPageAgent.ts +import { createTabTools } from './tabTools' +import { myCustomTool } from './customTools' // 1. 引入你的工具 + +export class MultiPageAgent extends PageAgentCore { + constructor(config: MultiPageAgentConfig) { + // ... (原有的 controller 初始化代码) + + // 2. 将你的工具合并到 customTools 对象中 + const customTools = { + ...createTabTools(tabsController), // 保留原有的内置 Tab 切换工具 + my_custom_tool: myCustomTool, // 注册你的新工具,键名 (my_custom_tool) 就是工具对外的 ID + } + + super({ + // ... (原有的其他配置) + customTools: customTools, // 3. 将工具对象传递给底层配置 + }); + + // ... + } +} +``` + +## 步骤三:编译与发布插件 + +代码修改完毕后,你需要重新构建浏览器插件: + +在项目的根目录(`/Users/seay/Documents/code/page-agent`)执行以下命令: + +```bash +npm run build:ext +``` + +- 该命令会自动编译压缩代码,并在 `packages/extension/.output/` 目录下生成打包好的 `.zip` 文件(例如 `.output/page-agent-ext-1.10.0-chrome.zip`)。 +- **更新插件:** 将此 `.zip` 文件发给客户解压。在浏览器的 `chrome://extensions/` 界面中覆盖加载该文件夹,并点击“刷新”图标即可生效。大模型立刻就能使用你的新工具了。 diff --git a/package-lock.json b/package-lock.json index 5677835..c60b916 100644 --- a/package-lock.json +++ b/package-lock.json @@ -807,448 +807,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", diff --git a/packages/extension/src/agent/MultiPageAgent.ts b/packages/extension/src/agent/MultiPageAgent.ts index 7764816..d6eb334 100644 --- a/packages/extension/src/agent/MultiPageAgent.ts +++ b/packages/extension/src/agent/MultiPageAgent.ts @@ -2,6 +2,7 @@ import { type AgentConfig, PageAgentCore } from '@page-agent/core' import { RemotePageController } from './RemotePageController' import { TabsController } from './TabsController' +import { scrapeErpOrdersTool } from './customTools' import SYSTEM_PROMPT from './system_prompt.md?raw' import { createTabTools } from './tabTools' @@ -26,7 +27,12 @@ export class MultiPageAgent extends PageAgentCore { // multi page controller const tabsController = new TabsController() const pageController = new RemotePageController(tabsController) - const customTools = createTabTools(tabsController) + + // 注册内置工具以及我们自定义的 ERP 抓取工具 + const customTools = { + ...createTabTools(tabsController), + scrape_erp_orders: scrapeErpOrdersTool, + } // system prompt - auto-detect language if not specified const language = config.language ?? detectLanguage() diff --git a/packages/extension/src/agent/customTools.ts b/packages/extension/src/agent/customTools.ts new file mode 100644 index 0000000..250ac1a --- /dev/null +++ b/packages/extension/src/agent/customTools.ts @@ -0,0 +1,64 @@ +import { tool } from '@page-agent/core' +import * as z from 'zod/v4' + +export const scrapeErpOrdersTool = tool({ + description: + '当到达 ERP 订单列表或查询列表的第一页时,调用此工具。该工具会自动接管浏览器,静默抓取所有分页表格数据。', + inputSchema: z.object({}), + execute: async function (input, { signal }) { + console.log('🚀 大模型已切断,原生爬虫代码接管...') + + // 获取当前正在被 Agent 控制的标签页 ID + const tabId = (this.pageController as any).currentTabId + if (!tabId) { + return '❌ 错误:没有找到激活的标签页,无法执行抓取。' + } + + try { + // 在 Chrome 插件中,不能使用 executeJavascript,必须使用 Chrome 原生的 scripting API 注入代码 + const results = await chrome.scripting.executeScript({ + target: { tabId: tabId }, + func: async () => { + // --- 以下代码将原封不动地在金蝶/AntDesign的网页环境内部执行 --- + let allData: any[] = [] + let pageCount = 1 + while (true) { + const rows = document.querySelectorAll('tr.ant-table-row') + const pageData = Array.from(rows).map((row) => { + const cells = row.querySelectorAll('td') + return { + ruleName: cells[1] ? (cells[1] as HTMLElement).innerText.trim() : '', + desc: cells[2] ? (cells[2] as HTMLElement).innerText.trim() : '', + } + }) + + allData = allData.concat(pageData) + + const nextLi = document.querySelector('li.ant-pagination-next') + if (!nextLi || nextLi.classList.contains('ant-pagination-disabled')) { + return allData // 把数据回传 + } + + const nextBtn = nextLi.querySelector('button') + if (nextBtn) nextBtn.click() + else (nextLi as HTMLElement).click() + + pageCount++ + await new Promise((r) => setTimeout(r, 1500)) + } + // --- 网页内部代码结束 --- + }, + }) + + // 提取网页返回的数据 + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const extractedOrders = (results && results[0] ? results[0].result : []) as any + console.log('✅ 成功拿到了网页返回的全部数据:', extractedOrders) + + return `成功抓取了所有 ${extractedOrders?.length || 0} 条订单数据,并已打印到后台控制台日志中。` + } catch (error) { + console.error('执行脚本失败:', error) + return `❌ 抓取失败: ${error}` + } + }, +}) diff --git a/packages/extension/wxt.config.js b/packages/extension/wxt.config.js index 2f29be0..aa47fbe 100644 --- a/packages/extension/wxt.config.js +++ b/packages/extension/wxt.config.js @@ -45,7 +45,7 @@ export default defineConfig({ name: '__MSG_extName__', description: '__MSG_extDescription__', homepage_url: 'https://alibaba.github.io/page-agent/', - permissions: ['tabs', 'tabGroups', 'sidePanel', 'storage'], + permissions: ['tabs', 'tabGroups', 'sidePanel', 'storage', 'scripting'], host_permissions: [''], icons: { 64: 'assets/page-agent-64.png',