@round2ai/r2-cli 1.0.12-beta.0 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  R2-CLI — 二手潮奢交易命令行工具,由 [Round2AI](https://github.com/Round2AI) 团队维护 — 让人类和 AI Agent 都能在终端中完成商品上架等交易操作。
7
7
 
8
- 覆盖商品上架、挂售、认证登录等核心业务域,提供 12 个命令及 3 个 AI Agent [Skills](./skills/)。
8
+ 覆盖商品上架、挂售、商品信息修改、认证登录等核心业务域,提供 16 个命令及 3 个 AI Agent [Skills](./skills/)。
9
9
 
10
10
  [安装](#安装与快速开始) · [AI Agent 快速开始](#快速开始ai-agent) · [Agent Skills](#agent-skills) · [认证](#认证) · [命令](#命令参考) · [安全](#安全与风险提示)
11
11
 
@@ -21,8 +21,8 @@ R2-CLI — 二手潮奢交易命令行工具,由 [Round2AI](https://github.com
21
21
  | 类别 | 能力 |
22
22
  |------|------|
23
23
  | 认证登录 | 扫码登录(第二回合 APP / 微信 / 支付宝)、闲鱼店铺授权、状态查询、登出(Agent 一步式流程) |
24
- | 商品管理 | 商品上架(4 步流程:获取店铺 → 获取仓库 → 获取选品商品 → 提交上架 + 自动轮询上架结果)、店铺查看、仓库查看、选品商品查看、上架列表查询、下架、改价 |
25
- | 闲鱼挂售 | 完整商品信息模式:类目查询属性/品牌选择图片上传 → 提交挂售 |
24
+ | 商品管理 | 商品上架(4 步流程:获取店铺 → 获取仓库 → 获取选品商品 → 提交上架 + 自动轮询上架结果)、商品信息修改(AI 读图识别 → 自动匹配类目/属性 → 提交修改)、店铺查看、仓库查看、选品商品查看、上架列表查询、下架、改价 |
25
+ | 闲鱼挂售 | 图片上传AI 读图识别商品信息 自动匹配类目/属性 → 提交挂售 |
26
26
 
27
27
  ---
28
28
 
@@ -117,7 +117,7 @@ npx skills add Round2AI/r2-cli --all -y
117
117
  |-------|------|
118
118
  | `r2-shared` | 共享基础:安装、统一错误格式、命令概览 |
119
119
  | `r2-auth` | 认证登录:一步式扫码登录(生成二维码 + 自动轮询,支持第二回合 APP / 微信 / 支付宝)、闲鱼店铺授权、状态查询、登出 |
120
- | `r2-goods` | 商品管理:4 步上架流程、挂售流程(类目→属性→图片→提交)、下架、改价、店铺/仓库/商品/上架列表查看 |
120
+ | `r2-goods` | 商品管理:4 步上架流程、挂售流程、商品信息修改(AI 读图识别 → 自动匹配类目/属性 → 提交)、下架、改价、店铺/仓库/商品/上架列表查看 |
121
121
 
122
122
  ---
123
123
 
@@ -149,7 +149,7 @@ Token 存储在 `~/.r2-cli/config.json`(原子写入防丢失),过期后
149
149
  | `r2-cli goods list [--stock-id <id>] [--stock-goods-id <id>] [--json]` | 查看选品商品(可按仓库或商品 ID 过滤,支持 `--page` 和 `--size`) |
150
150
  | `r2-cli goods listing [--json]` | 查询上架列表(支持 `--id` / `--shop-id` / `--stock-goods-id` / `--stock-id` / `--status` / `--platform` 过滤) |
151
151
 
152
- ### 商品上架/下架
152
+ ### 商品上架/下架/改价
153
153
 
154
154
  | 命令 | 说明 |
155
155
  |------|------|
@@ -158,9 +158,22 @@ Token 存储在 `~/.r2-cli/config.json`(原子写入防丢失),过期后
158
158
  | `r2-cli goods down --id <id> [--json]` | 下架商品(也可用 `--stock-goods-id <id> --shop-id <id>`) |
159
159
  | `r2-cli goods price --id <id> --price <amount> [--json]` | 修改上架价格(也可用 `--stock-goods-id <id> --shop-id <id>`) |
160
160
 
161
+ ### 修改商品信息
162
+
163
+ 修改已上架商品的标题、描述、品牌、类目、图片、属性等。Agent 可自动读图识别并填充商品信息。
164
+
165
+ | 命令 | 说明 |
166
+ |------|------|
167
+ | `r2-cli goods edit --id <id> --category-id <id> --channel-cat-id <id> --json` | 修改商品信息(必填:定位参数 + 类目) |
168
+ | `r2-cli goods edit --id <id> --category-id <id> --channel-cat-id <id> --image-ids <ids> --item-attrs <json> --brand-name <name> --json` | 带图片和属性修改 |
169
+
170
+ **定位商品**:优先使用 `--id <goodsListingId>`(从上架列表 `id` 字段获取),也可用 `--stock-goods-id <id> --account <shopId>`。**必填参数**:定位参数(二选一)+ `--category-id` + `--channel-cat-id`(后端必填)
171
+
172
+ **可选参数**:`--title`、`--desc`、`--image-ids`(需先通过 `hang-up upload-images` 上传)、`--item-attrs`(JSON)、`--brand-name`、`--stuff-status`、`--goods-no`、`--original-price`、`--size`
173
+
161
174
  ### 闲鱼挂售(完整商品信息模式)
162
175
 
163
- 挂售模式支持完整的商品信息:图片、类目、属性、品牌等。流程:查询类目/属性上传图片提交挂售。
176
+ 挂售模式支持完整的商品信息:图片、类目、属性、品牌等。Agent 可自动识别图片内容填充商品信息,流程:上传图片 AI 识别 匹配类目/属性 → 提交。
164
177
 
165
178
  | 命令 | 说明 |
166
179
  |------|------|
@@ -174,10 +187,13 @@ Token 存储在 `~/.r2-cli/config.json`(原子写入防丢失),过期后
174
187
 
175
188
  **挂售可选参数**:`--brand-name`、`--size`、`--goods-no`、`--original-price`、`--item-attrs`(属性列表 JSON)、`--trade-type`(默认 0 仅在线)、`--transport-fee`(默认 0 包邮)、`--yhb`(验货宝)、`--division-id`(默认 330100 杭州)
176
189
 
190
+ **售后服务**(默认关闭):提交时自动附带售后服务配置,默认全部关闭。卖家需在闲鱼 APP 开通对应服务后才能开启(我的 → 设置 → 卖家服务 → 保障服务)。
191
+
177
192
  ### 其他命令
178
193
 
179
194
  | 命令 | 说明 |
180
195
  |------|------|
196
+ | `r2-cli update` | 一键更新 CLI 和技能 |
181
197
  | `r2-cli uninstall` | 卸载 R2-CLI 并清除所有配置 |
182
198
 
183
199
  ---
@@ -187,7 +203,8 @@ Token 存储在 `~/.r2-cli/config.json`(原子写入防丢失),过期后
187
203
  本工具可供 AI Agent 调用以自动化操作二手潮奢交易,Agent 将以您的用户身份在授权范围内执行操作,可能导致商品误上架、价格错误等风险,请谨慎操作。
188
204
 
189
205
  建议:
190
- - Agent 提交前向用户确认关键参数(售价、店铺)
206
+ - Agent 收到"上架"指令时,若用户未明确指定方式(选品上架/挂售上架),**必须询问用户**选择哪种上架方式
207
+ - Agent 自动识别图片并填充商品信息,缺少必填字段(售价、商家编码)时会向用户询问
191
208
  - Token 存储在本地 `~/.r2-cli/config.json`(原子写入,防止中断导致配置丢失),注意保护
192
209
 
193
210
  ---
package/dist/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  R2-CLI — 二手潮奢交易命令行工具,由 [Round2AI](https://github.com/Round2AI) 团队维护 — 让人类和 AI Agent 都能在终端中完成商品上架等交易操作。
7
7
 
8
- 覆盖商品上架、挂售、认证登录等核心业务域,提供 12 个命令及 3 个 AI Agent [Skills](./skills/)。
8
+ 覆盖商品上架、挂售、商品信息修改、认证登录等核心业务域,提供 16 个命令及 3 个 AI Agent [Skills](./skills/)。
9
9
 
10
10
  [安装](#安装与快速开始) · [AI Agent 快速开始](#快速开始ai-agent) · [Agent Skills](#agent-skills) · [认证](#认证) · [命令](#命令参考) · [安全](#安全与风险提示)
11
11
 
@@ -21,8 +21,8 @@ R2-CLI — 二手潮奢交易命令行工具,由 [Round2AI](https://github.com
21
21
  | 类别 | 能力 |
22
22
  |------|------|
23
23
  | 认证登录 | 扫码登录(第二回合 APP / 微信 / 支付宝)、闲鱼店铺授权、状态查询、登出(Agent 一步式流程) |
24
- | 商品管理 | 商品上架(4 步流程:获取店铺 → 获取仓库 → 获取选品商品 → 提交上架 + 自动轮询上架结果)、店铺查看、仓库查看、选品商品查看、上架列表查询、下架、改价 |
25
- | 闲鱼挂售 | 完整商品信息模式:类目查询属性/品牌选择图片上传 → 提交挂售 |
24
+ | 商品管理 | 商品上架(4 步流程:获取店铺 → 获取仓库 → 获取选品商品 → 提交上架 + 自动轮询上架结果)、商品信息修改(AI 读图识别 → 自动匹配类目/属性 → 提交修改)、店铺查看、仓库查看、选品商品查看、上架列表查询、下架、改价 |
25
+ | 闲鱼挂售 | 图片上传AI 读图识别商品信息 自动匹配类目/属性 → 提交挂售 |
26
26
 
27
27
  ---
28
28
 
@@ -117,7 +117,7 @@ npx skills add Round2AI/r2-cli --all -y
117
117
  |-------|------|
118
118
  | `r2-shared` | 共享基础:安装、统一错误格式、命令概览 |
119
119
  | `r2-auth` | 认证登录:一步式扫码登录(生成二维码 + 自动轮询,支持第二回合 APP / 微信 / 支付宝)、闲鱼店铺授权、状态查询、登出 |
120
- | `r2-goods` | 商品管理:4 步上架流程、挂售流程(类目→属性→图片→提交)、下架、改价、店铺/仓库/商品/上架列表查看 |
120
+ | `r2-goods` | 商品管理:4 步上架流程、挂售流程、商品信息修改(AI 读图识别 → 自动匹配类目/属性 → 提交)、下架、改价、店铺/仓库/商品/上架列表查看 |
121
121
 
122
122
  ---
123
123
 
@@ -149,7 +149,7 @@ Token 存储在 `~/.r2-cli/config.json`(原子写入防丢失),过期后
149
149
  | `r2-cli goods list [--stock-id <id>] [--stock-goods-id <id>] [--json]` | 查看选品商品(可按仓库或商品 ID 过滤,支持 `--page` 和 `--size`) |
150
150
  | `r2-cli goods listing [--json]` | 查询上架列表(支持 `--id` / `--shop-id` / `--stock-goods-id` / `--stock-id` / `--status` / `--platform` 过滤) |
151
151
 
152
- ### 商品上架/下架
152
+ ### 商品上架/下架/改价
153
153
 
154
154
  | 命令 | 说明 |
155
155
  |------|------|
@@ -158,9 +158,22 @@ Token 存储在 `~/.r2-cli/config.json`(原子写入防丢失),过期后
158
158
  | `r2-cli goods down --id <id> [--json]` | 下架商品(也可用 `--stock-goods-id <id> --shop-id <id>`) |
159
159
  | `r2-cli goods price --id <id> --price <amount> [--json]` | 修改上架价格(也可用 `--stock-goods-id <id> --shop-id <id>`) |
160
160
 
161
+ ### 修改商品信息
162
+
163
+ 修改已上架商品的标题、描述、品牌、类目、图片、属性等。Agent 可自动读图识别并填充商品信息。
164
+
165
+ | 命令 | 说明 |
166
+ |------|------|
167
+ | `r2-cli goods edit --id <id> --category-id <id> --channel-cat-id <id> --json` | 修改商品信息(必填:定位参数 + 类目) |
168
+ | `r2-cli goods edit --id <id> --category-id <id> --channel-cat-id <id> --image-ids <ids> --item-attrs <json> --brand-name <name> --json` | 带图片和属性修改 |
169
+
170
+ **定位商品**:优先使用 `--id <goodsListingId>`(从上架列表 `id` 字段获取),也可用 `--stock-goods-id <id> --account <shopId>`。**必填参数**:定位参数(二选一)+ `--category-id` + `--channel-cat-id`(后端必填)
171
+
172
+ **可选参数**:`--title`、`--desc`、`--image-ids`(需先通过 `hang-up upload-images` 上传)、`--item-attrs`(JSON)、`--brand-name`、`--stuff-status`、`--goods-no`、`--original-price`、`--size`
173
+
161
174
  ### 闲鱼挂售(完整商品信息模式)
162
175
 
163
- 挂售模式支持完整的商品信息:图片、类目、属性、品牌等。流程:查询类目/属性上传图片提交挂售。
176
+ 挂售模式支持完整的商品信息:图片、类目、属性、品牌等。Agent 可自动识别图片内容填充商品信息,流程:上传图片 AI 识别 匹配类目/属性 → 提交。
164
177
 
165
178
  | 命令 | 说明 |
166
179
  |------|------|
@@ -174,10 +187,13 @@ Token 存储在 `~/.r2-cli/config.json`(原子写入防丢失),过期后
174
187
 
175
188
  **挂售可选参数**:`--brand-name`、`--size`、`--goods-no`、`--original-price`、`--item-attrs`(属性列表 JSON)、`--trade-type`(默认 0 仅在线)、`--transport-fee`(默认 0 包邮)、`--yhb`(验货宝)、`--division-id`(默认 330100 杭州)
176
189
 
190
+ **售后服务**(默认关闭):提交时自动附带售后服务配置,默认全部关闭。卖家需在闲鱼 APP 开通对应服务后才能开启(我的 → 设置 → 卖家服务 → 保障服务)。
191
+
177
192
  ### 其他命令
178
193
 
179
194
  | 命令 | 说明 |
180
195
  |------|------|
196
+ | `r2-cli update` | 一键更新 CLI 和技能 |
181
197
  | `r2-cli uninstall` | 卸载 R2-CLI 并清除所有配置 |
182
198
 
183
199
  ---
@@ -187,7 +203,8 @@ Token 存储在 `~/.r2-cli/config.json`(原子写入防丢失),过期后
187
203
  本工具可供 AI Agent 调用以自动化操作二手潮奢交易,Agent 将以您的用户身份在授权范围内执行操作,可能导致商品误上架、价格错误等风险,请谨慎操作。
188
204
 
189
205
  建议:
190
- - Agent 提交前向用户确认关键参数(售价、店铺)
206
+ - Agent 收到"上架"指令时,若用户未明确指定方式(选品上架/挂售上架),**必须询问用户**选择哪种上架方式
207
+ - Agent 自动识别图片并填充商品信息,缺少必填字段(售价、商家编码)时会向用户询问
191
208
  - Token 存储在本地 `~/.r2-cli/config.json`(原子写入,防止中断导致配置丢失),注意保护
192
209
 
193
210
  ---
@@ -1,57 +1,219 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
3
- <title>第二回合 - 扫码登录</title>
4
- <style>
5
- *{box-sizing:border-box;margin:0;padding:0}
6
- :root{--primary:#06d290;--primary-light:#06d2901a;--primary-dark:#06d290cc;--text:#1a1a1a;--text-muted:#8c8c8c;--bg:#f7f8fa;--card:#fff;--border:#e8e8e8;--radius:16px;--shadow:0 2px 16px #06d2901a;--success:#52c41a;--error:#ff4d4f;--info:#1890ff}
7
- body{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;background:var(--bg);color:var(--text)}
8
- .card{background:var(--card);border-radius:var(--radius);padding:40px 32px 32px;box-shadow:var(--shadow);border:1px solid var(--border);text-align:center;max-width:360px;width:90%;transition:all .3s ease}
9
- .brand{margin-bottom:24px}
10
- .brand-name{font-size:22px;font-weight:700;color:var(--text);letter-spacing:2px}
11
- .brand-sub{font-size:10px;color:var(--text-muted);margin-top:4px;letter-spacing:3px;text-transform:uppercase;font-weight:500}
12
- img#qr{max-width:200px;border-radius:12px;transition:all .3s ease}
13
- img#qr.dimmed{opacity:.15;filter:blur(4px);transform:scale(.95)}
14
- .status-text{font-size:16px;font-weight:600;color:var(--text);margin-top:16px}
15
- .hint{color:var(--text-muted);font-size:13px;margin-top:6px}
16
- .scan-hint{color:var(--text-muted);font-size:12px;margin-top:16px}
17
- .status-icon{font-size:56px;margin-bottom:8px;animation:popIn .4s cubic-bezier(.175,.885,.32,1.275)}
18
- @keyframes popIn{0%{transform:scale(0);opacity:0}100%{transform:scale(1);opacity:1}}
19
- .success-bg .card{border-color:var(--success);box-shadow:0 2px 16px rgba(82,196,26,.12)}
20
- .success-bg .status-text{color:var(--success)}
21
- .expired-bg .card{border-color:var(--error);box-shadow:0 2px 16px rgba(255,77,79,.1)}
22
- .expired-bg .status-text{color:var(--error)}
23
- .scanning-bg .card{border-color:var(--info);box-shadow:0 2px 16px rgba(24,144,255,.1)}
24
- .scanning-bg .status-text{color:var(--info)}
25
- .pulse{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--info);animation:pulse 1.4s infinite;margin-right:6px;vertical-align:middle}
26
- @keyframes pulse{0%{opacity:1;transform:scale(1)}50%{opacity:.4;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}
27
- </style></head>
28
- <body>
29
- <div class="card" id="app">
30
- <div class="brand"><div class="brand-name">第二回合</div><div class="brand-sub">Round2AI</div></div>
31
- <img id="qr" src="./qr.png" alt="第二回合 - 扫码登录"/>
32
- <p class="status-text" id="statusText">请扫码登录</p>
33
- <p class="scan-hint">支持 第二回合 APP / 微信 / 支付宝 扫码</p>
34
- <p class="hint" id="hint"></p>
35
- </div>
36
- <script>
37
- const qr=document.getElementById("qr"),st=document.getElementById("statusText"),ht=document.getElementById("hint"),app=document.getElementById("app"),bd=document.body;
38
- const states={
39
- waiting:{icon:"",text:"请扫码登录",hint:"",bg:"",dim:false},
40
- scanning:{icon:"",text:'<span class="pulse"></span>已扫码,请在手机上确认',hint:"等待确认中...",bg:"scanning-bg",dim:true},
41
- success:{icon:"✅",text:"登录成功!",hint:"可关闭此页面",bg:"success-bg",dim:true},
42
- expired:{icon:"⏰",text:"二维码已过期",hint:"请重新获取",bg:"expired-bg",dim:true}
43
- };
44
- function render(s){
45
- const d=states[s]||states.waiting;
46
- bd.className=d.bg;
47
- if(d.icon){
48
- qr.style.display="none";
49
- app.innerHTML='<div class="brand"><div class="brand-name">第二回合</div><div class="brand-sub">Round2AI</div></div><div class="status-icon">'+d.icon+'</div><p class="status-text">'+d.text+'</p>'+(d.hint?'<p class="hint">'+d.hint+'</p>':'');
50
- }else{
51
- qr.style.display="";qr.classList.toggle("dimmed",d.dim);st.innerHTML=d.text;ht.textContent=d.hint;
52
- }
53
- }
54
- const es=new EventSource("./events");
55
- es.onmessage=function(e){try{const d=JSON.parse(e.data);if(d.status)render(d.status)}catch(ex){}};
56
- es.onerror=function(){es.close()};
57
- </script></body></html>
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>第二回合 - 扫码登录</title>
7
+ <style>
8
+ * {
9
+ box-sizing: border-box;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+ :root {
14
+ --primary: #06d290;
15
+ --primary-light: #06d2901a;
16
+ --primary-dark: #06d290cc;
17
+ --text: #1a1a1a;
18
+ --text-muted: #8c8c8c;
19
+ --bg: #f7f8fa;
20
+ --card: #fff;
21
+ --border: #e8e8e8;
22
+ --radius: 16px;
23
+ --shadow: 0 2px 16px #06d2901a;
24
+ --success: #52c41a;
25
+ --error: #ff4d4f;
26
+ --info: #1890ff;
27
+ }
28
+ body {
29
+ display: flex;
30
+ flex-direction: column;
31
+ align-items: center;
32
+ justify-content: center;
33
+ min-height: 100vh;
34
+ font-family:
35
+ system-ui,
36
+ -apple-system,
37
+ "Segoe UI",
38
+ Roboto,
39
+ sans-serif;
40
+ background: var(--bg);
41
+ color: var(--text);
42
+ }
43
+ .card {
44
+ background: var(--card);
45
+ border-radius: var(--radius);
46
+ padding: 40px 32px 32px;
47
+ box-shadow: var(--shadow);
48
+ border: 1px solid var(--border);
49
+ text-align: center;
50
+ max-width: 360px;
51
+ width: 90%;
52
+ transition: all 0.3s ease;
53
+ }
54
+ .brand {
55
+ margin-bottom: 24px;
56
+ }
57
+ .brand-name {
58
+ font-size: 22px;
59
+ font-weight: 700;
60
+ color: var(--text);
61
+ letter-spacing: 2px;
62
+ }
63
+ .brand-sub {
64
+ font-size: 10px;
65
+ color: var(--text-muted);
66
+ margin-top: 4px;
67
+ letter-spacing: 3px;
68
+ text-transform: uppercase;
69
+ font-weight: 500;
70
+ }
71
+ img#qr {
72
+ max-width: 200px;
73
+ border-radius: 12px;
74
+ transition: all 0.3s ease;
75
+ }
76
+ img#qr.dimmed {
77
+ opacity: 0.15;
78
+ filter: blur(4px);
79
+ transform: scale(0.95);
80
+ }
81
+ .status-text {
82
+ font-size: 16px;
83
+ font-weight: 600;
84
+ color: var(--text);
85
+ margin-top: 16px;
86
+ }
87
+ .hint {
88
+ color: var(--text-muted);
89
+ font-size: 13px;
90
+ margin-top: 6px;
91
+ }
92
+ .scan-hint {
93
+ color: var(--text-muted);
94
+ font-size: 12px;
95
+ margin-top: 16px;
96
+ }
97
+ .status-icon {
98
+ font-size: 56px;
99
+ margin-bottom: 8px;
100
+ animation: popIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
101
+ }
102
+ @keyframes popIn {
103
+ 0% {
104
+ transform: scale(0);
105
+ opacity: 0;
106
+ }
107
+ 100% {
108
+ transform: scale(1);
109
+ opacity: 1;
110
+ }
111
+ }
112
+ .success-bg .card {
113
+ border-color: var(--success);
114
+ box-shadow: 0 2px 16px rgba(82, 196, 26, 0.12);
115
+ }
116
+ .success-bg .status-text {
117
+ color: var(--success);
118
+ }
119
+ .expired-bg .card {
120
+ border-color: var(--error);
121
+ box-shadow: 0 2px 16px rgba(255, 77, 79, 0.1);
122
+ }
123
+ .expired-bg .status-text {
124
+ color: var(--error);
125
+ }
126
+ .scanning-bg .card {
127
+ border-color: var(--info);
128
+ box-shadow: 0 2px 16px rgba(24, 144, 255, 0.1);
129
+ }
130
+ .scanning-bg .status-text {
131
+ color: var(--info);
132
+ }
133
+ .pulse {
134
+ display: inline-block;
135
+ width: 6px;
136
+ height: 6px;
137
+ border-radius: 50%;
138
+ background: var(--info);
139
+ animation: pulse 1.4s infinite;
140
+ margin-right: 6px;
141
+ vertical-align: middle;
142
+ }
143
+ @keyframes pulse {
144
+ 0% {
145
+ opacity: 1;
146
+ transform: scale(1);
147
+ }
148
+ 50% {
149
+ opacity: 0.4;
150
+ transform: scale(0.8);
151
+ }
152
+ 100% {
153
+ opacity: 1;
154
+ transform: scale(1);
155
+ }
156
+ }
157
+ </style>
158
+ </head>
159
+ <body>
160
+ <div class="card" id="app">
161
+ <div class="brand">
162
+ <div class="brand-name">第二回合</div>
163
+ <div class="brand-sub">Round2AI</div>
164
+ </div>
165
+ <img id="qr" src="./qr.png" alt="第二回合 - 扫码登录" />
166
+ <p class="status-text" id="statusText">请扫码登录</p>
167
+ <p class="scan-hint">支持 第二回合 APP / 微信 / 支付宝 扫码</p>
168
+ <p class="hint" id="hint"></p>
169
+ </div>
170
+ <script>
171
+ const qr = document.getElementById("qr"),
172
+ st = document.getElementById("statusText"),
173
+ ht = document.getElementById("hint"),
174
+ app = document.getElementById("app"),
175
+ bd = document.body;
176
+ const states = {
177
+ waiting: { icon: "", text: "请扫码登录", hint: "", bg: "", dim: false },
178
+ scanning: {
179
+ icon: "",
180
+ text: '<span class="pulse"></span>已扫码,请在手机上确认',
181
+ hint: "等待确认中...",
182
+ bg: "scanning-bg",
183
+ dim: true,
184
+ },
185
+ success: { icon: "✅", text: "登录成功!", hint: "可关闭此页面", bg: "success-bg", dim: true },
186
+ expired: { icon: "⏰", text: "二维码已过期", hint: "请重新获取", bg: "expired-bg", dim: true },
187
+ };
188
+ function render(s) {
189
+ const d = states[s] || states.waiting;
190
+ bd.className = d.bg;
191
+ if (d.icon) {
192
+ qr.style.display = "none";
193
+ app.innerHTML =
194
+ '<div class="brand"><div class="brand-name">第二回合</div><div class="brand-sub">Round2AI</div></div><div class="status-icon">' +
195
+ d.icon +
196
+ '</div><p class="status-text">' +
197
+ d.text +
198
+ "</p>" +
199
+ (d.hint ? '<p class="hint">' + d.hint + "</p>" : "");
200
+ } else {
201
+ qr.style.display = "";
202
+ qr.classList.toggle("dimmed", d.dim);
203
+ st.innerHTML = d.text;
204
+ ht.textContent = d.hint;
205
+ }
206
+ }
207
+ const es = new EventSource("./events");
208
+ es.onmessage = function (e) {
209
+ try {
210
+ const d = JSON.parse(e.data);
211
+ if (d.status) render(d.status);
212
+ } catch (ex) {}
213
+ };
214
+ es.onerror = function () {
215
+ es.close();
216
+ };
217
+ </script>
218
+ </body>
219
+ </html>