mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-05-11 18:10:01 +08:00
feat: add admin account page with logout and responsive address bar (#803)
- Add admin account tab to display current login method - Support logout for admin password login only - Show login method (password/user admin/disabled check) - Improve address bar responsive layout with auto-wrap - Update changelog for new features 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
### Features
|
||||
|
||||
- feat: |Admin| 新增管理员账号页面,显示当前登录方式并支持退出登录(仅限密码登录方式)
|
||||
- feat: |邮件转发| 新增来源地址正则转发功能,支持按发件人地址过滤转发,完全向后兼容
|
||||
- feat: |地址来源| 新增地址来源追踪功能,记录地址创建来源(Web 记录 IP,Telegram 记录用户 ID,Admin 后台标记)
|
||||
- feat: |邮件过滤| 移除后端 keyword 参数,改为前端过滤当前页邮件,优化查询性能
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
### Features
|
||||
|
||||
- feat: |Admin| Add admin account page, display current login method and support logout (password login only)
|
||||
- feat: |Email Forwarding| Add source address regex forwarding, filter by sender address, fully backward compatible
|
||||
- feat: |Address Source| Add address source tracking feature, record address creation source (Web records IP, Telegram records user ID, Admin panel marked)
|
||||
- feat: |Email Filtering| Remove backend keyword parameter, switch to frontend filtering of current page emails, optimize query performance
|
||||
|
||||
@@ -230,7 +230,7 @@ watch([userJwt, isTelegram, () => settings.value.address], async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-flex class="address-row" align="center" justify="center" :wrap="false">
|
||||
<n-flex class="address-row" align="center" justify="center" :wrap="true">
|
||||
<n-select v-model:value="addressValue" :options="addressOptions" :size="size" filterable
|
||||
:loading="addressLoading" :placeholder="t('address')" @update:value="onAddressChange"
|
||||
class="address-select" />
|
||||
@@ -244,16 +244,17 @@ watch([userJwt, isTelegram, () => settings.value.address], async () => {
|
||||
<style scoped>
|
||||
.address-row {
|
||||
width: 100%;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.address-select {
|
||||
min-width: 220px;
|
||||
max-width: 420px;
|
||||
flex: 0 1 420px;
|
||||
flex: 1 1 220px;
|
||||
}
|
||||
|
||||
.address-copy {
|
||||
margin-left: 10px;
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { useGlobalState } from '../store'
|
||||
import { api } from '../api'
|
||||
import { getRouterPathWithLang } from '../utils'
|
||||
|
||||
import SenderAccess from './admin/SenderAccess.vue'
|
||||
import Statistics from "./admin/Statistics.vue"
|
||||
@@ -30,9 +32,11 @@ import AiExtractSettings from './admin/AiExtractSettings.vue';
|
||||
|
||||
const {
|
||||
adminAuth, showAdminAuth, adminTab, loading,
|
||||
globalTabplacement, showAdminPage, userSettings
|
||||
globalTabplacement, showAdminPage, userSettings,
|
||||
openSettings
|
||||
} = useGlobalState()
|
||||
const message = useMessage()
|
||||
const router = useRouter()
|
||||
|
||||
const SendMail = defineAsyncComponent(() => {
|
||||
loading.value = true;
|
||||
@@ -49,7 +53,20 @@ const authFunc = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const { t } = useI18n({
|
||||
const showLogoutModal = ref(false)
|
||||
|
||||
const handleLogout = async () => {
|
||||
// 清空管理员认证
|
||||
adminAuth.value = '';
|
||||
// 重置管理员相关状态
|
||||
showAdminAuth.value = false;
|
||||
adminTab.value = 'account';
|
||||
// 显示成功提示并跳转
|
||||
message.success(t('logoutSuccess'));
|
||||
await router.push(getRouterPathWithLang('/', locale.value));
|
||||
}
|
||||
|
||||
const { t, locale } = useI18n({
|
||||
messages: {
|
||||
en: {
|
||||
accessHeader: 'Admin Password',
|
||||
@@ -80,6 +97,16 @@ const { t } = useI18n({
|
||||
about: 'About',
|
||||
ok: 'OK',
|
||||
mailWebhook: 'Mail Webhook',
|
||||
adminAccount: 'Admin',
|
||||
loginMethod: 'Login Method',
|
||||
loginViaPassword: 'Admin Password Login',
|
||||
loginViaUserAdmin: 'User Admin Permission',
|
||||
loginViaDisabledCheck: 'Disabled Password Check',
|
||||
logout: 'Logout',
|
||||
logoutConfirmTitle: 'Confirm Logout',
|
||||
logoutConfirmContent: 'Are you sure you want to logout from admin panel?',
|
||||
confirm: 'Confirm',
|
||||
logoutSuccess: 'Logout successful',
|
||||
},
|
||||
zh: {
|
||||
accessHeader: 'Admin 密码',
|
||||
@@ -110,12 +137,36 @@ const { t } = useI18n({
|
||||
about: '关于',
|
||||
ok: '确定',
|
||||
mailWebhook: '邮件 Webhook',
|
||||
adminAccount: '管理员',
|
||||
loginMethod: '登录方式',
|
||||
loginViaPassword: 'Admin 密码登录',
|
||||
loginViaUserAdmin: '用户管理员权限',
|
||||
loginViaDisabledCheck: '已禁用密码检查',
|
||||
logout: '退出登录',
|
||||
logoutConfirmTitle: '确认退出',
|
||||
logoutConfirmContent: '确定要退出管理员面板吗?',
|
||||
confirm: '确认',
|
||||
logoutSuccess: '退出成功',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const showAdminPasswordModal = computed(() => !showAdminPage.value || showAdminAuth.value)
|
||||
const tmpAdminAuth = ref('')
|
||||
// 判断是否通过 admin password 登录(而非用户管理员权限)
|
||||
const isAdminPasswordLogin = computed(() => !!adminAuth.value)
|
||||
|
||||
// 获取当前登录方式
|
||||
const currentLoginMethod = computed(() => {
|
||||
if (adminAuth.value) {
|
||||
return t('loginViaPassword');
|
||||
} else if (userSettings.value.is_admin) {
|
||||
return t('loginViaUserAdmin');
|
||||
} else if (openSettings.value.disableAdminPasswordCheck) {
|
||||
return t('loginViaDisabledCheck');
|
||||
}
|
||||
return '';
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
// make sure user_id is fetched
|
||||
@@ -234,10 +285,32 @@ onMounted(async () => {
|
||||
<n-tab-pane name="appearance" :tab="t('appearance')">
|
||||
<Appearance />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="adminAccount" :tab="t('adminAccount')">
|
||||
<div style="display: flex; justify-content: center; padding: 20px;">
|
||||
<n-card style="width: 600px;">
|
||||
<n-space vertical>
|
||||
<n-text strong>{{ t('loginMethod') }}</n-text>
|
||||
<n-text>{{ currentLoginMethod }}</n-text>
|
||||
<n-divider v-if="isAdminPasswordLogin" />
|
||||
<n-button v-if="isAdminPasswordLogin" type="warning" @click="showLogoutModal = true" block>
|
||||
{{ t('logout') }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="about" :tab="t('about')">
|
||||
<About />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
<n-modal v-model:show="showLogoutModal" preset="dialog" :title="t('logoutConfirmTitle')">
|
||||
<p>{{ t('logoutConfirmContent') }}</p>
|
||||
<template #action>
|
||||
<n-button :loading="loading" @click="handleLogout" size="small" tertiary type="warning">
|
||||
{{ t('confirm') }}
|
||||
</n-button>
|
||||
</template>
|
||||
</n-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.address-manage {
|
||||
margin-left: 10px;
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user