From ccc20f9abf1f39c73dd2a721a86ab61116d87637 Mon Sep 17 00:00:00 2001
From: XCool <2350444842@qq.com>
Date: Sat, 18 Oct 2025 16:18:47 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.env.development | 3 +-
dist/report.html | 39 +
src/App.vue | 13 +
src/api/index.js | 116 +++
src/api/mock.js | 951 +++++++++++++++++++++
src/api/request.js | 2 +-
src/assets/styles/global.css | 1183 ++++++++++++++++++++++++++-
src/components/BottomTabbar.vue | 121 +++
src/components/GlobalBackground.vue | 93 +++
src/components/README.md | 122 +++
src/components/SearchComponent.vue | 1130 +++++++++++++++++++++++++
src/components/ThemeSwitcher.vue | 188 +++++
src/plugins/touch.js | 111 ++-
src/store/index.js | 2 +-
src/utils/theme.js | 152 ++++
src/views/ApiDemo.vue | 35 +-
src/views/CapsuleDetail.vue | 101 ---
src/views/Compose.vue | 185 +++--
src/views/Home.vue | 124 +--
src/views/Inbox.vue | 39 -
src/views/Login.vue | 44 +-
src/views/Profile.vue | 39 -
src/views/Register.vue | 40 +-
src/views/Sent.vue | 39 -
src/views/Timeline.vue | 39 -
发送至未来API文档.md | 307 +++++++
存入胶囊API文档.md | 372 +++++++++
27 files changed, 5017 insertions(+), 573 deletions(-)
create mode 100644 dist/report.html
create mode 100644 src/api/mock.js
create mode 100644 src/components/BottomTabbar.vue
create mode 100644 src/components/GlobalBackground.vue
create mode 100644 src/components/README.md
create mode 100644 src/components/SearchComponent.vue
create mode 100644 src/components/ThemeSwitcher.vue
create mode 100644 src/utils/theme.js
create mode 100644 发送至未来API文档.md
create mode 100644 存入胶囊API文档.md
diff --git a/.env.development b/.env.development
index bd0dbef..7164158 100644
--- a/.env.development
+++ b/.env.development
@@ -1,3 +1,4 @@
# 开发环境配置
VUE_APP_API_BASE_URL=/api/v1
-VUE_APP_TITLE=ChronoMail - 未来邮箱
\ No newline at end of file
+VUE_APP_TITLE=ChronoMail - 未来邮箱
+VUE_APP_USE_MOCK_API=false
\ No newline at end of file
diff --git a/dist/report.html b/dist/report.html
new file mode 100644
index 0000000..4fd9dbb
--- /dev/null
+++ b/dist/report.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+ future-mail [18 Oct 2025 at 10:15]
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/App.vue b/src/App.vue
index 8d04795..5da60f6 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,13 +1,26 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/components/GlobalBackground.vue b/src/components/GlobalBackground.vue
new file mode 100644
index 0000000..26b9c9f
--- /dev/null
+++ b/src/components/GlobalBackground.vue
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/README.md b/src/components/README.md
new file mode 100644
index 0000000..aa6a45a
--- /dev/null
+++ b/src/components/README.md
@@ -0,0 +1,122 @@
+# 搜索组件 (SearchComponent)
+
+## 概述
+
+SearchComponent 是一个基于 Vant UI 组件库开发的搜索组件,已从 Home.vue 页面中提取出来,实现了搜索功能的模块化和复用性。
+
+## 功能特性
+
+1. **全屏搜索界面** - 提供沉浸式的搜索体验
+2. **搜索建议** - 显示热门搜索标签,快速选择搜索内容
+3. **搜索历史** - 自动保存和显示搜索历史记录
+4. **搜索结果** - 展示搜索结果,支持不同类型的内容
+5. **搜索筛选** - 支持按内容类型筛选搜索结果
+6. **响应式设计** - 适配移动端和桌面端
+
+## 使用方法
+
+### 基本用法
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+### 组件属性
+
+| 属性 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| modelValue | Boolean | false | 控制搜索组件的显示/隐藏 |
+
+### 组件事件
+
+| 事件名 | 说明 | 回调参数 |
+|--------|------|----------|
+| update:modelValue | 显示状态变化时触发 | (value: boolean) |
+| search | 执行搜索时触发 | (query: string, filters: string[]) |
+| result-click | 点击搜索结果时触发 | (result: SearchResult) |
+
+### 搜索结果类型
+
+```typescript
+interface SearchResult {
+ id: string
+ type: 'capsule' | 'sent' | 'received' | 'draft'
+ title: string
+ description: string
+ date: string
+ capsuleId?: string
+ mailId?: string
+}
+```
+
+## 自定义配置
+
+### 修改搜索建议
+
+在 SearchComponent.vue 中修改 `searchSuggestions` 数组:
+
+```javascript
+const searchSuggestions = ref(['新年愿望', '生日祝福', '未来寄语', '毕业纪念', '爱情宣言'])
+```
+
+### 修改搜索历史存储键名
+
+在 SearchComponent.vue 中修改 `HISTORY_STORAGE_KEY` 常量:
+
+```javascript
+const HISTORY_STORAGE_KEY = 'myApp_searchHistory'
+```
+
+### 修改模拟搜索结果
+
+在 `generateMockSearchResults` 函数中自定义搜索结果的生成逻辑。
+
+## 样式定制
+
+组件使用了 Vant 的主题变量,可以通过修改 CSS 变量来自定义样式:
+
+```css
+.search-popup-container {
+ --primary-color: #1989fa;
+ --background-color: #1a1a2e;
+ --text-color: #ffffff;
+}
+```
+
+## 注意事项
+
+1. 组件依赖 Vant UI 组件库,使用前请确保已正确安装和配置
+2. 搜索历史使用 localStorage 存储,注意浏览器兼容性
+3. 当前搜索结果为模拟数据,实际使用时需要替换为真实的 API 调用
+4. 组件使用了 Vue 3 的 Composition API,需要 Vue 3 环境
+
+## 更新日志
+
+### v1.0.0
+- 从 Home.vue 中提取搜索功能为独立组件
+- 使用 Vant UI 组件重写样式
+- 实现搜索建议、历史记录和结果展示功能
+- 添加搜索筛选器功能
\ No newline at end of file
diff --git a/src/components/SearchComponent.vue b/src/components/SearchComponent.vue
new file mode 100644
index 0000000..27c3cf0
--- /dev/null
+++ b/src/components/SearchComponent.vue
@@ -0,0 +1,1130 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/ThemeSwitcher.vue b/src/components/ThemeSwitcher.vue
new file mode 100644
index 0000000..eb44bdd
--- /dev/null
+++ b/src/components/ThemeSwitcher.vue
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+
{{ theme.name }}
+
{{ theme.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/plugins/touch.js b/src/plugins/touch.js
index fd9f80c..3309542 100644
--- a/src/plugins/touch.js
+++ b/src/plugins/touch.js
@@ -1,4 +1,113 @@
-import { touchDirectives } from './touch';
+/**
+ * 移动端触摸交互指令
+ */
+
+// 触摸反馈指令
+const touchFeedback = {
+ mounted(el, binding) {
+ const { value = {} } = binding;
+ const { scale = 0.95, duration = 200 } = value;
+
+ el.addEventListener('touchstart', function() {
+ el.style.transition = `transform ${duration}ms`;
+ el.style.transform = `scale(${scale})`;
+ }, { passive: true });
+
+ el.addEventListener('touchend', function() {
+ el.style.transform = 'scale(1)';
+ }, { passive: true });
+
+ el.addEventListener('touchcancel', function() {
+ el.style.transform = 'scale(1)';
+ }, { passive: true });
+ }
+};
+
+// 滑动指令
+const swipe = {
+ mounted(el, binding) {
+ const { value = {} } = binding;
+ const {
+ left = null,
+ right = null,
+ up = null,
+ down = null,
+ threshold = 50
+ } = value;
+
+ let startX = 0;
+ let startY = 0;
+
+ el.addEventListener('touchstart', function(e) {
+ startX = e.touches[0].clientX;
+ startY = e.touches[0].clientY;
+ }, { passive: true });
+
+ el.addEventListener('touchend', function(e) {
+ if (!e.changedTouches.length) return;
+
+ const endX = e.changedTouches[0].clientX;
+ const endY = e.changedTouches[0].clientY;
+ const deltaX = endX - startX;
+ const deltaY = endY - startY;
+
+ // 检测水平滑动
+ if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > threshold) {
+ if (deltaX > 0 && right) {
+ right();
+ } else if (deltaX < 0 && left) {
+ left();
+ }
+ }
+
+ // 检测垂直滑动
+ else if (Math.abs(deltaY) > threshold) {
+ if (deltaY > 0 && down) {
+ down();
+ } else if (deltaY < 0 && up) {
+ up();
+ }
+ }
+ }, { passive: true });
+ }
+};
+
+// 长按指令
+const longPress = {
+ mounted(el, binding) {
+ const { value = {} } = binding;
+ const { duration = 500, callback = null } = value;
+
+ let pressTimer = null;
+
+ el.addEventListener('touchstart', function() {
+ pressTimer = setTimeout(() => {
+ if (callback) callback();
+ }, duration);
+ }, { passive: true });
+
+ el.addEventListener('touchend', function() {
+ if (pressTimer) {
+ clearTimeout(pressTimer);
+ pressTimer = null;
+ }
+ }, { passive: true });
+
+ el.addEventListener('touchmove', function() {
+ if (pressTimer) {
+ clearTimeout(pressTimer);
+ pressTimer = null;
+ }
+ }, { passive: true });
+ }
+};
+
+// 导出所有触摸指令
+const touchDirectives = {
+ 'touch-feedback': touchFeedback,
+ 'swipe': swipe,
+ 'long-press': longPress
+};
/**
* 移动端触摸交互Vue插件
diff --git a/src/store/index.js b/src/store/index.js
index 36613a7..08837fb 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -179,7 +179,7 @@ export const userActions = {
try {
const res = await api.user.getSubscription()
// 根据后端实际返回的数据结构提取数据
- userState.subscription = res.data.data
+ userState.subscription = res.data
return res
} catch (error) {
console.error('获取订阅信息失败:', error)
diff --git a/src/utils/theme.js b/src/utils/theme.js
new file mode 100644
index 0000000..2392b0f
--- /dev/null
+++ b/src/utils/theme.js
@@ -0,0 +1,152 @@
+import { ref, computed } from 'vue'
+
+// 主题配置
+const themes = {
+ starryNight: {
+ name: '星空之夜',
+ primary: '#0F1C2E',
+ secondary: '#1D3B5A',
+ accent: '#00D4FF',
+ gradient: 'linear-gradient(135deg, #1D3B5A, #0F1C2E)',
+ textPrimary: '#ffffff',
+ textSecondary: '#a0b3d0',
+ glassBg: 'rgba(255, 255, 255, 0.1)',
+ glassBorder: 'rgba(255, 255, 255, 0.2)',
+ buttonGradient: 'linear-gradient(135deg, #00D4FF, #0099CC)',
+ buttonHoverGradient: 'linear-gradient(135deg, #0099CC, #006699)',
+ starColor: '#ffffff',
+ starAnimation: 'twinkle'
+ },
+ oceanDeep: {
+ name: '深海秘境',
+ primary: '#0A1929',
+ secondary: '#134074',
+ accent: '#00BCD4',
+ gradient: 'linear-gradient(135deg, #134074, #0A1929)',
+ textPrimary: '#ffffff',
+ textSecondary: '#B0E0E6',
+ glassBg: 'rgba(255, 255, 255, 0.08)',
+ glassBorder: 'rgba(0, 188, 212, 0.3)',
+ buttonGradient: 'linear-gradient(135deg, #00BCD4, #0097A7)',
+ buttonHoverGradient: 'linear-gradient(135deg, #0097A7, #00838F)',
+ starColor: '#00E5FF',
+ starAnimation: 'float'
+ },
+ sunsetGlow: {
+ name: '晚霞余晖',
+ primary: '#2D1B69',
+ secondary: '#624CAB',
+ accent: '#FF6B6B',
+ gradient: 'linear-gradient(135deg, #624CAB, #2D1B69)',
+ textPrimary: '#ffffff',
+ textSecondary: '#F8B500',
+ glassBg: 'rgba(255, 107, 107, 0.1)',
+ glassBorder: 'rgba(248, 181, 0, 0.3)',
+ buttonGradient: 'linear-gradient(135deg, #FF6B6B, #FF8E53)',
+ buttonHoverGradient: 'linear-gradient(135deg, #FF8E53, #FF6B6B)',
+ starColor: '#FFE66D',
+ starAnimation: 'pulse'
+ },
+ aurora: {
+ name: '极光幻彩',
+ primary: '#0F2027',
+ secondary: '#203A43',
+ accent: '#2C5364',
+ gradient: 'linear-gradient(135deg, #203A43, #0F2027, #2C5364)',
+ textPrimary: '#ffffff',
+ textSecondary: '#D4E6F1',
+ glassBg: 'rgba(44, 83, 100, 0.1)',
+ glassBorder: 'rgba(212, 230, 241, 0.3)',
+ buttonGradient: 'linear-gradient(135deg, #2C5364, #203A43)',
+ buttonHoverGradient: 'linear-gradient(135deg, #203A43, #0F2027)',
+ starColor: '#00FF88',
+ starAnimation: 'shimmer'
+ },
+ galaxy: {
+ name: '银河漫游',
+ primary: '#1A0033',
+ secondary: '#330867',
+ accent: '#C77DFF',
+ gradient: 'linear-gradient(135deg, #330867, #1A0033)',
+ textPrimary: '#ffffff',
+ textSecondary: '#E0AAFF',
+ glassBg: 'rgba(199, 125, 255, 0.1)',
+ glassBorder: 'rgba(224, 170, 255, 0.3)',
+ buttonGradient: 'linear-gradient(135deg, #C77DFF, #9D4EDD)',
+ buttonHoverGradient: 'linear-gradient(135deg, #9D4EDD, #7B2CBF)',
+ starColor: '#E0AAFF',
+ starAnimation: 'twinkle'
+ }
+}
+
+// 当前主题
+const currentTheme = ref(localStorage.getItem('selectedTheme') || 'starryNight')
+
+// 获取当前主题名称
+const getCurrentTheme = () => {
+ return localStorage.getItem('selectedTheme') || 'starryNight'
+}
+
+// 切换主题
+const setTheme = (themeName) => {
+ if (themes[themeName]) {
+ currentTheme.value = themeName
+ localStorage.setItem('selectedTheme', themeName)
+ applyThemeToDocument(themes[themeName])
+ return true
+ }
+ return false
+}
+
+// 应用主题到文档
+const applyThemeToDocument = (theme) => {
+ const root = document.documentElement
+
+ // 设置CSS变量
+ root.style.setProperty('--primary-color', theme.primary)
+ root.style.setProperty('--secondary-color', theme.secondary)
+ root.style.setProperty('--accent-color', theme.accent)
+ root.style.setProperty('--gradient-color', theme.gradient)
+ root.style.setProperty('--text-primary', theme.textPrimary)
+ root.style.setProperty('--text-secondary', theme.textSecondary)
+ root.style.setProperty('--glass-bg', theme.glassBg)
+ root.style.setProperty('--glass-border', theme.glassBorder)
+ root.style.setProperty('--button-gradient', theme.buttonGradient)
+ root.style.setProperty('--button-hover-gradient', theme.buttonHoverGradient)
+ root.style.setProperty('--star-color', theme.starColor)
+}
+
+// 应用主题
+const applyTheme = (themeName) => {
+ const theme = themes[themeName] || themes.starryNight
+ applyThemeToDocument(theme)
+
+ // 设置主题类名
+ document.body.className = `theme-${themeName}`
+}
+
+// 初始化主题
+const initTheme = () => {
+ const themeName = getCurrentTheme()
+ applyTheme(themeName)
+}
+
+// 获取所有可用主题
+const getAvailableThemes = () => {
+ return Object.keys(themes).map(key => ({
+ id: key,
+ name: themes[key].name,
+ description: themes[key].description || `美丽的${themes[key].name}主题`,
+ preview: themes[key].gradient
+ }))
+}
+
+export {
+ themes,
+ currentTheme,
+ getCurrentTheme,
+ setTheme,
+ applyTheme,
+ initTheme,
+ getAvailableThemes
+}
\ No newline at end of file
diff --git a/src/views/ApiDemo.vue b/src/views/ApiDemo.vue
index 52b5593..41929a8 100644
--- a/src/views/ApiDemo.vue
+++ b/src/views/ApiDemo.vue
@@ -1,9 +1,5 @@
-
-
{
- 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 size = Math.random() * 3 + 1
- star.style.width = `${size}px`
- star.style.height = `${size}px`
-
- // 随机位置
- star.style.left = `${Math.random() * 100}%`
- star.style.top = `${Math.random() * 100}%`
-
- // 随机动画延迟
- star.style.animationDelay = `${Math.random() * 4}s`
-
- starsContainer.appendChild(star)
- }
- }
-
// 处理登录
const handleLogin = async () => {
if (!loginForm.email || !loginForm.password) {
@@ -382,11 +350,10 @@ export default {
}
onMounted(() => {
- initStars()
+ // 组件挂载后的初始化代码
})
return {
- stars,
loginForm,
loginLoading,
mails,
diff --git a/src/views/CapsuleDetail.vue b/src/views/CapsuleDetail.vue
index fda698b..337f4c0 100644
--- a/src/views/CapsuleDetail.vue
+++ b/src/views/CapsuleDetail.vue
@@ -1,11 +1,5 @@