diff --git a/console/frontend/src/pages/Accounts.tsx b/console/frontend/src/pages/Accounts.tsx index 2467549..a2db0db 100644 --- a/console/frontend/src/pages/Accounts.tsx +++ b/console/frontend/src/pages/Accounts.tsx @@ -1,4 +1,4 @@ -/** 账号页:添加账号(Cloudflare OAuth / API Token 同窗切换),授权后自动接入并同步域名;搜索、详情、凭据管理 */ +/** 账号页:添加账号(Cloudflare OAuth / API Token 同窗平铺),授权后自动接入并同步域名;搜索、详情、凭据管理 */ import { useCallback, useEffect, useMemo, useState } from "react"; import { useSearchParams } from "react-router-dom"; import { @@ -9,7 +9,6 @@ import { Form, Input, Modal, - Radio, Space, Table, Tag, @@ -123,8 +122,6 @@ export function AccountsPage() { /** 添加账号:OAuth 与 API Token 两种方式都收在同一个模态窗口里 */ const [addOpen, setAddOpen] = useState(false); - /** 当前选中的授权方式 **/ - const [addMode, setAddMode] = useState<"oauth" | "token">("oauth"); /** API Token 提交的 loading(要防重复点击,一次接入要走好几次 Cloudflare 调用) **/ const [adding, setAdding] = useState(false); const [tokenForm] = Form.useForm(); @@ -270,10 +267,8 @@ export function AccountsPage() { { if (adding) return; setAddOpen(false); @@ -299,22 +294,20 @@ export function AccountsPage() { /** # 成员变量: 添加账号模态窗口的属性 **/ interface AddAccountModalProps { open: boolean; - mode: "oauth" | "token"; loading: boolean; form: ReturnType[0]; - onModeChange: (mode: "oauth" | "token") => void; onCancel: () => void; onOAuth: () => void; onTokenSubmit: () => void; } /** # 函数 - * 「添加 Cloudflare 账号」模态窗口:Cloudflare OAuth 授权与 API Token 授权在同一个窗口, - * 顶部按钮组切换方式,字段随方式变化 —— OAuth 只要一个「前往授权」按钮, - * API Token 填账户名称 + Token 即可(账户 ID 由 Token 在 Cloudflare 侧实际所属账户自动确定)。 + * 「添加 Cloudflare 账号」模态窗口:两种授权方式平铺展示、无需切换 —— + * 顶部「Cloudflare OAuth 授权」一键跳转授权,分隔线下方是 API Token 表单 + * (账户名称 + Token,账户 ID 由 Token 在 Cloudflare 侧实际所属账户自动确定)。 */ function AddAccountModal(props: AddAccountModalProps) { - const { open, mode, loading, form, onModeChange, onCancel, onOAuth, onTokenSubmit } = props; + const { open, loading, form, onCancel, onOAuth, onTokenSubmit } = props; return ( - onModeChange(e.target.value as "oauth" | "token")} - style={{ marginBottom: 4 }} - > - - Cloudflare OAuth 授权 - - - API Token 授权 - - + - {mode === "oauth" ? ( - - ) : ( - - - - - - - - - - )} + ); } diff --git a/console/frontend/src/pages/Inbox.tsx b/console/frontend/src/pages/Inbox.tsx index 6270fa4..2d5c861 100644 --- a/console/frontend/src/pages/Inbox.tsx +++ b/console/frontend/src/pages/Inbox.tsx @@ -340,10 +340,6 @@ export function InboxPage() { {safeArr(detail.email.to_addresses).join(", ") || "-"} - {detail.email.mailbox_address ?? "(未匹配)"} - - {detail.email.message_id ?? "-"} - {new Date(detail.email.received_at).toLocaleString()} →{" "} {detail.email.retained ? ( diff --git a/package.json b/package.json index 5cd1e8b..a0609c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wmailhub", - "version": "2.5.0", + "version": "2.5.1", "description": "WmailHub - 基于 Cloudflare Email Workers 的轻量邮件接收与转发平台(Console 直连 Email Worker)", "private": true, "type": "module", diff --git a/tests/ui-smoke.ts b/tests/ui-smoke.ts index 041fc65..74576d5 100644 --- a/tests/ui-smoke.ts +++ b/tests/ui-smoke.ts @@ -84,6 +84,12 @@ await page.locator(".ant-table-row").first().click(); await page.waitForSelector(".ant-drawer-open", { timeout: 8000 }); const drawerText = (await page.locator(".ant-drawer-title").textContent()) ?? ""; check("邮件详情抽屉打开(标题=邮件主题)", drawerText.trim().length > 0, drawerText.trim()); +check( + "详情抽屉不再展示「邮箱」/「Message-ID」", + (await page.locator(".ant-drawer .ant-descriptions-item-label:has-text('邮箱')").count()) === 0 && + (await page.locator(".ant-drawer .ant-descriptions-item-label:has-text('Message-ID')").count()) === 0, +); +await page.waitForTimeout(500); // 等抽屉滑入动画结束再截图 await page.screenshot({ path: `${SHOT_DIR}/08-email-detail.png` }); await page.keyboard.press("Escape"); @@ -144,7 +150,7 @@ for (const path of ["/accounts", "/domains", "/mailboxes", "/inboxs"]) { if (path === "/mailboxes") await page.screenshot({ path: `${SHOT_DIR}/11-batch-selected.png` }); } -// 6b. 添加账号模态窗口:OAuth / API Token 同窗(Radio 切换),只留必要字段 +// 6b. 添加账号模态窗口:OAuth 按钮 + API Token 表单同窗平铺,无需切换控件 await page.goto(`${BASE}/accounts`, { waitUntil: "domcontentloaded" }); await page.waitForSelector(".ant-table", { timeout: 8000 }); check( @@ -153,32 +159,25 @@ check( ); await page.click("button:has-text('添加账号')"); await page.waitForSelector(".ant-modal:has-text('添加 Cloudflare 账号')", { timeout: 8000 }); -const modeLabels = await page.locator(".ant-modal .ant-radio-button-wrapper").allInnerTexts(); check( - "添加账号模态窗口含 OAuth / API Token 两种授权方式(同窗按钮组)", - modeLabels.some((t) => t.includes("OAuth")) && modeLabels.some((t) => t.includes("API Token")), - modeLabels.join(" | "), + "两种授权方式同窗平铺(两个按钮 + 两个字段同时可见),无切换控件", + (await page.locator(".ant-modal button:has-text('Cloudflare OAuth 授权')").count()) === 1 && + (await page.locator(".ant-modal button:has-text('API Token 授权')").count()) === 1 && + (await page.locator(".ant-modal .ant-radio-button-wrapper").count()) === 0 && + (await page.locator(".ant-modal .ant-tabs-tab").count()) === 0 && + (await page.locator(".ant-modal:visible").count()) === 1, ); check( "窗口内不再有说明文字(Alert)与账户 ID 字段", (await page.locator(".ant-modal .ant-alert").count()) === 0 && (await page.locator(".ant-modal :text('账户 ID')").count()) === 0, ); -// OAuth 模式:只有「前往 Cloudflare 授权」按钮 check( - "OAuth 模式默认展示「前往 Cloudflare 授权」按钮", - (await page.locator(".ant-modal button:has-text('前往 Cloudflare 授权')").count()) === 1, + "API Token 表单只含 账户名称 / API Token 两个字段(1 个密码框)", + (await page.locator(".ant-modal input[type=password]").count()) === 1, ); -await page.screenshot({ path: `${SHOT_DIR}/12a-add-account-oauth.png` }); -// 切到 API Token:账户名称 + API Token 两个字段 -await page.click(".ant-modal .ant-radio-button-wrapper:has-text('API Token')"); -await page.waitForSelector(".ant-modal :text('账户名称')", { timeout: 8000 }); -check( - "API Token 方式只含 账户名称 / API Token 两个字段", - (await page.locator(".ant-modal input[type=password]").count()) === 1 && - (await page.locator(".ant-modal :text('账户 ID')").count()) === 0, -); -await page.screenshot({ path: `${SHOT_DIR}/12b-add-account-token.png` }); +await page.waitForTimeout(500); // 等开窗动画结束再截图 +await page.screenshot({ path: `${SHOT_DIR}/12a-add-account.png` }); await page.keyboard.press("Escape"); await page.waitForTimeout(400);