-
-
@@ -147,8 +141,6 @@ export default {
setup() {
const router = useRouter()
const route = useRoute()
- const stars = ref(null)
- const particles = ref(null)
const capsule3d = ref(null)
// 胶囊数据
@@ -164,70 +156,6 @@ export default {
// 获取胶囊ID
const mailId = computed(() => route.params.id)
- // 生成星空背景
- const generateStars = () => {
- if (!stars.value) return
-
- const starsContainer = stars.value
- const starCount = 200
-
- for (let i = 0; i < starCount; i++) {
- const star = document.createElement('div')
- star.className = 'star'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 3 + 1
-
- // 随机动画延迟
- const delay = Math.random() * 4
-
- star.style.left = `${left}%`
- star.style.top = `${top}%`
- star.style.width = `${size}px`
- star.style.height = `${size}px`
- star.style.animationDelay = `${delay}s`
-
- starsContainer.appendChild(star)
- }
- }
-
- // 生成漂浮粒子
- const generateParticles = () => {
- if (!particles.value) return
-
- const particlesContainer = particles.value
- const particleCount = 30
-
- for (let i = 0; i < particleCount; i++) {
- const particle = document.createElement('div')
- particle.className = 'particle'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 6 + 2
-
- // 随机动画延迟和持续时间
- const delay = Math.random() * 10
- const duration = Math.random() * 20 + 20
-
- particle.style.left = `${left}%`
- particle.style.top = `${top}%`
- particle.style.width = `${size}px`
- particle.style.height = `${size}px`
- particle.style.animationDelay = `${delay}s`
- particle.style.animationDuration = `${duration}s`
-
- particlesContainer.appendChild(particle)
- }
- }
-
// 加载胶囊数据
const loadCapsuleData = async () => {
try {
@@ -355,14 +283,10 @@ export default {
}
onMounted(() => {
- generateStars()
- generateParticles()
loadCapsuleData()
})
return {
- stars,
- particles,
capsule3d,
capsuleData,
isCapsuleOpened,
@@ -692,29 +616,4 @@ export default {
transform: translateX(5px);
}
}
-
-/* 粒子效果 */
-.particle {
- position: absolute;
- background: radial-gradient(circle, rgba(0, 212, 255, 0.8) 0%, rgba(0, 212, 255, 0) 70%);
- border-radius: 50%;
- animation: float-particle linear infinite;
-}
-
-@keyframes float-particle {
- 0% {
- transform: translateY(100vh) rotate(0deg);
- opacity: 0;
- }
- 10% {
- opacity: 1;
- }
- 90% {
- opacity: 1;
- }
- 100% {
- transform: translateY(-100vh) rotate(720deg);
- opacity: 0;
- }
-}
\ No newline at end of file
diff --git a/src/views/Compose.vue b/src/views/Compose.vue
index 7a403b7..3e5b81c 100644
--- a/src/views/Compose.vue
+++ b/src/views/Compose.vue
@@ -1,10 +1,5 @@
-
-
-
-
-
-
@@ -192,12 +187,12 @@
import { ref, onMounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import { showLoadingToast, showSuccessToast, showFailToast, closeToast, Dialog } from 'vant'
+import { mailAPI, capsuleAPI } from '../api'
export default {
name: 'Compose',
setup() {
const router = useRouter()
- const stars = ref(null)
// 表单数据
const recipientType = ref('SELF') // 对应API的SELF, SPECIFIC, PUBLIC
@@ -324,37 +319,6 @@ export default {
}
}
- // 生成星空背景
- const generateStars = () => {
- if (!stars.value) return
-
- const starsContainer = stars.value
- const starCount = 100
-
- for (let i = 0; i < starCount; i++) {
- const star = document.createElement('div')
- star.className = 'star'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 3 + 1
-
- // 随机动画延迟
- const delay = Math.random() * 4
-
- star.style.left = `${left}%`
- star.style.top = `${top}%`
- star.style.width = `${size}px`
- star.style.height = `${size}px`
- star.style.animationDelay = `${delay}s`
-
- starsContainer.appendChild(star)
- }
- }
-
// 返回上一页
const goBack = () => {
router.back()
@@ -590,32 +554,59 @@ export default {
return
}
+ if (!content.value) {
+ showFailToast('请填写邮件内容')
+ return
+ }
+
try {
showLoadingToast({
message: '保存中...',
forbidClick: true,
})
- // 模拟API调用延迟
- await new Promise(resolve => setTimeout(resolve, 1000))
+ const mailData = {
+ ...buildMailData(),
+ status: 'DRAFT'
+ }
- // 保存到本地存储作为草稿
- const mailData = buildMailData()
- const drafts = JSON.parse(localStorage.getItem('draftMails') || '[]')
- drafts.push({
- ...mailData,
- id: Date.now().toString(),
- status: 'DRAFT',
- createdAt: new Date().toISOString()
- })
- localStorage.setItem('draftMails', JSON.stringify(drafts))
+ // 先创建邮件草稿
+ const mailResponse = await mailAPI.createMail(mailData)
+
+ if (mailResponse.code !== 200) {
+ closeToast()
+ showFailToast(mailResponse.message || '保存失败')
+ return
+ }
+
+ // 将邮件存入胶囊
+ const capsuleData = {
+ capsuleStyle: capsuleStyle.value,
+ position: {
+ x: Math.random(),
+ y: Math.random(),
+ z: Math.random()
+ },
+ glowIntensity: Math.random()
+ }
+
+ const capsuleResponse = await capsuleAPI.saveToCapsule(
+ mailResponse.data.mailId,
+ capsuleData
+ )
closeToast()
- showSuccessToast('草稿已保存')
- router.back()
+
+ if (capsuleResponse.code === 200) {
+ showSuccessToast('邮件已存入胶囊')
+ router.back()
+ } else {
+ showFailToast(capsuleResponse.message || '存入胶囊失败')
+ }
} catch (error) {
closeToast()
- const errorMessage = error.response?.data?.message || '保存失败,请重试'
+ console.error('存入胶囊失败:', error)
+ const errorMessage = error.response?.data?.message || '存入胶囊失败,请重试'
showFailToast(errorMessage)
}
}
@@ -643,59 +634,77 @@ export default {
forbidClick: true,
})
- // 模拟API调用延迟
- await new Promise(resolve => setTimeout(resolve, 1500))
-
+ // 先构建邮件数据
const mailData = buildMailData()
- // 保存到本地存储作为已发送邮件
- const sentMails = JSON.parse(localStorage.getItem('sentMails') || '[]')
- sentMails.push({
+ // 创建邮件草稿
+ const createMailData = {
...mailData,
- id: Date.now().toString(),
- status: 'PENDING',
- createdAt: new Date().toISOString()
- })
- localStorage.setItem('sentMails', JSON.stringify(sentMails))
-
- closeToast()
-
- // 计算发送时间用于显示
- let deliveryDate
- if (timeType.value === 'preset' || timeType.value === 'custom') {
- deliveryDate = new Date(mailData.sendTime)
- } else {
- deliveryDate = new Date()
- deliveryDate.setFullYear(deliveryDate.getFullYear() + 1) // 默认显示一年后
+ status: 'DRAFT'
}
- Dialog.confirm({
- title: '邮件已发送',
- message: `您的邮件将在${deliveryDate.toLocaleDateString()}送达,是否返回首页?`,
- confirmButtonText: '返回首页',
- cancelButtonText: '继续撰写',
- })
- .then(() => {
- router.push('/home')
- })
- .catch(() => {
- // 继续撰写
+ const mailResponse = await mailAPI.createMail(createMailData)
+
+ if (mailResponse.code !== 200) {
+ closeToast()
+ showFailToast(mailResponse.message || '创建邮件失败')
+ return
+ }
+
+ // 获取发送时间和触发条件
+ const { sendTime, triggerType, triggerCondition } = mailData
+
+ // 调用发送至未来API
+ const response = await mailAPI.sendToFuture(
+ mailResponse.data.mailId,
+ sendTime,
+ triggerType,
+ triggerCondition
+ )
+
+ if (response.code === 200) {
+ closeToast()
+
+ // 计算发送时间用于显示
+ let deliveryDate
+ if (timeType.value === 'preset' || timeType.value === 'custom') {
+ deliveryDate = new Date(sendTime)
+ } else {
+ deliveryDate = new Date()
+ deliveryDate.setFullYear(deliveryDate.getFullYear() + 1) // 默认显示一年后
+ }
+
+ Dialog.confirm({
+ title: '邮件已发送至未来',
+ message: `您的邮件将在${deliveryDate.toLocaleDateString()}送达,是否返回首页?`,
+ confirmButtonText: '返回首页',
+ cancelButtonText: '继续撰写',
})
+ .then(() => {
+ router.push('/home')
+ })
+ .catch(() => {
+ // 继续撰写
+ })
+ } else {
+ closeToast()
+ showFailToast(response.message || '发送失败')
+ }
} catch (error) {
closeToast()
+ console.error('发送邮件失败:', error)
const errorMessage = error.response?.data?.message || '发送失败,请重试'
showFailToast(errorMessage)
}
}
onMounted(() => {
- generateStars()
+ // 组件初始化
})
return {
- stars,
- recipientType,
- recipientEmail,
+ recipientType,
+ recipientEmail,
timeType,
selectedPresetTime,
customDeliveryDate,
diff --git a/src/views/Home.vue b/src/views/Home.vue
index 3cb3d65..4fa529f 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -1,10 +1,5 @@
-
-
-
-
-
-
-
- 时光胶囊
- 收件箱
- 发件箱
- 个人中心
-
+
-
-
+
@@ -112,13 +82,18 @@ import { ref, onMounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import { showFailToast } from 'vant'
import { userState, mailState, capsuleState, mailActions, capsuleActions } from '../store'
+import SearchComponent from '@/components/SearchComponent.vue'
+import BottomTabbar from '@/components/BottomTabbar.vue'
export default {
+ components: {
+ SearchComponent,
+ BottomTabbar
+ },
name: 'Home',
setup() {
const router = useRouter()
const active = ref(0)
- const stars = ref(null)
const capsulesSpace = ref(null)
const showSearch = ref(false)
const showNotifications = ref(false)
@@ -139,37 +114,6 @@ export default {
return '晚上好,今天过得怎么样'
})
- // 生成星空背景
- const generateStars = () => {
- if (!stars.value) return
-
- const starsContainer = stars.value
- const starCount = 150
-
- for (let i = 0; i < starCount; i++) {
- const star = document.createElement('div')
- star.className = 'star'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 3 + 1
-
- // 随机动画延迟
- const delay = Math.random() * 4
-
- star.style.left = `${left}%`
- star.style.top = `${top}%`
- star.style.width = `${size}px`
- star.style.height = `${size}px`
- star.style.animationDelay = `${delay}s`
-
- starsContainer.appendChild(star)
- }
- }
-
// 获取胶囊样式
const getCapsuleStyle = (capsule, index) => {
// 添加基于索引的延迟动画
@@ -262,24 +206,14 @@ export default {
router.push(`/capsule/${capsule.mailId || capsule.capsuleId || capsule.id}`)
}
- // 跳转到撰写页面
- const goToCompose = () => {
- router.push('/compose')
- }
-
// 搜索处理
- const onSearch = (value) => {
- if (!value) {
+ const onSearch = () => {
+ if (!searchValue.value.trim()) {
showFailToast('请输入搜索内容')
return
}
-
- // 暂时显示提示,因为搜索页面尚未实现
- showFailToast(`搜索功能开发中,您搜索了: ${value}`)
- showSearch.value = false
-
- // TODO: 实现搜索功能后,可以跳转到搜索页面
- // router.push(`/search?q=${encodeURIComponent(value)}`)
+ showFailToast(`搜索: ${searchValue.value}`)
+ // TODO: 实现搜索功能
}
// 获取时光胶囊数据
@@ -314,7 +248,6 @@ export default {
}
onMounted(async () => {
- generateStars()
await fetchCapsules()
await fetchNotifications()
})
@@ -323,12 +256,10 @@ export default {
active,
userName,
greetingText,
- stars,
capsulesSpace,
capsules,
showSearch,
showNotifications,
- searchValue,
notifications,
activeCapsule,
getCapsuleStyle,
@@ -341,7 +272,6 @@ export default {
formatDate,
formatTime,
openCapsule,
- goToCompose,
onSearch
}
}
@@ -538,30 +468,6 @@ export default {
}
}
-.fab-container {
- position: absolute;
- bottom: 80px;
- right: 20px;
- z-index: 10;
-}
-
-.fab-button {
- width: 60px;
- height: 60px;
- border-radius: 50%;
- border: none;
- font-size: 14px;
- min-height: 44px;
- min-width: 44px;
- -webkit-tap-highlight-color: transparent;
- transition: transform 0.2s, box-shadow 0.2s;
-}
-
-.fab-button:active {
- transform: scale(0.95);
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
-}
-
.search-popup {
padding: 20px;
}
@@ -599,7 +505,7 @@ export default {
}
.notification-time {
- font-size: 12px;
- color: var(--text-secondary);
-}
+ font-size: 12px;
+ color: var(--text-secondary);
+ }
\ No newline at end of file
diff --git a/src/views/Inbox.vue b/src/views/Inbox.vue
index f538440..b266399 100644
--- a/src/views/Inbox.vue
+++ b/src/views/Inbox.vue
@@ -1,10 +1,5 @@
-
-
-
@@ -54,36 +49,11 @@
-
-
-
-
-
-
-
-
-
-
@@ -140,7 +135,6 @@ export default {
const router = useRouter()
const active = ref(1)
const activeTab = ref(0)
- const stars = ref(null)
// 使用直接导入的状态和操作
const mails = computed(() => mailState.inboxList || [])
@@ -211,37 +205,6 @@ export default {
}
}
- // 生成星空背景
- const generateStars = () => {
- if (!stars.value) return
-
- const starsContainer = stars.value
- const starCount = 100
-
- for (let i = 0; i < starCount; i++) {
- const star = document.createElement('div')
- star.className = 'star'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 3 + 1
-
- // 随机动画延迟
- const delay = Math.random() * 4
-
- star.style.left = `${left}%`
- star.style.top = `${top}%`
- star.style.width = `${size}px`
- star.style.height = `${size}px`
- star.style.animationDelay = `${delay}s`
-
- starsContainer.appendChild(star)
- }
- }
-
// 返回上一页
const goBack = () => {
router.back()
@@ -325,14 +288,12 @@ export default {
}
onMounted(() => {
- generateStars()
fetchMails(true)
})
return {
active,
activeTab,
- stars,
deliveredMails,
incomingMails,
loading,
diff --git a/src/views/Login.vue b/src/views/Login.vue
index 20bd36a..e002440 100644
--- a/src/views/Login.vue
+++ b/src/views/Login.vue
@@ -1,10 +1,5 @@
-
-
-
-
-
-
-
-
@@ -37,6 +32,10 @@
登录
+
+
测试账号:test@example.com / testuser
+密码:任意密码
+
还没有账号?立即注册
@@ -59,38 +58,6 @@ export default {
const router = useRouter()
const email = ref('')
const password = ref('')
- const stars = ref(null)
-
- // 生成星空背景
- const generateStars = () => {
- if (!stars.value) return
-
- const starsContainer = stars.value
- const starCount = 100
-
- for (let i = 0; i < starCount; i++) {
- const star = document.createElement('div')
- star.className = 'star'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 3 + 1
-
- // 随机动画延迟
- const delay = Math.random() * 4
-
- star.style.left = `${left}%`
- star.style.top = `${top}%`
- star.style.width = `${size}px`
- star.style.height = `${size}px`
- star.style.animationDelay = `${delay}s`
-
- starsContainer.appendChild(star)
- }
- }
// 登录处理
const onSubmit = async () => {
@@ -124,13 +91,12 @@ export default {
}
onMounted(() => {
- generateStars()
+ // 组件挂载后的初始化代码
})
return {
email,
password,
- stars,
onSubmit,
goToRegister
}
diff --git a/src/views/Profile.vue b/src/views/Profile.vue
index 21a97a5..dc33a95 100644
--- a/src/views/Profile.vue
+++ b/src/views/Profile.vue
@@ -1,10 +1,5 @@
-
-
@@ -148,7 +143,6 @@ export default {
setup() {
const router = useRouter()
const active = ref(3)
- const stars = ref(null)
const showAboutPopup = ref(false)
// 使用直接导入的状态和操作
@@ -195,37 +189,6 @@ export default {
}
}
- // 生成星空背景
- const generateStars = () => {
- if (!stars.value) return
-
- const starsContainer = stars.value
- const starCount = 100
-
- for (let i = 0; i < starCount; i++) {
- const star = document.createElement('div')
- star.className = 'star'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 3 + 1
-
- // 随机动画延迟
- const delay = Math.random() * 4
-
- star.style.left = `${left}%`
- star.style.top = `${top}%`
- star.style.width = `${size}px`
- star.style.height = `${size}px`
- star.style.animationDelay = `${delay}s`
-
- starsContainer.appendChild(star)
- }
- }
-
// 返回上一页
const goBack = () => {
router.back()
@@ -303,7 +266,6 @@ export default {
}
onMounted(() => {
- generateStars()
fetchUserProfile()
fetchStatistics()
@@ -318,7 +280,6 @@ export default {
return {
active,
- stars,
userName,
userEmail,
userMotto,
diff --git a/src/views/Register.vue b/src/views/Register.vue
index 4e13e04..9b8b16f 100644
--- a/src/views/Register.vue
+++ b/src/views/Register.vue
@@ -1,10 +1,5 @@
-
-
-
-
-
-
-
-
@@ -85,38 +80,6 @@ export default {
const email = ref('')
const password = ref('')
const confirmPassword = ref('')
- const stars = ref(null)
-
- // 生成星空背景
- const generateStars = () => {
- if (!stars.value) return
-
- const starsContainer = stars.value
- const starCount = 100
-
- for (let i = 0; i < starCount; i++) {
- const star = document.createElement('div')
- star.className = 'star'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 3 + 1
-
- // 随机动画延迟
- const delay = Math.random() * 4
-
- star.style.left = `${left}%`
- star.style.top = `${top}%`
- star.style.width = `${size}px`
- star.style.height = `${size}px`
- star.style.animationDelay = `${delay}s`
-
- starsContainer.appendChild(star)
- }
- }
// 验证密码
const validatePassword = () => {
@@ -159,7 +122,7 @@ export default {
}
onMounted(() => {
- generateStars()
+ // 组件挂载后的初始化代码
})
return {
@@ -167,7 +130,6 @@ export default {
email,
password,
confirmPassword,
- stars,
validatePassword,
onSubmit,
goToLogin
diff --git a/src/views/Sent.vue b/src/views/Sent.vue
index 28d905e..0c656b8 100644
--- a/src/views/Sent.vue
+++ b/src/views/Sent.vue
@@ -1,10 +1,5 @@
-
-
@@ -138,7 +133,6 @@ export default {
setup() {
const router = useRouter()
const active = ref(2)
- const stars = ref(null)
const sortType = ref('sendDate')
const showSort = ref(false)
const showPreview = ref(false)
@@ -176,37 +170,6 @@ export default {
}
})
- // 生成星空背景
- const generateStars = () => {
- if (!stars.value) return
-
- const starsContainer = stars.value
- const starCount = 100
-
- for (let i = 0; i < starCount; i++) {
- const star = document.createElement('div')
- star.className = 'star'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 3 + 1
-
- // 随机动画延迟
- const delay = Math.random() * 4
-
- star.style.left = `${left}%`
- star.style.top = `${top}%`
- star.style.width = `${size}px`
- star.style.height = `${size}px`
- star.style.animationDelay = `${delay}s`
-
- starsContainer.appendChild(star)
- }
- }
-
// 获取已发送邮件列表
const fetchMails = async (reset = false) => {
if (loading.value || finished.value) return
@@ -378,13 +341,11 @@ export default {
}
onMounted(() => {
- generateStars()
fetchMails(true)
})
return {
active,
- stars,
sortType,
showSort,
showPreview,
diff --git a/src/views/Timeline.vue b/src/views/Timeline.vue
index 5f322d6..d24aa65 100644
--- a/src/views/Timeline.vue
+++ b/src/views/Timeline.vue
@@ -1,10 +1,5 @@
-
-
-
-
-
@@ -138,7 +133,6 @@ export default {
setup() {
const router = useRouter()
const active = ref(3)
- const stars = ref(null)
const showFilter = ref(false)
// 使用直接导入的状态和操作
@@ -198,37 +192,6 @@ export default {
return timelineData.value.length
})
- // 生成星空背景
- const generateStars = () => {
- if (!stars.value) return
-
- const starsContainer = stars.value
- const starCount = 150
-
- for (let i = 0; i < starCount; i++) {
- const star = document.createElement('div')
- star.className = 'star'
-
- // 随机位置
- const left = Math.random() * 100
- const top = Math.random() * 100
-
- // 随机大小
- const size = Math.random() * 3 + 1
-
- // 随机动画延迟
- const delay = Math.random() * 4
-
- star.style.left = `${left}%`
- star.style.top = `${top}%`
- star.style.width = `${size}px`
- star.style.height = `${size}px`
- star.style.animationDelay = `${delay}s`
-
- starsContainer.appendChild(star)
- }
- }
-
// 格式化日期
const formatDate = (dateStr) => {
const date = new Date(dateStr)
@@ -256,13 +219,11 @@ export default {
}
onMounted(() => {
- generateStars()
fetchTimeline()
})
return {
active,
- stars,
timelineData,
showFilter,
filterType,
diff --git a/发送至未来API文档.md b/发送至未来API文档.md
new file mode 100644
index 0000000..f22d13b
--- /dev/null
+++ b/发送至未来API文档.md
@@ -0,0 +1,307 @@
+# 发送至未来功能 API 文档
+
+## 概述
+发送至未来功能允许用户将邮件设置为在未来特定时间自动发送,邮件状态将变为待投递(PENDING),系统会在指定时间自动处理发送。
+
+## API 接口
+
+### 发送至未来
+
+**接口地址:** `POST /api/v1/mails/send-to-future`
+
+**接口描述:** 将草稿状态的邮件设置为在未来特定时间自动发送
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| mailId | string | 是 | 邮件ID |
+| sendTime | string | 是 | 发送时间,ISO格式时间字符串(如:2025-12-31T23:59:59Z) |
+| triggerType | string | 否 | 触发类型:TIME(时间)、LOCATION(地点)、EVENT(事件),默认为TIME |
+| triggerCondition | object | 否 | 触发条件 |
+| triggerCondition.location | object | 否 | 地点触发条件 |
+| triggerCondition.location.latitude | number | 否 | 纬度 |
+| triggerCondition.location.longitude | number | 否 | 经度 |
+| triggerCondition.location.city | string | 否 | 城市 |
+| triggerCondition.event | object | 否 | 事件触发条件 |
+| triggerCondition.event.keywords | array | 否 | 关键词列表 |
+| triggerCondition.event.type | string | 否 | 事件类型 |
+
+#### 请求示例
+
+```json
+{
+ "mailId": "mail_1234567890",
+ "sendTime": "2025-12-31T23:59:59Z",
+ "triggerType": "TIME",
+ "triggerCondition": {}
+}
+```
+
+#### 响应参数
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | number | 响应状态码,200表示成功 |
+| message | string | 响应消息 |
+| data | object | 响应数据 |
+| data.mailId | string | 邮件ID |
+| data.capsuleId | string | 胶囊ID |
+| data.status | string | 邮件状态:PENDING |
+| data.sendTime | string | 发送时间 |
+| data.countdown | number | 倒计时秒数 |
+| data.updatedAt | string | 更新时间,ISO格式时间字符串 |
+
+#### 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "mailId": "mail_1234567890",
+ "capsuleId": "capsule_1234567890",
+ "status": "PENDING",
+ "sendTime": "2025-12-31T23:59:59Z",
+ "countdown": 94608000,
+ "updatedAt": "2023-07-20T10:30:00Z"
+ }
+}
+```
+
+### 获取待发送邮件列表
+
+**接口地址:** `GET /api/v1/mails`
+
+**接口描述:** 获取用户的待发送邮件列表
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| type | string | 否 | 邮件类型:INBOX、SENT、DRAFT,获取待发送时使用SENT |
+| status | string | 否 | 状态筛选:PENDING、DELIVERING、DELIVERED、DRAFT,获取待发送时使用PENDING |
+| page | number | 否 | 页码,默认为1 |
+| size | number | 否 | 每页数量,默认为10 |
+
+#### 请求示例
+
+```
+GET /api/v1/mails?type=SENT&status=PENDING&page=1&size=10
+```
+
+#### 响应参数
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | number | 响应状态码,200表示成功 |
+| message | string | 响应消息 |
+| data | object | 响应数据 |
+| data.list | array | 邮件列表 |
+| data.list[].mailId | string | 邮件ID |
+| data.list[].title | string | 邮件标题 |
+| data.list[].sender | object | 发件人信息 |
+| data.list[].recipient | object | 收件人信息 |
+| data.list[].sendTime | string | 发送时间 |
+| data.list[].deliveryTime | string | 送达时间 |
+| data.list[].status | string | 邮件状态 |
+| data.list[].hasAttachments | boolean | 是否有附件 |
+| data.list[].isEncrypted | boolean | 是否加密 |
+| data.list[].capsuleStyle | string | 胶囊样式 |
+| data.list[].countdown | number | 倒计时秒数 |
+| data.total | number | 总数量 |
+| data.page | number | 当前页码 |
+| data.size | number | 每页数量 |
+
+#### 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "list": [
+ {
+ "mailId": "mail_1234567890",
+ "title": "写给未来的自己",
+ "sender": {
+ "userId": "user_123",
+ "username": "张三",
+ "avatar": "https://example.com/avatar.jpg"
+ },
+ "recipient": {
+ "userId": "user_123",
+ "username": "张三",
+ "avatar": "https://example.com/avatar.jpg"
+ },
+ "sendTime": "2025-12-31T23:59:59Z",
+ "deliveryTime": null,
+ "status": "PENDING",
+ "hasAttachments": true,
+ "isEncrypted": false,
+ "capsuleStyle": "default",
+ "countdown": 94608000
+ }
+ ],
+ "total": 1,
+ "page": 1,
+ "size": 10
+ }
+}
+```
+
+### 获取待发送邮件详情
+
+**接口地址:** `GET /api/v1/mails/{mailId}`
+
+**接口描述:** 获取指定待发送邮件的详细信息
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| mailId | string | 是 | 邮件ID |
+
+#### 请求示例
+
+```
+GET /api/v1/mails/mail_1234567890
+```
+
+#### 响应参数
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | number | 响应状态码,200表示成功 |
+| message | string | 响应消息 |
+| data | object | 响应数据 |
+| data.mailId | string | 邮件ID |
+| data.title | string | 邮件标题 |
+| data.content | string | 邮件内容 |
+| data.sender | object | 发件人信息 |
+| data.recipient | object | 收件人信息 |
+| data.sendTime | string | 发送时间 |
+| data.createdAt | string | 创建时间 |
+| data.deliveryTime | string | 送达时间 |
+| data.status | string | 邮件状态 |
+| data.triggerType | string | 触发类型 |
+| data.triggerCondition | object | 触发条件 |
+| data.attachments | array | 附件列表 |
+| data.isEncrypted | boolean | 是否加密 |
+| data.capsuleStyle | string | 胶囊样式 |
+| data.canEdit | boolean | 是否可编辑(待发送状态为false) |
+| data.canRevoke | boolean | 是否可撤销(待发送状态为true) |
+| data.countdown | number | 倒计时秒数 |
+
+#### 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "mailId": "mail_1234567890",
+ "title": "写给未来的自己",
+ "content": "亲爱的未来的我,当你读到这封信时,希望你已经实现了现在的梦想...",
+ "sender": {
+ "userId": "user_123",
+ "username": "张三",
+ "avatar": "https://example.com/avatar.jpg",
+ "email": "zhangsan@example.com"
+ },
+ "recipient": {
+ "userId": "user_123",
+ "username": "张三",
+ "avatar": "https://example.com/avatar.jpg",
+ "email": "zhangsan@example.com"
+ },
+ "sendTime": "2025-12-31T23:59:59Z",
+ "createdAt": "2023-07-20T10:30:00Z",
+ "deliveryTime": null,
+ "status": "PENDING",
+ "triggerType": "TIME",
+ "triggerCondition": {},
+ "attachments": [
+ {
+ "id": "attach_123",
+ "type": "IMAGE",
+ "url": "https://example.com/image.jpg",
+ "thumbnail": "https://example.com/thumb.jpg",
+ "size": 1024000
+ }
+ ],
+ "isEncrypted": false,
+ "capsuleStyle": "default",
+ "canEdit": false,
+ "canRevoke": true,
+ "countdown": 94608000
+ }
+}
+```
+
+### 撤销待发送邮件
+
+**接口地址:** `POST /api/v1/mails/{mailId}/revoke`
+
+**接口描述:** 撤销待发送的邮件,将状态改回草稿
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| mailId | string | 是 | 邮件ID(路径参数) |
+
+#### 请求示例
+
+```
+POST /api/v1/mails/mail_1234567890/revoke
+```
+
+#### 响应参数
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | number | 响应状态码,200表示成功 |
+| message | string | 响应消息 |
+| data | object | 响应数据 |
+| data.mailId | string | 邮件ID |
+| data.status | string | 邮件状态:DRAFT |
+| data.revokedAt | string | 撤销时间,ISO格式时间字符串 |
+
+#### 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "mailId": "mail_1234567890",
+ "status": "DRAFT",
+ "revokedAt": "2023-07-21T14:30:00Z"
+ }
+}
+```
+
+## 错误码
+
+| 错误码 | 说明 |
+|--------|------|
+| 200 | 成功 |
+| 400 | 请求参数错误 |
+| 401 | 未授权,需要登录 |
+| 403 | 权限不足 |
+| 404 | 资源不存在 |
+| 422 | 验证失败 |
+| 500 | 服务器内部错误 |
+
+## 注意事项
+
+1. 发送至未来的邮件状态为PENDING,表示等待系统在未来指定时间自动发送
+2. 只有草稿状态(DRAFT)的邮件可以设置为发送至未来
+3. 发送时间必须晚于当前时间至少1小时
+4. 待发送状态的邮件不能编辑内容,但可以撤销发送
+5. 撤销后的邮件状态将变回草稿(DRAFT),可以重新编辑或设置发送时间
+6. 系统会在发送时间到达前10分钟进入投递中状态(DELIVERING)
+7. 免费用户每月最多可设置5封邮件发送至未来
+8. 附件大小限制为10MB
+9. 加密邮件需要额外验证才能查看内容
\ No newline at end of file
diff --git a/存入胶囊API文档.md b/存入胶囊API文档.md
new file mode 100644
index 0000000..a75a18f
--- /dev/null
+++ b/存入胶囊API文档.md
@@ -0,0 +1,372 @@
+# 存入胶囊功能 API 文档
+
+## 概述
+存入胶囊功能允许用户将邮件保存为时光胶囊状态,邮件将以草稿形式保存,用户可以随时编辑或发送。
+
+## API 接口
+
+### 创建胶囊邮件
+
+**接口地址:** `POST /api/v1/mails`
+
+**接口描述:** 创建一个新邮件并将其保存为时光胶囊状态(草稿)
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| title | string | 是 | 邮件标题 |
+| content | string | 是 | 邮件内容 |
+| recipientType | string | 是 | 收件人类型:SELF(自己)、SPECIFIC(指定收件人)、PUBLIC(公开信) |
+| recipientEmail | string | 否 | 指定收件人邮箱(当recipientType为SPECIFIC时必填) |
+| sendTime | string | 否 | 发送时间,ISO格式时间字符串(如:2025-12-31T23:59:59Z) |
+| triggerType | string | 否 | 触发类型:TIME(时间)、LOCATION(地点)、EVENT(事件) |
+| triggerCondition | object | 否 | 触发条件 |
+| triggerCondition.location | object | 否 | 地点触发条件 |
+| triggerCondition.location.latitude | number | 否 | 纬度 |
+| triggerCondition.location.longitude | number | 否 | 经度 |
+| triggerCondition.location.city | string | 否 | 城市 |
+| triggerCondition.event | object | 否 | 事件触发条件 |
+| triggerCondition.event.keywords | array | 否 | 关键词列表 |
+| triggerCondition.event.type | string | 否 | 事件类型 |
+| attachments | array | 否 | 附件列表 |
+| attachments[].type | string | 否 | 附件类型:IMAGE、VOICE、VIDEO |
+| attachments[].url | string | 否 | 附件URL |
+| attachments[].thumbnail | string | 否 | 缩略图URL |
+| isEncrypted | boolean | 否 | 是否加密 |
+| capsuleStyle | string | 否 | 胶囊样式 |
+| status | string | 是 | 邮件状态,存入胶囊时固定为:DRAFT |
+
+#### 请求示例
+
+```json
+{
+ "title": "写给未来的自己",
+ "content": "亲爱的未来的我,当你读到这封信时,希望你已经实现了现在的梦想...",
+ "recipientType": "SELF",
+ "sendTime": "2025-12-31T23:59:59Z",
+ "triggerType": "TIME",
+ "attachments": [
+ {
+ "type": "IMAGE",
+ "url": "https://example.com/image.jpg",
+ "thumbnail": "https://example.com/thumb.jpg"
+ }
+ ],
+ "isEncrypted": false,
+ "capsuleStyle": "default",
+ "status": "DRAFT"
+}
+```
+
+#### 响应参数
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | number | 响应状态码,200表示成功 |
+| message | string | 响应消息 |
+| data | object | 响应数据 |
+| data.mailId | string | 邮件ID |
+| data.capsuleId | string | 胶囊ID |
+| data.status | string | 邮件状态:DRAFT、PENDING、DELIVERING、DELIVERED |
+| data.createdAt | string | 创建时间,ISO格式时间字符串 |
+
+#### 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "mailId": "mail_1234567890",
+ "capsuleId": "capsule_1234567890",
+ "status": "DRAFT",
+ "createdAt": "2023-07-20T10:30:00Z"
+ }
+}
+```
+
+### 获取胶囊列表
+
+**接口地址:** `GET /api/v1/mails`
+
+**接口描述:** 获取用户的胶囊邮件列表
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| type | string | 否 | 邮件类型:INBOX、SENT、DRAFT,获取胶囊时使用DRAFT |
+| status | string | 否 | 状态筛选:PENDING、DELIVERING、DELIVERED、DRAFT |
+| page | number | 否 | 页码,默认为1 |
+| size | number | 否 | 每页数量,默认为10 |
+
+#### 请求示例
+
+```
+GET /api/v1/mails?type=DRAFT&page=1&size=10
+```
+
+#### 响应参数
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | number | 响应状态码,200表示成功 |
+| message | string | 响应消息 |
+| data | object | 响应数据 |
+| data.list | array | 邮件列表 |
+| data.list[].mailId | string | 邮件ID |
+| data.list[].title | string | 邮件标题 |
+| data.list[].sender | object | 发件人信息 |
+| data.list[].recipient | object | 收件人信息 |
+| data.list[].sendTime | string | 发送时间 |
+| data.list[].deliveryTime | string | 送达时间 |
+| data.list[].status | string | 邮件状态 |
+| data.list[].hasAttachments | boolean | 是否有附件 |
+| data.list[].isEncrypted | boolean | 是否加密 |
+| data.list[].capsuleStyle | string | 胶囊样式 |
+| data.total | number | 总数量 |
+| data.page | number | 当前页码 |
+| data.size | number | 每页数量 |
+
+#### 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "list": [
+ {
+ "mailId": "mail_1234567890",
+ "title": "写给未来的自己",
+ "sender": {
+ "userId": "user_123",
+ "username": "张三",
+ "avatar": "https://example.com/avatar.jpg"
+ },
+ "recipient": {
+ "userId": "user_123",
+ "username": "张三",
+ "avatar": "https://example.com/avatar.jpg"
+ },
+ "sendTime": "2025-12-31T23:59:59Z",
+ "deliveryTime": null,
+ "status": "DRAFT",
+ "hasAttachments": true,
+ "isEncrypted": false,
+ "capsuleStyle": "default"
+ }
+ ],
+ "total": 1,
+ "page": 1,
+ "size": 10
+ }
+}
+```
+
+### 获取胶囊详情
+
+**接口地址:** `GET /api/v1/mails/{mailId}`
+
+**接口描述:** 获取指定胶囊邮件的详细信息
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| mailId | string | 是 | 邮件ID |
+
+#### 请求示例
+
+```
+GET /api/v1/mails/mail_1234567890
+```
+
+#### 响应参数
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | number | 响应状态码,200表示成功 |
+| message | string | 响应消息 |
+| data | object | 响应数据 |
+| data.mailId | string | 邮件ID |
+| data.title | string | 邮件标题 |
+| data.content | string | 邮件内容 |
+| data.sender | object | 发件人信息 |
+| data.recipient | object | 收件人信息 |
+| data.sendTime | string | 发送时间 |
+| data.createdAt | string | 创建时间 |
+| data.deliveryTime | string | 送达时间 |
+| data.status | string | 邮件状态 |
+| data.triggerType | string | 触发类型 |
+| data.triggerCondition | object | 触发条件 |
+| data.attachments | array | 附件列表 |
+| data.isEncrypted | boolean | 是否加密 |
+| data.capsuleStyle | string | 胶囊样式 |
+| data.canEdit | boolean | 是否可编辑(草稿状态为true) |
+| data.canRevoke | boolean | 是否可撤销(待投递状态为true) |
+
+#### 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "mailId": "mail_1234567890",
+ "title": "写给未来的自己",
+ "content": "亲爱的未来的我,当你读到这封信时,希望你已经实现了现在的梦想...",
+ "sender": {
+ "userId": "user_123",
+ "username": "张三",
+ "avatar": "https://example.com/avatar.jpg",
+ "email": "zhangsan@example.com"
+ },
+ "recipient": {
+ "userId": "user_123",
+ "username": "张三",
+ "avatar": "https://example.com/avatar.jpg",
+ "email": "zhangsan@example.com"
+ },
+ "sendTime": "2025-12-31T23:59:59Z",
+ "createdAt": "2023-07-20T10:30:00Z",
+ "deliveryTime": null,
+ "status": "DRAFT",
+ "triggerType": "TIME",
+ "triggerCondition": {},
+ "attachments": [
+ {
+ "id": "attach_123",
+ "type": "IMAGE",
+ "url": "https://example.com/image.jpg",
+ "thumbnail": "https://example.com/thumb.jpg",
+ "size": 1024000
+ }
+ ],
+ "isEncrypted": false,
+ "capsuleStyle": "default",
+ "canEdit": true,
+ "canRevoke": false
+ }
+}
+```
+
+### 更新胶囊邮件
+
+**接口地址:** `PUT /api/v1/mails/{mailId}`
+
+**接口描述:** 更新胶囊邮件内容(仅草稿状态可更新)
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| mailId | string | 是 | 邮件ID(路径参数) |
+| title | string | 否 | 邮件标题 |
+| content | string | 否 | 邮件内容 |
+| recipientType | string | 否 | 收件人类型:SELF、SPECIFIC、PUBLIC |
+| recipientEmail | string | 否 | 指定收件人邮箱(当recipientType为SPECIFIC时必填) |
+| sendTime | string | 否 | 发送时间,ISO格式时间字符串 |
+| triggerType | string | 否 | 触发类型:TIME、LOCATION、EVENT |
+| triggerCondition | object | 否 | 触发条件 |
+| attachments | array | 否 | 附件列表 |
+| isEncrypted | boolean | 否 | 是否加密 |
+| capsuleStyle | string | 否 | 胶囊样式 |
+
+#### 请求示例
+
+```json
+{
+ "title": "更新后的标题",
+ "content": "更新后的内容...",
+ "sendTime": "2026-12-31T23:59:59Z"
+}
+```
+
+#### 响应参数
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | number | 响应状态码,200表示成功 |
+| message | string | 响应消息 |
+| data | object | 响应数据 |
+| data.mailId | string | 邮件ID |
+| data.capsuleId | string | 胶囊ID |
+| data.status | string | 邮件状态 |
+| data.updatedAt | string | 更新时间,ISO格式时间字符串 |
+
+#### 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "mailId": "mail_1234567890",
+ "capsuleId": "capsule_1234567890",
+ "status": "DRAFT",
+ "updatedAt": "2023-07-21T14:30:00Z"
+ }
+}
+```
+
+### 删除胶囊邮件
+
+**接口地址:** `DELETE /api/v1/mails/{mailId}`
+
+**接口描述:** 删除指定的胶囊邮件
+
+#### 请求参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| mailId | string | 是 | 邮件ID(路径参数) |
+
+#### 请求示例
+
+```
+DELETE /api/v1/mails/mail_1234567890
+```
+
+#### 响应参数
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | number | 响应状态码,200表示成功 |
+| message | string | 响应消息 |
+| data | object | 响应数据 |
+| data.mailId | string | 已删除的邮件ID |
+
+#### 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "mailId": "mail_1234567890"
+ }
+}
+```
+
+## 错误码
+
+| 错误码 | 说明 |
+|--------|------|
+| 200 | 成功 |
+| 400 | 请求参数错误 |
+| 401 | 未授权,需要登录 |
+| 403 | 权限不足 |
+| 404 | 资源不存在 |
+| 422 | 验证失败 |
+| 500 | 服务器内部错误 |
+
+## 注意事项
+
+1. 存入胶囊的邮件状态为DRAFT,可以在任何时候编辑或发送
+2. 只有草稿状态的邮件可以编辑或删除
+3. 发送时间必须晚于当前时间
+4. 附件大小限制为10MB
+5. 免费用户每月最多可创建10个胶囊邮件
+6. 加密邮件需要额外验证才能查看内容
\ No newline at end of file
-
-
-