From db300cad451c210035c5f7be88bd5836dbe0bc85 Mon Sep 17 00:00:00 2001 From: shy3130 Date: Thu, 15 Oct 2026 12:27:35 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ui):=20=E9=A1=B5=E9=9D=A2=E5=8C=85?= =?UTF-8?q?=E6=9B=B4=E5=90=8D=E4=B8=BA=E9=A1=B5=E9=9D=A2=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 消除断句歧义, 与数据源插件命名对齐; 仅改文案, 代码层标识不动。 --- assets/architecture.svg | 2 +- backend/app/api/ext_pages.py | 18 +++++++++--------- backend/app/services/ext_pages.py | 10 +++++----- backend/tests/test_ext_pages.py | 2 +- docs/page-sdk.md | 14 +++++++------- frontend/page-pack-template/README.md | 6 +++--- frontend/page-pack-template/src/extension.tsx | 8 ++++---- frontend/page-pack-template/src/tsp.ts | 4 ++-- frontend/page-pack-template/vite.config.ts | 4 ++-- frontend/src/custom/README.md | 4 ++-- frontend/src/extensions/runtime.test.ts | 2 +- frontend/src/extensions/runtime.tsx | 18 +++++++++--------- frontend/src/main.tsx | 4 ++-- frontend/src/pages/Settings.tsx | 2 +- frontend/src/pages/settings/PagePacks.tsx | 18 +++++++++--------- 15 files changed, 58 insertions(+), 58 deletions(-) diff --git a/assets/architecture.svg b/assets/architecture.svg index 4b4afeef..ac7ad0cd 100644 --- a/assets/architecture.svg +++ b/assets/architecture.svg @@ -83,7 +83,7 @@ 供策略 / 回测 / 监控取用 页面扩展 - 11 官方页 + 页面包 + 11 官方页 + 页面插件 custom 后端 AI 助手参考实现 diff --git a/backend/app/api/ext_pages.py b/backend/app/api/ext_pages.py index c01c2185..47f2b0c6 100644 --- a/backend/app/api/ext_pages.py +++ b/backend/app/api/ext_pages.py @@ -1,4 +1,4 @@ -"""运行时页面包 API — 上传/清单回填/启停/删除/bundle 分发。 +"""运行时页面插件 API — 上传/清单回填/启停/删除/bundle 分发。 分发端点位于 /api 前缀下, 自动受集中鉴权中间件保护: 浏览器同源动态 import 会携带登录 cookie, 与页面会话同一访问边界。 @@ -33,7 +33,7 @@ async def _write_bundle_capped(file: UploadFile, dest: Path, max_bytes: int) -> break total += len(chunk) if total > max_bytes: - raise HTTPException(413, f"页面包过大(上限 {max_bytes // (1024 * 1024)}MB)") + raise HTTPException(413, f"页面插件过大(上限 {max_bytes // (1024 * 1024)}MB)") f.write(chunk) @@ -57,17 +57,17 @@ async def upload_page_pack( id: Annotated[str, Form()], file: Annotated[UploadFile, File()], ) -> PagePackMeta: - """上传页面包产物(.js/.mjs), 覆盖式: 同 ID 重新上传即更新。""" + """上传页面插件产物(.js/.mjs), 覆盖式: 同 ID 重新上传即更新。""" suffix = Path(file.filename or "").suffix.lower() if suffix not in (".js", ".mjs"): - raise HTTPException(400, "仅支持 .js / .mjs 页面包产物") + raise HTTPException(400, "仅支持 .js / .mjs 页面插件产物") store = _store(request) tmp_dir = Path(tempfile.mkdtemp()) try: tmp_path = tmp_dir / "bundle.js" await _write_bundle_capped(file, tmp_path, _MAX_BUNDLE_BYTES) if tmp_path.stat().st_size == 0: - raise HTTPException(400, "页面包为空文件") + raise HTTPException(400, "页面插件为空文件") try: return store.save_upload(id, tmp_path) except ValueError as e: @@ -91,7 +91,7 @@ def get_page_pack_bundle(pack_id: str, request: Request) -> FileResponse: raise HTTPException(400, str(e)) from e bundle = store.bundle_path(pack_id) if meta else None if meta is None or bundle is None: - raise HTTPException(404, f"页面包 '{pack_id}' 不存在") + raise HTTPException(404, f"页面插件 '{pack_id}' 不存在") return FileResponse( bundle, media_type="text/javascript", @@ -109,7 +109,7 @@ def put_page_pack_manifest(pack_id: str, body: ManifestBody, request: Request) - try: return store.update_manifest(pack_id, label=body.label, path=body.path, error=body.error) except KeyError: - raise HTTPException(404, f"页面包 '{pack_id}' 不存在") from None + raise HTTPException(404, f"页面插件 '{pack_id}' 不存在") from None except ValueError as e: raise HTTPException(400, str(e)) from e @@ -120,7 +120,7 @@ def put_page_pack_enabled(pack_id: str, body: EnabledBody, request: Request) -> try: return store.set_enabled(pack_id, body.enabled) except KeyError: - raise HTTPException(404, f"页面包 '{pack_id}' 不存在") from None + raise HTTPException(404, f"页面插件 '{pack_id}' 不存在") from None except ValueError as e: raise HTTPException(400, str(e)) from e @@ -130,7 +130,7 @@ def delete_page_pack(pack_id: str, request: Request) -> dict: store = _store(request) try: if not store.delete(pack_id): - raise HTTPException(404, f"页面包 '{pack_id}' 不存在") + raise HTTPException(404, f"页面插件 '{pack_id}' 不存在") except ValueError as e: raise HTTPException(400, str(e)) from e return {"ok": True} diff --git a/backend/app/services/ext_pages.py b/backend/app/services/ext_pages.py index 8c1ecb94..7dd67a1e 100644 --- a/backend/app/services/ext_pages.py +++ b/backend/app/services/ext_pages.py @@ -1,6 +1,6 @@ -"""运行时页面包存储 — data/ext_pages/{id}/ 目录式持久化。 +"""运行时页面插件存储 — data/ext_pages/{id}/ 目录式持久化。 -页面包是前端扩展的打包产物(单文件 ES 模块): 上传后由前端在启动引导窗口 +页面插件是前端扩展的打包产物(单文件 ES 模块): 上传后由前端在启动引导窗口 动态 import 挂载(见 frontend/src/extensions/runtime.ts)。后端只负责存储、 元数据与分发, 不解析 JS 内容 —— 清单契约(能否 import、apiVersion、路由 冲突)由前端注册表校验, 校验结果经 manifest 接口回填到 page.json。 @@ -41,7 +41,7 @@ def _sha256_file(path: Path) -> str: class PagePackStore: - """每个页面包一个目录: page.json(元数据) + bundle.js(产物)。 + """每个页面插件一个目录: page.json(元数据) + bundle.js(产物)。 元数据写入走 临时文件+rename, 与 ext_data 的原子写同语义, 避免半写 状态被启动扫描读到。 @@ -53,7 +53,7 @@ class PagePackStore: def _dir(self, pack_id: str) -> Path: if not _VALID_ID.match(pack_id or ""): - raise ValueError("页面包 ID 仅允许字母/数字/下划线, 长度 1-64") + raise ValueError("页面插件 ID 仅允许字母/数字/下划线, 长度 1-64") return self.data_dir / pack_id def _write_meta(self, meta: PagePackMeta) -> None: @@ -86,7 +86,7 @@ class PagePackStore: try: return PagePackMeta(**json.loads(p.read_text(encoding="utf-8"))) except (json.JSONDecodeError, TypeError): - # 半写/损坏的元数据按损坏页处理, 不阻断其他页面包 + # 半写/损坏的元数据按损坏页处理, 不阻断其他页面插件 return PagePackMeta(id=pack_id, error="page.json 损坏, 请重新上传") def bundle_path(self, pack_id: str) -> Path | None: diff --git a/backend/tests/test_ext_pages.py b/backend/tests/test_ext_pages.py index 6e0350bf..be4f34a6 100644 --- a/backend/tests/test_ext_pages.py +++ b/backend/tests/test_ext_pages.py @@ -1,4 +1,4 @@ -"""运行时页面包 API 测试 — 上传校验/生命周期/bundle 分发。 +"""运行时页面插件 API 测试 — 上传校验/生命周期/bundle 分发。 TestClient 最小 app 风格(同 test_ext_market_level): 不起完整后端, app.state.repo 只提供 store.data_dir。 diff --git a/docs/page-sdk.md b/docs/page-sdk.md index 66e494d4..3ece4f32 100644 --- a/docs/page-sdk.md +++ b/docs/page-sdk.md @@ -1,6 +1,6 @@ -# 页面包开发指南 (Page Pack SDK v1) +# 页面插件开发指南 (Page Pack SDK v1) -页面包是**运行时可挂载的页面产物**:把页面打包成单文件 ES 模块(`extension.js`),在「设置 → 页面包」上传,刷新后即成为完整页面——自动注册路由、侧栏入口,可在菜单设置里排序/隐藏。本地 dev、生产构建、桌面端三种运行形态一致生效,宿主无需重新构建。 +页面插件是**运行时可挂载的页面产物**:把页面打包成单文件 ES 模块(`extension.js`),在「设置 → 页面插件」上传,刷新后即成为完整页面——自动注册路由、侧栏入口,可在菜单设置里排序/隐藏。本地 dev、生产构建、桌面端三种运行形态一致生效,宿主无需重新构建。 ## 快速开始 @@ -9,7 +9,7 @@ cp -r frontend/page-pack-template my-pack && cd my-pack pnpm install && pnpm build # → dist/extension.js ``` -打开宿主「设置 → 页面包」→ 填 ID(字母/数字/下划线)→ 选择 `dist/extension.js` → 上传并校验 → 刷新页面。 +打开宿主「设置 → 页面插件」→ 填 ID(字母/数字/下划线)→ 选择 `dist/extension.js` → 上传并校验 → 刷新页面。 ## 产物契约 (apiVersion 1) @@ -29,7 +29,7 @@ const extension = { }, }], navigation: [{ - id: 'hello', routeId: 'hello', label: '页面包演示', icon: 'Sparkles', + id: 'hello', routeId: 'hello', label: '页面插件演示', icon: 'Sparkles', }], } export default extension @@ -39,7 +39,7 @@ export default extension ## 能力桥 ctx -页面包**自包含**(React 等依赖由模板打进产物,与宿主不共享模块实例),宿主能力经 `ctx` 暴露: +页面插件**自包含**(React 等依赖由模板打进产物,与宿主不共享模块实例),宿主能力经 `ctx` 暴露: | 能力 | 说明 | | --- | --- | @@ -60,13 +60,13 @@ export default extension - **路由集成**:页面挂在宿主路由容器内(`/你的路径` 可直达、可刷新),但包内不共享宿主 Router 上下文——需要跳转宿主页面时用 `window.location` 或自行包 `MemoryRouter`。 - **生效方式**:上传/启停后**刷新页面生效**(挂载发生在启动引导窗口),不做热插拔。 - **大小**:上限 5MB/包;自包含 React 后典型产物 60–150KB(minify + 可自行加 brotli/gzip 由后端协商)。 -- **安全**:页面包是任意代码,与登录会话同权运行——只上传自己或可信来源的产物;这是单用户产品的既定边界,第三方插件沙箱不在 v1 范围。 +- **安全**:页面插件是任意代码,与登录会话同权运行——只上传自己或可信来源的产物;这是单用户产品的既定边界,第三方插件沙箱不在 v1 范围。 - **版本**:宿主升级破坏契约时提升 `apiVersion` 并保留旧版兼容期;产物内 `apiVersion` 不匹配会被明确拒绝。 ## 与其他扩展方式的取舍 | 方式 | 适用 | 见 | | --- | --- | --- | -| 页面包(运行时产物) | 免重建挂载、独立分发 | 本文档 | +| 页面插件(运行时产物) | 免重建挂载、独立分发 | 本文档 | | 源码级扩展 `frontend/src/custom//extension.tsx` | 随宿主一起构建维护、可用全部核心模块 | `docs/secondary-development.md` §3 | | 声明式分析页面(零代码) | 扩展数据 + 模板化列表/分组页 | 设置 → 扩展页面 | diff --git a/frontend/page-pack-template/README.md b/frontend/page-pack-template/README.md index d5cfa723..db5b41ba 100644 --- a/frontend/page-pack-template/README.md +++ b/frontend/page-pack-template/README.md @@ -1,6 +1,6 @@ -# 页面包模板 (Page Pack Template) +# 页面插件模板 (Page Pack Template) -把一个页面打包成**单文件 ES 模块产物**(`dist/extension.js`),上传到运行中的面板(设置 → 页面包),刷新后即成为完整页面——自动注册路由与侧栏入口,可在菜单设置里排序/隐藏。本地 dev、Docker、桌面端一致生效,**宿主无需重新构建**。 +把一个页面打包成**单文件 ES 模块产物**(`dist/extension.js`),上传到运行中的面板(设置 → 页面插件),刷新后即成为完整页面——自动注册路由与侧栏入口,可在菜单设置里排序/隐藏。本地 dev、Docker、桌面端一致生效,**宿主无需重新构建**。 ## 打包 @@ -13,7 +13,7 @@ pnpm build # → dist/extension.js ## 上传 -面板 **设置 → 页面包**:填 ID(字母/数字/下划线)→ 选择 `dist/extension.js` → 上传并校验(即时试载,清单不合法或路由冲突会被拒绝并显示原因)→ 启用 → 刷新页面。 +面板 **设置 → 页面插件**:填 ID(字母/数字/下划线)→ 选择 `dist/extension.js` → 上传并校验(即时试载,清单不合法或路由冲突会被拒绝并显示原因)→ 启用 → 刷新页面。 ## 开发要点 diff --git a/frontend/page-pack-template/src/extension.tsx b/frontend/page-pack-template/src/extension.tsx index 2cb67f01..c0b10655 100644 --- a/frontend/page-pack-template/src/extension.tsx +++ b/frontend/page-pack-template/src/extension.tsx @@ -1,5 +1,5 @@ /** - * 页面包示例 — 复制本模板改造你的页面。 + * 页面插件示例 — 复制本模板改造你的页面。 * * 产物契约(v1): * - default 导出清单对象; apiVersion 固定 1 @@ -51,9 +51,9 @@ function HelloPage() { return (
-

页面包演示页

+

页面插件演示页

- 这是一个自包含的运行时页面: 打包成单文件 ES 模块后上传到「设置 → 页面包」, + 这是一个自包含的运行时页面: 打包成单文件 ES 模块后上传到「设置 → 页面插件」, 刷新即出现在侧栏。{serverTime}