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 @@ + + + + + \ 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 @@