From 5fd181d9c07c4f0eaf20ff50d2103024e1c332e6 Mon Sep 17 00:00:00 2001 From: Dream Hunter Date: Mon, 31 Aug 2026 22:36:08 +0800 Subject: [PATCH] refactor(frontend): unify mailbox terminology Closes #1129 --- CHANGELOG.md | 1 + CHANGELOG_EN.md | 1 + e2e/tests/browser/custom-subdomain.spec.ts | 4 +- .../browser/user-address-pagination.spec.ts | 2 +- frontend/src/components/AddressSelect.vue | 4 +- frontend/src/i18n/locales/source/de.ts | 51 ++-- frontend/src/i18n/locales/source/es.ts | 51 ++-- frontend/src/i18n/locales/source/ja.ts | 51 ++-- frontend/src/i18n/locales/source/ptBR.ts | 51 ++-- frontend/src/i18n/message-registry.ts | 256 +++++++++--------- frontend/src/views/Admin.vue | 10 +- frontend/src/views/Header.vue | 10 +- frontend/src/views/Index.vue | 4 +- frontend/src/views/admin/Account.vue | 6 +- frontend/src/views/admin/CreateAccount.vue | 2 +- .../src/views/admin/UserAddressManagement.vue | 2 +- frontend/src/views/index/AccountSettings.vue | 6 +- frontend/src/views/index/AddressBar.vue | 2 +- frontend/src/views/index/SimpleIndex.vue | 4 +- frontend/src/views/user/AddressManagement.vue | 7 +- .../docs/en/guide/config-send-mail.md | 9 +- vitepress-docs/docs/en/guide/feature/admin.md | 16 +- .../docs/zh/guide/config-send-mail.md | 15 +- vitepress-docs/docs/zh/guide/feature/admin.md | 26 +- 24 files changed, 319 insertions(+), 272 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e16fe56..001fd02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ ### Improvements +- fix: |Frontend| 统一邮箱、邮箱地址、用户账号与管理后台的核心界面术语,并补齐附加语言缺失的邮件状态与远程图片文案(issue #1129) - feat: |发信页面| 优化用户和 Admin 发信页面的信息层级与响应式布局,增加正文格式工具栏、草稿状态、底部发送操作区及隔离的 HTML 预览 ### Testing diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md index 3b01e15..d241234 100644 --- a/CHANGELOG_EN.md +++ b/CHANGELOG_EN.md @@ -23,6 +23,7 @@ ### Improvements +- fix: |Frontend| Unify core mailbox, email-address, user-account, and Admin Console terminology, and fill missing additional-locale strings for mail status and remote images (issue #1129) - feat: |Send Mail| Improve the information hierarchy and responsive layout of the user and Admin composers, with a content-format toolbar, draft status, bottom send-action area, and isolated HTML preview ### Testing diff --git a/e2e/tests/browser/custom-subdomain.spec.ts b/e2e/tests/browser/custom-subdomain.spec.ts index f79c8f0..40568f8 100644 --- a/e2e/tests/browser/custom-subdomain.spec.ts +++ b/e2e/tests/browser/custom-subdomain.spec.ts @@ -6,7 +6,7 @@ test('create an address with a custom subdomain from the UI', async ({ page, req try { await page.goto(`${FRONTEND_URL}/en/`); - await page.getByRole('button', { name: 'Create New Email' }).click(); + await page.getByRole('button', { name: 'Create Email Address' }).click(); const name = `subui${Date.now()}`; const createForm = page.locator('.n-tab-pane:visible form'); @@ -36,7 +36,7 @@ test('create an address with a custom subdomain from the UI', async ({ page, req await createForm.getByText('Use Custom Subdomain', { exact: true }).click(); await createForm.locator('.n-input-group:visible .n-input input').last().fill('team'); - await createForm.getByRole('button', { name: 'Create New Email' }).click(); + await createForm.getByRole('button', { name: 'Create Email Address' }).click(); const domain = `team.${TEST_DOMAIN}`; const address = `tmp${name}@${domain}`; diff --git a/e2e/tests/browser/user-address-pagination.spec.ts b/e2e/tests/browser/user-address-pagination.spec.ts index d8d67f6..38b2c23 100644 --- a/e2e/tests/browser/user-address-pagination.spec.ts +++ b/e2e/tests/browser/user-address-pagination.spec.ts @@ -100,7 +100,7 @@ test.describe('User address pagination browser flow', () => { return url.pathname === '/user_api/bind_address' && url.searchParams.get('limit') === '100'; }); - await page.getByText('Mail Box', { exact: true }).click(); + await page.getByText('Inbox', { exact: true }).click(); const initialMailboxResponse = await initialMailboxAddressesResponse; expect(initialMailboxResponse.ok()).toBe(true); const mailboxAddressSelect = page.locator('.n-input-group .n-select').first(); diff --git a/frontend/src/components/AddressSelect.vue b/frontend/src/components/AddressSelect.vue index faa6583..f08143a 100644 --- a/frontend/src/components/AddressSelect.vue +++ b/frontend/src/components/AddressSelect.vue @@ -153,7 +153,7 @@ const refreshAddressOptions = async () => { if (userJwt.value) { const userChildren = await buildUserOptions(); if (userChildren.length > 0) { - groups.push({ type: 'group', label: t('userAddresses'), children: userChildren }); + groups.push({ type: 'group', label: t('linkedMailboxes'), children: userChildren }); } const userAddressSet = new Set(userChildren.map((item) => item.address)); const localChildren = buildLocalOptions(userAddressSet); @@ -215,7 +215,7 @@ watch([userJwt, isTelegram, () => settings.value.address], async () => { - +
@@ -145,7 +145,7 @@ onMounted(() => { - + diff --git a/frontend/src/views/admin/Account.vue b/frontend/src/views/admin/Account.vue index fcdde87..423dfc2 100644 --- a/frontend/src/views/admin/Account.vue +++ b/frontend/src/views/admin/Account.vue @@ -265,7 +265,7 @@ const columns = computed(() => [ sortOrder: sortBy.value === 'id' ? sortOrder.value : false }, { - title: t('name'), + title: t('emailAddress'), key: "name", sorter: true, sortOrder: sortBy.value === 'name' ? sortOrder.value : false @@ -479,11 +479,11 @@ onMounted(async () => {
- +

{{ t('deleteTip') }}

diff --git a/frontend/src/views/admin/CreateAccount.vue b/frontend/src/views/admin/CreateAccount.vue index 37d165c..96c14bd 100644 --- a/frontend/src/views/admin/CreateAccount.vue +++ b/frontend/src/views/admin/CreateAccount.vue @@ -152,7 +152,7 @@ onMounted(async () => { - {{ t('creatNewEmail') }} + {{ t('createEmailAddress') }}
diff --git a/frontend/src/views/admin/UserAddressManagement.vue b/frontend/src/views/admin/UserAddressManagement.vue index 3d143e7..2f009be 100644 --- a/frontend/src/views/admin/UserAddressManagement.vue +++ b/frontend/src/views/admin/UserAddressManagement.vue @@ -32,7 +32,7 @@ const fetchData = async () => { const columns = [ { - title: t('name'), + title: t('emailAddress'), key: "name" }, { diff --git a/frontend/src/views/index/AccountSettings.vue b/frontend/src/views/index/AccountSettings.vue index 97523ee..122aee0 100644 --- a/frontend/src/views/index/AccountSettings.vue +++ b/frontend/src/views/index/AccountSettings.vue @@ -113,7 +113,7 @@ const changePassword = async () => { - {{ t('deleteAccount') }} + {{ t('deleteMailbox') }} @@ -125,11 +125,11 @@ const changePassword = async () => { - +

{{ t('deleteAccountConfirm') }}

diff --git a/frontend/src/views/index/AddressBar.vue b/frontend/src/views/index/AddressBar.vue index 06d900c..7935048 100644 --- a/frontend/src/views/index/AddressBar.vue +++ b/frontend/src/views/index/AddressBar.vue @@ -71,7 +71,7 @@ onMounted(async () => { - {{ t('userLogin') }} + {{ t('userCenter') }}
diff --git a/frontend/src/views/index/SimpleIndex.vue b/frontend/src/views/index/SimpleIndex.vue index 5802303..e13db2b 100644 --- a/frontend/src/views/index/SimpleIndex.vue +++ b/frontend/src/views/index/SimpleIndex.vue @@ -188,7 +188,7 @@ onBeforeUnmount(() => { - {{ t('accountSettings') }} + {{ t('mailboxSettings') }}
@@ -200,7 +200,7 @@ onBeforeUnmount(() => { + @close="showAccountSettingsCard = false" :title="t('mailboxSettings')"> diff --git a/frontend/src/views/user/AddressManagement.vue b/frontend/src/views/user/AddressManagement.vue index 1167dcf..e079dbf 100644 --- a/frontend/src/views/user/AddressManagement.vue +++ b/frontend/src/views/user/AddressManagement.vue @@ -44,7 +44,6 @@ const showCredential = async (row) => { const changeMailAddress = async (address_id) => { try { const res = await api.fetch(`/user_api/bind_address_jwt/${address_id}`); - message.success(t('changeMailAddress') + " " + t('success')); if (!res.jwt) { message.error("jwt not found"); return; @@ -130,7 +129,7 @@ const fetchData = async () => { const columns = [ { - title: t('name'), + title: t('emailAddress'), key: "name" }, { @@ -180,9 +179,9 @@ const columns = [ tertiary: true, type: "primary", }, - { default: () => t('changeMailAddress') } + { default: () => t('openMailbox') } ), - default: () => `${t('changeMailAddress')}?` + default: () => `${t('openMailbox')}?` } ), h(NButton, diff --git a/vitepress-docs/docs/en/guide/config-send-mail.md b/vitepress-docs/docs/en/guide/config-send-mail.md index 2c0eccb..34fa4ed 100644 --- a/vitepress-docs/docs/en/guide/config-send-mail.md +++ b/vitepress-docs/docs/en/guide/config-send-mail.md @@ -150,19 +150,20 @@ cd worker wrangler secret put SMTP_CONFIG ``` -## User Accounts, Email Addresses, and Send Permission +## User Accounts, Mailboxes, Email Addresses, and Send Permission These concepts are related, but they identify different objects: | Concept | Description | |---------|-------------| -| User account | Signs in to the user center and can bind and manage multiple email addresses. The user account's login email is not automatically a sending or receiving address | -| Email address | The identity that actually receives and sends mail, such as `name@example.com`. The address currently selected on the frontend home page is authenticated by its Address JWT | +| User account | Signs in to the user center and can link and manage multiple mailboxes. The account email is not automatically a sending or receiving address | +| Mailbox | The user-facing object that contains an inbox, sent mail, sending capability, and mailbox settings | +| Email address | The concrete value that identifies a mailbox, such as `name@example.com`. The current mailbox is authenticated by its Address JWT | | Send permission and balance | Permission and balance records in `address_sender` are managed independently **per email address**; user roles configured by `NO_LIMIT_SEND_ROLE` can bypass address balance checks | Multiple email addresses bound to the same user account therefore do not share send permission or balance. To request permission for an address: -1. Switch to the email address that needs to send mail +1. Switch to the mailbox that needs to send mail 2. Open the **Send Mail** page 3. Click **Request Access** diff --git a/vitepress-docs/docs/en/guide/feature/admin.md b/vitepress-docs/docs/en/guide/feature/admin.md index c0a82c2..82be4c6 100644 --- a/vitepress-docs/docs/en/guide/feature/admin.md +++ b/vitepress-docs/docs/en/guide/feature/admin.md @@ -4,31 +4,31 @@ > You need to configure `ADMIN_PASSWORDS` or `ADMIN_USER_ROLE` to access the admin console > Admin role configuration: if the user role equals ADMIN_USER_ROLE, they can access the admin console -After deploying the frontend application, click the upper-left logo 5 times or visit the `/admin` path to enter the management console. +After deploying the frontend application, click the upper-left logo 5 times or visit the `/admin` path to enter the Admin Console. You need to configure `ADMIN_PASSWORDS` in the backend or ensure the current user role is `ADMIN_USER_ROLE`, otherwise access to the console will be denied. ## Admin Passwords vs User Accounts -`ADMIN_PASSWORDS` is the management password for the Admin console. It is not a site user account -and does not correspond to any mailbox address. Logging in with an admin password grants access to +`ADMIN_PASSWORDS` is the password for the Admin Console. It is not a site user account +and does not correspond to any mailbox. Logging in with an admin password grants access to the console, but that login itself cannot receive mail. Site user accounts are stored in the `users` table and use the user login flow. Whether a user can -receive mail depends on whether they created or bound a mailbox address. Creating a normal user -whose email looks like `admin@example.com` does not automatically grant admin permissions. +receive mail depends on whether they created or bound a mailbox. Creating a normal user +whose account email is `admin@example.com` does not automatically grant admin permissions. If you want a user account to access the Admin console, configure `ADMIN_USER_ROLE` and assign the same role to that user in user management. ![admin](/feature/admin.png) -## Account List Sorting +## Mailbox List Sorting -The Accounts tab in the admin console supports column sorting. Click the column header to toggle ascending/descending order for: +**Admin Console → Mailbox Management → Mailboxes** supports column sorting. Click a column header to toggle ascending/descending order for: - ID -- Name +- Email Address - Created At - Updated At - Mail Count diff --git a/vitepress-docs/docs/zh/guide/config-send-mail.md b/vitepress-docs/docs/zh/guide/config-send-mail.md index ce3ec4b..c517295 100644 --- a/vitepress-docs/docs/zh/guide/config-send-mail.md +++ b/vitepress-docs/docs/zh/guide/config-send-mail.md @@ -150,19 +150,20 @@ cd worker wrangler secret put SMTP_CONFIG ``` -## 用户账号、邮箱地址与发信权限 +## 用户账号、邮箱、邮箱地址与发信权限 -这三个概念相互关联,但不是同一个对象: +这些概念相互关联,但不是同一个对象: | 概念 | 说明 | |------|------| -| 用户账号 | 用于登录用户中心,可绑定和管理多个邮箱地址;用户账号的登录邮箱本身不等于用于收发信的邮箱地址 | -| 邮箱地址 | 实际收信和发信的身份,例如 `name@example.com`;前端首页当前选中的地址由该地址的凭证(Address JWT)认证 | +| 用户账号 | 用于登录用户中心,可绑定和管理多个邮箱;用户账号的用户邮箱本身不等于用于收发信的邮箱地址 | +| 邮箱 | 包含收件箱、发件箱、发信能力和邮箱设置的业务对象 | +| 邮箱地址 | 标识邮箱的具体值,例如 `name@example.com`;当前邮箱由该地址的 Address JWT 认证 | | 发信权限与额度 | `address_sender` 中的权限和额度以**邮箱地址**为单位独立管理;`NO_LIMIT_SEND_ROLE` 配置的用户角色可以绕过地址额度检查 | 因此,同一用户账号绑定的多个邮箱地址不会共享发信权限或余额。为某个地址申请发信权限时: -1. 先在前端切换到需要发信的邮箱地址 +1. 先在前端切换到需要发信的邮箱 2. 打开「发送邮件」页 3. 点击「申请权限」 @@ -175,7 +176,7 @@ wrangler secret put SMTP_CONFIG 1. **自动初始化默认额度**:当 `DEFAULT_SEND_BALANCE > 0` 时,用户打开前端发信页或第一次调用发信接口时,系统会自动为该地址初始化默认额度 2. **手动申请**:如果 `DEFAULT_SEND_BALANCE = 0`,使用者可以在前端切换到目标邮箱地址并点击「申请权限」,为当前地址创建待管理员处理的发信权限记录 3. **无限制发送**:以下方式可以跳过余额检查: - - 在 admin 后台将地址加入「无限制发送地址列表」 + - 在管理后台将邮箱地址加入「无限制发送地址列表」 - 配置 `NO_LIMIT_SEND_ROLE` 环境变量,指定可以无限发送的用户角色 > [!NOTE] @@ -193,4 +194,4 @@ wrangler secret put SMTP_CONFIG 适合未完成 Email Routing onboarding 的域名,或 Workers 免费版。 -只有收件人在 admin 后台的 `已验证地址列表` 中时,才会通过 `SEND_MAIL` binding 发信。 +只有收件人在管理后台的 `已验证地址列表` 中时,才会通过 `SEND_MAIL` binding 发信。 diff --git a/vitepress-docs/docs/zh/guide/feature/admin.md b/vitepress-docs/docs/zh/guide/feature/admin.md index 78285e4..aa13026 100644 --- a/vitepress-docs/docs/zh/guide/feature/admin.md +++ b/vitepress-docs/docs/zh/guide/feature/admin.md @@ -1,29 +1,29 @@ -# Admin 控制台 +# 管理后台 > [!NOTE] -> 需要配置 `ADMIN_PASSWORDS` 或者 `ADMIN_USER_ROLE` 才可以访问 admin 控制台 -> admin 角色配置, 如果用户角色等于 ADMIN_USER_ROLE 则可以访问 admin 控制台 +> 需要配置 `ADMIN_PASSWORDS` 或者 `ADMIN_USER_ROLE` 才可以访问管理后台 +> 管理员角色配置:如果用户角色等于 `ADMIN_USER_ROLE`,则可以访问管理后台 -部署前端应用之后,点击 左上角 logo 5 次 或者访问 `/admin` 路径即可进入管理控制台。 +部署前端应用之后,点击左上角 logo 5 次或访问 `/admin` 路径即可进入管理后台。 -需要在后端配置 `ADMIN_PASSWORDS` 或者当前用户角色为 `ADMIN_USER_ROLE`,否则不允许访问控制台。 +需要在后端配置 `ADMIN_PASSWORDS` 或者当前用户角色为 `ADMIN_USER_ROLE`,否则不允许访问管理后台。 -## 管理口令和用户账号的区别 +## 管理员密码和用户账号的区别 -`ADMIN_PASSWORDS` 是 Admin 控制台的管理口令,不是站点用户账号,也不对应某个邮箱地址。使用管理口令登录后可以进入后台,但它本身不能收信。 +`ADMIN_PASSWORDS` 是管理后台的管理员密码,不是站点用户账号,也不对应某个邮箱地址。使用管理员密码登录后可以进入后台,但它本身不能收信。 -站点用户账号存储在 `users` 表中,需要通过用户登录体系进入;用户是否能收信取决于是否创建或绑定了邮箱地址。即使你创建了一个邮箱为 `admin@example.com` 或用户名看起来像 `admin` 的普通用户,它也不会自动获得后台权限。 +站点用户账号存储在 `users` 表中,需要通过用户登录体系进入;用户是否能收信取决于是否创建或绑定了邮箱。即使你创建了一个用户邮箱为 `admin@example.com` 或用户名看起来像 `admin` 的普通用户,它也不会自动获得后台权限。 -如果希望某个用户也能进入 Admin 控制台,请配置 `ADMIN_USER_ROLE`,并在用户管理中给该用户设置相同的角色。 +如果希望某个用户也能进入管理后台,请配置 `ADMIN_USER_ROLE`,并在用户管理中给该用户设置相同的角色。 ![admin](/feature/admin.png) -## 账号列表排序 +## 邮箱列表排序 -管理后台的账号标签页支持按列排序,可点击表头对以下列进行升序/降序排列: +管理后台的「邮箱管理 → 邮箱列表」支持按列排序,可点击表头进行升序/降序排列: - ID -- 名称 +- 邮箱地址 - 创建时间 - 更新时间 - 邮件数量 @@ -37,7 +37,7 @@ ## IP 黑名单 / 白名单 -在 Admin 控制台 → **IP 黑名单设置** 页面可配置访问控制,作用于以下接口:创建邮箱地址、发送邮件、外部发送邮件 API、用户注册、验证码校验。 +在管理后台的 **IP 黑名单设置** 页面可配置访问控制,作用于以下接口:创建邮箱地址、发送邮件、外部发送邮件 API、用户注册、验证码校验。 ### IP 白名单(严格模式)