Skip to content

Commit d8e7921

Browse files
committed
v14.0-G2: 修复密码眼睛位置 + 注册加邮箱验证码
- 眼睛按钮改用 top:0;bottom:0;display:flex;align-items:center 绝对居中 根本原因: top:50%基于包含margin的wrap高度计算导致偏下 - 修复 .sdrop-auth-panel .sdrop-pass-wrap input 选择器权重 - 注册表单加邮箱验证码: 邮箱行右侧「获取」按钮+60s倒计时+验证码输入框 - sendCode消息路由 -> _handleSendCode -> cloudSendEmailCode('/auth/send-code') - dev_code自动填入验证码框(方便SMTP未配置时测试) - cloudRegister加emailCode可选参数
1 parent e9e11cc commit d8e7921

2 files changed

Lines changed: 92 additions & 10 deletions

File tree

extension/src/engineClient.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,13 +457,15 @@ export class EngineClient {
457457
}
458458

459459
/** 云端注册 */
460-
async cloudRegister(email: string, username: string, password: string): Promise<{
460+
async cloudRegister(email: string, username: string, password: string, emailCode?: string): Promise<{
461461
access_token: string;
462462
refresh_token?: string;
463463
token_type: string;
464464
user: { id: number; email: string; username: string; role: string; plan?: string; credits?: number };
465465
}> {
466-
return this._cloudPost("/auth/register", { email, username, password });
466+
const body: Record<string, string> = { email, username, password };
467+
if (emailCode) body.email_code = emailCode;
468+
return this._cloudPost("/auth/register", body);
467469
}
468470

469471
/** 获取当前用户信息 */

extension/src/sidebarProvider.ts

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
105105
case "register":
106106
await this._handleRegister(msg);
107107
break;
108+
case "sendCode":
109+
await this._handleSendCode(msg);
110+
break;
108111
case "logout":
109112
await this._handleLogout();
110113
break;
@@ -166,9 +169,9 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
166169
}
167170
}
168171

