修复发送成功无法记录联系人

This commit is contained in:
eoao
2025-10-18 22:32:00 +08:00
parent 7042302e2f
commit a42e8fce8e
13 changed files with 35 additions and 65 deletions
+5 -23
View File
@@ -7,7 +7,7 @@
<h1>Cloud Mail</h1> <h1>Cloud Mail</h1>
</div> </div>
<div align="center"> <div align="center">
<h4>A responsive email service built with Vue 3 that supports email sending and can be deployed on Cloudflare. 🎉</h4> <h4>Serverless responsive email service with sending capabilities, deployable on the Cloudflare platform 🎉</h4>
</div> </div>
@@ -15,14 +15,12 @@
- [Online Demo](https://skymail.ink)<br> - [Online Demo](https://skymail.ink)<br>
- [Deployment Guide](https://doc.skymail.ink/en/)<br> - [Deployment Guide](https://doc.skymail.ink/en/)<br>
- [Beginners Guide UI Deployment](https://doc.skymail.ink/en/guide/via-ui.html) - [UI Deployment](https://doc.skymail.ink/en/guide/via-ui.html)
| ![](/doc/demo/demo1.png) | ![](/doc/demo/demo2.png) | | ![](/doc/demo/demo1.png) | ![](/doc/demo/demo2.png) |
|--------------------------|---------------------| |--------------------------|--------------------------|
| ![](/doc/demo/demo3.png) | ![](/doc/demo/demo4.png) | | ![](/doc/demo/demo3.png) | ![](/doc/demo/demo4.png) |
| ![](/doc/demo/demo5.png) | ![](/doc/demo/demo6.png) |
| ![](/doc/demo/demo7.png) | ![](/doc/demo/demo8.png) |
## Features ## Features
@@ -34,8 +32,6 @@
- **🛡️ Admin Features**: Admins can manage users and emails, with RBAC permission control to limit access to features and resources. - **🛡️ Admin Features**: Admins can manage users and emails, with RBAC permission control to limit access to features and resources.
- **🔀 Multiple Accounts**: Users can add multiple email accounts.
- **📦 Attachment Support**: Send and receive attachments, stored and downloaded via R2 object storage. - **📦 Attachment Support**: Send and receive attachments, stored and downloaded via R2 object storage.
- **🔔 Email Push**: Forward received emails to Telegram bots or other email providers. - **🔔 Email Push**: Forward received emails to Telegram bots or other email providers.
@@ -44,14 +40,8 @@
- **📈 Data Visualization**: Use Echarts to visualize system data, including user email growth. - **📈 Data Visualization**: Use Echarts to visualize system data, including user email growth.
- **⭐ Starred Emails**: Mark important emails for quick access.
- **🎨 Personalization**: Customize website title, login background, and transparency. - **🎨 Personalization**: Customize website title, login background, and transparency.
- **⚙️ Feature Settings**: Toggle on or off features like registration, email sending, and more, with the option to make the site private.
- **🤖 CAPTCHA**: Integrated with Turnstile CAPTCHA to prevent automated registration.
- **📜 More Features**: Under development... - **📜 More Features**: Under development...
## Tech Stack ## Tech Stack
@@ -62,7 +52,7 @@
- **ORM**: [Drizzle](https://orm.drizzle.team/) - **ORM**: [Drizzle](https://orm.drizzle.team/)
- **Platform**: [Cloudflare Workers](https://developers.cloudflare.com/workers/) - **Serverless Platform**: [Cloudflare Workers](https://developers.cloudflare.com/workers/)
- **Email Service**: [Resend](https://resend.com/) - **Email Service**: [Resend](https://resend.com/)
@@ -75,17 +65,9 @@
## Support ## Support
<a href="https://doc.skymail.ink/support.html"> <a href="https://doc.skymail.ink/support.html">
<img width="170px" src="./doc/images/support.png" alt=""> <img width="170px" src="./doc/images/support.png" alt="">
</a><br><br> </a>
**Special Sponsors**
[DartNode](https://dartnode.com)Providing cloud computing service resource support.
[![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
## License ## License
+9 -27
View File
@@ -6,7 +6,7 @@
<h1>Cloud Mail</h1> <h1>Cloud Mail</h1>
</div> </div>
<div align="center"> <div align="center">
<h4>使用Vue3开发的响应式邮箱服务,支持邮件发送,无需服务器可部署到Cloudflare平台 🎉</h4> <h4>Serverless 响应式邮箱服务,支持邮件发送,可部署到Cloudflare平台 🎉</h4>
</div> </div>
<div align="center"> <div align="center">
<span>简体中文 | <a href="/README-en.md" style="margin-left: 5px">English </a></span> <span>简体中文 | <a href="/README-en.md" style="margin-left: 5px">English </a></span>
@@ -14,26 +14,24 @@
## 项目简介 ## 项目简介
只需要一个域名,就可以创建多个不同的邮箱,类似各大邮箱平台 QQ邮箱,谷歌邮箱等,本项目使用Cloudflare部署,Resend推送邮件,无需服务器费用,搭建自己的邮箱服务 只需要一个域名,就可以创建多个不同的邮箱,类似各大邮箱平台,本项目可部署到 Cloudflare Workers ,降低服务器成本,搭建自己的邮箱服务
## 项目展示 ## 项目展示
- [在线演示](https://skymail.ink)<br> - [在线演示](https://skymail.ink)<br>
- [部署文档](https://doc.skymail.ink)<br> - [部署文档](https://doc.skymail.ink)<br>
- [小白保姆教程-界面部署](https://doc.skymail.ink/guide/via-ui.html) - [界面部署](https://doc.skymail.ink/guide/via-ui.html)
| ![](/doc/demo/demo1.png) | ![](/doc/demo/demo2.png) | | ![](/doc/demo/demo1.png) | ![](/doc/demo/demo2.png) |
|--------------------------|---------------------| |-----------------------|-----------------------|
| ![](/doc/demo/demo3.png) | ![](/doc/demo/demo4.png) | | ![](/doc/demo/demo3.png) | ![](/doc/demo/demo4.png) |
| ![](/doc/demo/demo5.png) | ![](/doc/demo/demo6.png) |
| ![](/doc/demo/demo7.png) | ![](/doc/demo/demo8.png) |
## 功能介绍 ## 功能介绍
- **💰 低成本使用**无需服务器,部署到 Cloudflare Workers 降低使用成本 - **💰 低成本使用** 部署到 Cloudflare Workers 降低服务器成本
- **💻 响应式设计**:响应式布局自动适配PC和大部分手机端浏览器 - **💻 响应式设计**:响应式布局自动适配PC和大部分手机端浏览器
@@ -41,8 +39,6 @@
- **🛡️ 管理员功能**:可以对用户,邮件进行管理,RABC权限控制对功能及使用资源限制 - **🛡️ 管理员功能**:可以对用户,邮件进行管理,RABC权限控制对功能及使用资源限制
- **🔀 多号模式**:开启后一个用户可以添加多个邮箱,默认一用户一邮箱,类似各大邮箱平台
- **📦 附件收发**:支持收发附件,使用R2对象存储保存和下载文件 - **📦 附件收发**:支持收发附件,使用R2对象存储保存和下载文件
- **🔔 邮件推送**:接收邮件后可以转发到TG机器人或其他服务商邮箱 - **🔔 邮件推送**:接收邮件后可以转发到TG机器人或其他服务商邮箱
@@ -51,27 +47,21 @@
- **📈 数据可视化**:使用echarts对系统数据详情,用户邮件增长可视化显示 - **📈 数据可视化**:使用echarts对系统数据详情,用户邮件增长可视化显示
- **⭐ 星标邮件**:标记重要邮件,以便快速查阅
- **🎨 个性化设置**:可以自定义网站标题,登录背景,透明度 - **🎨 个性化设置**:可以自定义网站标题,登录背景,透明度
- **⚙️ 功能设置**:可以对注册,邮件发送,添加等功能关闭和开启,设为私人站点
- **🤖 人机验证**:集成Turnstile人机验证,防止人机批量注册
- **📜 更多功能**:正在开发中... - **📜 更多功能**:正在开发中...
## 技术栈 ## 技术栈
- **框架**[Vue3](https://vuejs.org/) + [Element Plus](https://element-plus.org/) - **前端框架**[Vue3](https://vuejs.org/) + [Element Plus](https://element-plus.org/)
- **Web框架**[Hono](https://hono.dev/) - **Web框架**[Hono](https://hono.dev/)
- **ORM**[Drizzle](https://orm.drizzle.team/) - **ORM**[Drizzle](https://orm.drizzle.team/)
- **平台:** [Cloudflare workers](https://developers.cloudflare.com/workers/) - **Serverless 平台:** [Cloudflare workers](https://developers.cloudflare.com/workers/)
- **邮件推送:** [Resend](https://resend.com/) - **邮件推送:** [Resend](https://resend.com/)
@@ -84,17 +74,9 @@
## 赞助 ## 赞助
<a href="https://doc.skymail.ink/support.html" > <a href="https://doc.skymail.ink/support.html" >
<img width="170px" src="./doc/images/support.png" alt=""> <img width="170px" src="./doc/images/support.png" alt="">
</a><br><br> </a>
**特别赞助商**
[DartNode](https://dartnode.com):提供云计算服务资源支持
[![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
## 许可证 ## 许可证
Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 450 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 KiB

After

Width:  |  Height:  |  Size: 525 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

+19 -13
View File
@@ -167,12 +167,14 @@ const selectRecipientList = ref([])
const contacts = computed(() => writerStore.sendRecipientRecord.map(item => ({email: item}))) const contacts = computed(() => writerStore.sendRecipientRecord.map(item => ({email: item})))
function openContacts() { function openContacts() {
form.receiveEmail.forEach(item => {
if (writerStore.sendRecipientRecord.includes(item)) {
contactsTabRef.value.toggleRowSelection({email: item});
}
})
showContacts.value = true showContacts.value = true
nextTick(() => {
form.receiveEmail.forEach(item => {
if (writerStore.sendRecipientRecord.includes(item)) {
contactsTabRef.value.toggleRowSelection({email: item});
}
})
})
} }
function deleteContact() { function deleteContact() {
@@ -372,6 +374,8 @@ async function sendEmail() {
userStore.refreshUserInfo(); userStore.refreshUserInfo();
addRecipientRecord();
if (form.draftId) { if (form.draftId) {
form.subject = '' form.subject = ''
form.content = '' form.content = ''
@@ -379,8 +383,8 @@ async function sendEmail() {
draftStore.setDraft = {...toRaw(form)} draftStore.setDraft = {...toRaw(form)}
} }
resetForm()
show.value = false show.value = false
resetForm();
}).catch((e) => { }).catch((e) => {
ElNotification({ ElNotification({
title: t('sendFailMsg'), title: t('sendFailMsg'),
@@ -389,20 +393,22 @@ async function sendEmail() {
position: 'bottom-right' position: 'bottom-right'
}) })
show.value = true show.value = true
addRecipientRecord();
}).finally(() => { }).finally(() => {
percentMessage.close() percentMessage.close()
percent.value = 0 percent.value = 0
sending = false sending = false
writerStore.sendRecipientRecord = writerStore.sendRecipientRecord.filter(
email => !form.receiveEmail.includes(email)
);
writerStore.sendRecipientRecord.unshift(...form.receiveEmail);
writerStore.sendRecipientRecord = writerStore.sendRecipientRecord.slice(0, 500);
}) })
} }
function addRecipientRecord() {
writerStore.sendRecipientRecord = writerStore.sendRecipientRecord.filter(
email => !form.receiveEmail.includes(email)
);
writerStore.sendRecipientRecord.unshift(...form.receiveEmail);
writerStore.sendRecipientRecord = writerStore.sendRecipientRecord.slice(0, 500);
}
function resetForm() { function resetForm() {
form.receiveEmail = [] form.receiveEmail = []
+2 -2
View File
@@ -10,9 +10,9 @@ const r2Service = {
} }
const setting = await settingService.query(c); const setting = await settingService.query(c);
const { bucket, region, endpoint, s3AccessKey, s3SecretKey } = setting; const { bucket, endpoint, s3AccessKey, s3SecretKey } = setting;
return !!(bucket && region && endpoint && s3AccessKey && s3SecretKey); return !!(bucket && endpoint && s3AccessKey && s3SecretKey);
}, },
async putObj(c, key, content, metadata) { async putObj(c, key, content, metadata) {