# 登录 Widget 设计文档 > 日期:2026-05-20 | 分支:master | 状态:已批准 ## 概述 将现有前端登录表单封装为独立的 JS Widget,嵌入方通过 ` ``` ## Widget API ### `NewApiLoginWidget.init(options)` | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `container` | `string \| Element` | 是 | 挂载点,CSS 选择器或 DOM 元素 | | `apiUrl` | `string` | 是 | new-api 服务地址 | | `onSuccess` | `(data: UserData) => void` | 否 | 登录成功回调 | | `onError` | `(err: { message: string }) => void` | 否 | 登录失败回调 | | `theme` | `'light' \| 'dark'` | 否 | 主题,默认 `'light'` | ### 返回值 `{ destroy: () => void }` — 调用 `destroy()` 卸载 Widget 并清理 DOM。 ### 回调数据格式 ```typescript // onSuccess 回调参数 interface UserData { id: number; username: string; display_name: string; role: number; status: number; group: string; } ``` ## 技术方案 ### 构建方式 在 `web/widget/` 目录下创建独立的 Vite 入口,打包成 UMD 格式。 ``` web/widget/ index.jsx # 入口:init() API,创建 Shadow DOM 并挂载 React 组件 LoginForm.jsx # 登录表单组件(参考 web/src/components/auth/LoginForm.jsx) styles.css # Widget 样式 vite.config.js # UMD 打包配置 ``` ### 样式隔离 使用 Shadow DOM 隔离 Widget 样式,防止与嵌入方 CSS 冲突。 ### API 调用 直接调用现有 `POST /api/user/login` 接口,跨域 CORS 已开启(`AllowAllOrigins + AllowCredentials`)。 请求: ```json POST {apiUrl}/api/user/login { "username": "xxx", "password": "xxx" } ``` 响应(成功): ```json { "success": true, "data": { "id": 1, "username": "user", "display_name": "User", "role": 1, "status": 1, "group": "default" } } ``` 响应(失败): ```json { "success": false, "message": "用户名或密码错误" } ``` ### 打包产物 - 输出:`web/build/static/login-widget.js` - 格式:UMD(全局暴露 `NewApiLoginWidget`) - CSS 内联到 JS bundle 中,通过 Shadow DOM 注入 - React 和 ReactDOM 打包进 bundle(避免要求嵌入方提供 React) ### 托管 由 new-api 后端的静态文件服务自动托管。构建流程: 1. `cd web && bun run build` — 构建主前端 2. `cd web/widget && bun run build` — 构建 Widget(输出到 `web/build/static/`) 3. 最终 `web/build/static/login-widget.js` 随 new-api 部署 ## 改动范围 | 位置 | 改动 | |------|------| | `web/widget/` | 新增目录,Widget 源码 | | 后端 | **无改动** | | 构建流程 | `Makefile` 或 `web/package.json` 添加 Widget 构建步骤 | ## 范围限定(当前迭代) - 仅支持**密码登录** - 不处理 2FA - 不处理 OAuth/Passkey 等其他登录方式 - 不管理嵌入方的认证状态(通过回调交由嵌入方处理)