169-
private async _handleRegister(msg: { email: string; username: string; password: string }): Promise<void> {
172+
private async _handleRegister(msg: { email: string; username: string; password: string; emailCode?: string }): Promise<void> {
170173
try {
171-
const result = await this._client.cloudRegister(msg.email, msg.username, msg.password);
174+
const result = await this._client.cloudRegister(msg.email, msg.username, msg.password, msg.emailCode);
172175
await this._context.secrets.store("testpilot.token", result.access_token);
173176
if (result.refresh_token) {
174177
await this._context.secrets.store("testpilot.refresh_token", result.refresh_token);
@@ -188,6 +191,24 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
188191
}
189192
}
190193

194+
private async _handleSendCode(msg: { email: string }): Promise<void> {
195+
try {
196+
const result = await this._client.cloudSendEmailCode(msg.email);
197+
this._postMessage({
198+
command: "sendCodeResult",
199+
success: true,
200+
message: result.message,
201+
devCode: result.dev_code, // 开发模式下返回,方便调试
202+
});
203+
} catch (e: any) {
204+
this._postMessage({
205+
command: "sendCodeResult",
206+
success: false,
207+
error: e.message || "发送失败,请稍后重试",
208+
});
209+
}
210+
}
211+
191212
private async _handleLogout(): Promise<void> {
192213
await this._context.secrets.delete("testpilot.token");
193214
await this._context.secrets.delete("testpilot.refresh_token");
@@ -1658,16 +1679,28 @@ ${commonRules}`;
16581679
.sdrop-pass-wrap {
16591680
position: relative; margin-bottom: 5px;
16601681
}
1661-
.sdrop-pass-wrap input {
1662-
width: 100%; box-sizing: border-box; padding-right: 28px; margin-bottom: 0;
1682+
/* 三层选择器权重高于 .sdrop-auth-panel input,确保 margin-bottom:0 生效 */
1683+
.sdrop-auth-panel .sdrop-pass-wrap input {
1684+
margin-bottom: 0; padding-right: 26px;
16631685
}
16641686
.sdrop-eye-btn {
1665-
position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
1687+
position: absolute; right: 4px; top: 0; bottom: 0;
1688+
display: flex; align-items: center;
16661689
background: none; border: none; cursor: pointer;
1667-
font-size: 13px; line-height: 1; padding: 2px; color: var(--muted);
1690+
font-size: 12px; padding: 0 2px; color: var(--muted);
16681691
opacity: 0.6; transition: opacity 0.1s;
16691692
}
16701693
.sdrop-eye-btn:hover { opacity: 1; }
1694+
/* 发送验证码按钮 */
1695+
.sdrop-code-row { display: flex; gap: 4px; margin-bottom: 5px; }
1696+
.sdrop-code-row input { flex: 1; margin-bottom: 0; }
1697+
.sdrop-send-code-btn {
1698+
flex-shrink: 0; padding: 5px 6px; font-size: 10px; font-weight: 600;
1699+
background: none; color: var(--btn-bg);
1700+
border: 1px solid var(--btn-bg); border-radius: 3px; cursor: pointer;
1701+
white-space: nowrap; min-width: 44px;
1702+
}
1703+
.sdrop-send-code-btn:disabled { opacity: 0.4; cursor: not-allowed; }
16711704
.sdrop-login-btn {
16721705
width: 100%; padding: 6px; font-size: 11px; font-weight: 600;
16731706
background: var(--btn-bg); color: var(--btn-fg);
@@ -1741,7 +1774,11 @@ ${commonRules}`;
17411774
<!-- 注册表单 -->
17421775
<div id="registerForm" style="display:none">
17431776
<div id="regError" class="auth-error"></div>
1744-
<input id="regEmail" type="email" placeholder="邮箱地址" autocomplete="email" />
1777+
<div class="sdrop-code-row">
1778+
<input id="regEmail" type="email" placeholder="邮箱地址" autocomplete="email" />
1779+
<button class="sdrop-send-code-btn" id="btnSendCode">获取</button>
1780+
</div>
1781+
<input id="regCode" type="text" placeholder="邮箱验证码(6位)" autocomplete="off" maxlength="6" />
17451782
<input id="regUser" type="text" placeholder="用户名" autocomplete="username" />
17461783
<div class="sdrop-pass-wrap">
17471784
<input id="regPass" type="password" placeholder="密码" autocomplete="new-password" />
@@ -2042,13 +2079,37 @@ ${commonRules}`;
20422079
function doRegister() {
20432080
showRegError('');
20442081
const email = (document.getElementById('regEmail')).value.trim();
2082+
const code = (document.getElementById('regCode')).value.trim();
20452083
const user = (document.getElementById('regUser')).value.trim();
20462084
const pass = (document.getElementById('regPass')).value;
20472085
const confirm = (document.getElementById('regConfirm')).value;
20482086
if (!email || !user || !pass) { showRegError('请填写所有字段'); return; }
20492087
if (pass !== confirm) { showRegError('两次密码不一致'); return; }
20502088
if (pass.length < 6) { showRegError('密码至少6位'); return; }
2051-
vscode.postMessage({ command: 'register', email, username: user, password: pass });
2089+
vscode.postMessage({ command: 'register', email, username: user, password: pass, emailCode: code || undefined });
2090+
}
2091+
2092+
// 发送验证码(60s 倒计时)
2093+
let _codeCountdown = 0;
2094+
let _codeTimer = null;
2095+
function doSendCode() {
2096+
const emailEl = document.getElementById('regEmail');
2097+
const email = emailEl ? emailEl.value.trim() : '';
2098+
if (!email || !email.includes('@')) { showRegError('请先填写正确的邮箱地址'); return; }
2099+
showRegError('');
2100+
vscode.postMessage({ command: 'sendCode', email });
2101+
// 立即开始倒计时
2102+
const btn = document.getElementById('btnSendCode');
2103+
_codeCountdown = 60;
2104+
if (btn) btn.disabled = true;
2105+
_codeTimer = setInterval(() => {
2106+
_codeCountdown--;
2107+
if (btn) btn.textContent = _codeCountdown + 's';
2108+
if (_codeCountdown <= 0) {
2109+
clearInterval(_codeTimer);
2110+
if (btn) { btn.disabled = false; btn.textContent = '获取'; }
2111+
}
2112+
}, 1000);
20522113
}
20532114
20542115
function doLogout() {
@@ -2063,6 +2124,8 @@ ${commonRules}`;
20632124
if (authLoginBtn) authLoginBtn.addEventListener('click', doAuth);
20642125
const authRegisterSubmit = document.getElementById('authRegisterSubmit');
20652126
if (authRegisterSubmit) authRegisterSubmit.addEventListener('click', doRegister);
2127+
const btnSendCode = document.getElementById('btnSendCode');
2128+
if (btnSendCode) btnSendCode.addEventListener('click', (e) => { e.stopPropagation(); doSendCode(); });
20662129
const authLogoutBtn = document.getElementById('authLogoutBtn');
20672130
if (authLogoutBtn) authLogoutBtn.addEventListener('click', doLogout);
20682131
const themeToggleLabel = document.getElementById('themeToggleLabel');
@@ -2490,6 +2553,23 @@ ${commonRules}`;
24902553
}
24912554
}
24922555
break;
2556+
case "sendCodeResult": {
2557+
const btn = document.getElementById('btnSendCode');
2558+
if (!msg.success) {
2559+
// 发送失败:重置倒计时
2560+
if (_codeTimer) { clearInterval(_codeTimer); _codeTimer = null; }
2561+
_codeCountdown = 0;
2562+
if (btn) { btn.disabled = false; btn.textContent = '获取'; }
2563+
showRegError(msg.error || '发送失败');
2564+
} else {
2565+
// 开发模式:dev_code 直接填入验证码框方便测试
2566+
if (msg.devCode) {
2567+
const codeEl = document.getElementById('regCode');
2568+
if (codeEl) codeEl.value = msg.devCode;
2569+
}
2570+
}
2571+
break;
2572+
}
24932573
case "shareResult":
24942574
if (btnShareExperience) {
24952575
btnShareExperience.disabled = false;

0 commit comments

Comments
 (0)