新增点击复制邮箱功能

This commit is contained in:
eoao
2025-06-28 20:22:52 +08:00
parent 7820d7eef5
commit eb25f831d3
10 changed files with 71 additions and 44 deletions
+3
View File
@@ -45,6 +45,8 @@
- **📦附件收发**:支持收发附件,使用R2对象存储保存和下载文件
- **🔔邮件推送**:接收邮件后可以转发到TG机器人或其他服务商邮箱
- **📈数据可视化**:使用echarts对系统数据详情,用户邮件增长可视化显示
- **⭐星标邮件**:标记重要邮件,以便快速查阅
@@ -186,6 +188,7 @@ cloud-mail
│ │ ├── entity #数据库实体层
│ │ ├── error #自定义异常
│ │ ├── hono #web框架配置 拦截器等
│ │ ├── init #数据库缓存初始化
│ │ ├── model #响应体数据封装
│ │ ├── security #身份认证层
│ │ ├── service #服务层
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 207 KiB

@@ -302,7 +302,7 @@ function htmlToText(email) {
function cleanSpace(text) {
return text
.replace(/[\u200B-\u200F\uFEFF\u034F\u200B-\u200F\u00A0\u3000]/g, '') // 移除零宽空格
.replace(/[\u200B-\u200F\uFEFF\u034F\u200B-\u200F\u00A0\u3000\u00AD]/g, '')// 移除零宽空格
.replace(/\s+/g, ' ') // 多空白合并成一个空格
.trim();
}
+26 -2
View File
@@ -7,8 +7,13 @@
<el-scrollbar class="scrollbar">
<div v-infinite-scroll="getAccountList" :infinite-scroll-distance="600" :infinite-scroll-immediate="false">
<el-card class="item" :class="itemBg(item.accountId)" v-for="item in accounts" :key="item.accountId" @click="changeAccount(item)">
<div class="account" @click.stop>
{{ item.email }}
<div class="account" @click.stop="copyAccount(item.email)">
<el-tooltip v-if="showCopyInfo" effect="dark" :hide-after="0" :show-after="800" placement="top" content="点击复制">
{{ item.email }}
</el-tooltip>
<template v-else >
{{ item.email }}
</template>
</div>
<div class="opt">
<div class="send-email" @click.stop>
@@ -142,6 +147,7 @@ const addRef = ref({})
let account = null
let turnstileId = null
let verifyToken = ''
let showCopyInfo = window.innerWidth > 1024
const addForm = reactive({
email: '',
suffix: settingStore.domainList[0]
@@ -264,6 +270,24 @@ function add() {
},100)
}
async function copyAccount(account) {
try {
await navigator.clipboard.writeText(account);
ElMessage({
message: '复制成功',
type: 'success',
plain: true,
})
} catch (err) {
console.error('复制失败:', err);
ElMessage({
message: '复制失败',
type: 'error',
plain: true,
})
}
}
function getAccountList() {
if (loading.value || followLoading.value || noLoading.value) return;
+8 -8
View File
@@ -19,12 +19,12 @@
<el-menu-item @click="router.push({name: 'star'})" index="star"
:class="route.meta.name === 'star' ? 'choose-item' : ''">
<Icon icon="solar:star-line-duotone" width="20" height="20" />
<span class="menu-name" style="margin-left: 20px">星标邮件</span>
<span class="menu-name" style="margin-left: 21px">星标邮件</span>
</el-menu-item>
<el-menu-item @click="router.push({name: 'setting'})" index="setting"
:class="route.meta.name === 'setting' ? 'choose-item' : ''">
<Icon icon="fluent:settings-48-regular" width="20" height="20" />
<span class="menu-name" style="margin-left: 20px">个人设置</span>
<span class="menu-name" style="margin-left: 21px">个人设置</span>
</el-menu-item>
<div class="manage-title" v-perm="['user:query','role:query','setting:query','analysis:query']">
<div>管理</div>
@@ -32,27 +32,27 @@
<el-menu-item @click="router.push({name: 'analysis'})" index="analysis" v-perm="'analysis:query'"
:class="route.meta.name === 'analysis' ? 'choose-item' : ''">
<Icon icon="fluent:data-pie-20-regular" width="24" height="24" />
<span class="menu-name" style="margin-left: 16px">分析页</span>
<span class="menu-name" style="margin-left: 18px">分析页</span>
</el-menu-item>
<el-menu-item @click="router.push({name: 'user'})" index="setting" v-perm="'user:query'"
:class="route.meta.name === 'user' ? 'choose-item' : ''">
<Icon icon="iconoir:user" width="24" height="24" />
<span class="menu-name" style="margin-left: 16px">用户列表</span>
<Icon icon="si:user-alt-2-line" width="20" height="20" />
<span class="menu-name" style="margin-left: 21px">用户列表</span>
</el-menu-item>
<el-menu-item @click="router.push({name: 'sys-email'})" index="sys-email" v-perm="'sys-email:query'"
:class="route.meta.name === 'sys-email' ? 'choose-item' : ''">
<Icon icon="fluent:mail-list-28-regular" width="22" height="22" />
<span class="menu-name" style="margin-left: 18px">邮件列表</span>
<span class="menu-name" style="margin-left: 20px">邮件列表</span>
</el-menu-item>
<el-menu-item @click="router.push({name: 'role'})" index="setting" v-perm="'role:query'"
:class="route.meta.name === 'role' ? 'choose-item' : ''">
<Icon icon="hugeicons:key-02" width="22" height="22" />
<span class="menu-name" style="margin-left: 18px">权限控制</span>
<span class="menu-name" style="margin-left: 20px">权限控制</span>
</el-menu-item>
<el-menu-item @click="router.push({name: 'sys-setting'})" index="sys-setting" v-perm="'setting:query'"
:class="route.meta.name === 'sys-setting' ? 'choose-item' : ''">
<Icon icon="eos-icons:system-ok-outlined" width="18" height="18" />
<span class="menu-name" style="margin-left: 23px">系统设置</span>
<span class="menu-name" style="margin-left: 24px">系统设置</span>
</el-menu-item>
</el-menu>
</div>
+2 -2
View File
@@ -174,7 +174,7 @@
</div>
<div class="settings-card">
<div class="card-title">邮件转发通知</div>
<div class="card-title">邮件推送</div>
<div class="card-content">
<div class="setting-item">
<div><span>Telegram 机器人</span></div>
@@ -362,7 +362,7 @@
<template #header>
<div class="forward-head">
<span class="forward-set-title">第三方邮箱</span>
<el-tooltip effect="dark" trigger="click" content="可以将邮件转到其他服务商邮箱,需要在cloudflare验证邮箱">
<el-tooltip effect="dark" content="可以将邮件转到其他服务商邮箱,需要在cloudflare验证邮箱">
<Icon class="warning" icon="fe:warning" width="18" height="18"/>
</el-tooltip>
</div>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -6,8 +6,8 @@
<title></title>
<link rel="icon" href="/assets/favicon-C5dAZutX.svg" type="image/svg+xml">
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<script type="module" crossorigin src="/assets/index-Cgh0xJS2.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Cobuvpco.css">
<script type="module" crossorigin src="/assets/index-DJSpSDrA.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DuYAEzF2.css">
</head>
<body>
<div id="loading-first">
+1 -1
View File
@@ -115,7 +115,7 @@ export async function email(message, env, ctx) {
<b>收件人:\u200B</b>${message.to}
<b>时间:</b>${dayjs.utc(emailRow.createTime).tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm')}
${emailUtils.htmlToText(params.content) || ''}
${params.text || emailUtils.htmlToText(params.content) || ''}
`;
const tgChatIds = tgChatId.split(',');