From 45cc5a78086c4e4e4be43559ef658dc0ac80728f Mon Sep 17 00:00:00 2001 From: cc <94575594@qq.com> Date: Sat, 18 Apr 2026 13:55:29 +0800 Subject: [PATCH] =?UTF-8?q?refactor(homepage):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E4=B8=BB=E9=A1=B5=E5=B8=83=E5=B1=80=E4=B8=8E=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 精简功能卡片文案,提升描述简洁性 - 采用垂直和横向双重滚动分页,优化页面导航体验 - 增加键盘、滚轮和触摸滑动支持,实现多方式交互控制 - 重新设计鼠标光效跟踪,提升视觉反馈效果 - 引入统计数据展示模块,丰富首页内容 - 重构功能卡片点击处理逻辑,统一跳转方式 - 优化事件监听绑定,增强性能与代码可维护性 --- src/views/HomePage.vue | 1600 +++++++++++++++++++++++++++++++--------- 1 file changed, 1242 insertions(+), 358 deletions(-) diff --git a/src/views/HomePage.vue b/src/views/HomePage.vue index 569bbaf..1df1cc2 100644 --- a/src/views/HomePage.vue +++ b/src/views/HomePage.vue @@ -4,19 +4,21 @@ import { useRouter } from "vue-router"; import { LISTENING_AUDIO_URL } from "@/config/index.js"; const router = useRouter(); -const features = ref([ + +// 所有功能数据 +const allFeatures = ref([ { id: 1, title: "听力考试音频生成", - desc: "输入听力文本或上传Word,一键调用 AI 合成标准英语考试音频,支持多种题型场景,快速生成可用于备考的模拟听力材料。", + desc: "输入听力文本或上传Word,一键调用 AI 合成标准英语考试音频", class: "card-1", icon: "audio", url: LISTENING_AUDIO_URL, }, { id: 2, - title: "口语对话", - desc: "提供校园生活、兴趣爱好、英语考试、影视讨论、旅游英语五大场景,与 AI 外教实时对话,每条回复自动语音朗读,支持英语音色切换。", + title: "口语对话练习", + desc: "五大场景 AI 外教实时对话,每条回复自动语音朗读", class: "card-2", icon: "mic", route: "/speaking", @@ -24,7 +26,7 @@ const features = ref([ { id: 3, title: "作文原图批改", - desc: "上传手写英语作文的 JPG/PNG 图片,AI 自动识别内容并在原图上标注语法、拼写及表达问题,并排对比原图与批改结果,一目了然。", + desc: "上传手写英语作文图片,AI 自动标注语法、拼写及表达问题", class: "card-3", icon: "edit", route: "/essay-correction", @@ -32,7 +34,7 @@ const features = ref([ { id: 4, title: "英语试题AI分析", - desc: "支持文本粘贴或图片上传两种方式,AI 流式输出题干理解、考点识别、解题思路、正确答案、详细解析五个维度,全面拆解每道英语题目。", + desc: "五维度全面拆解每道英语题目,流式输出详细解析", class: "card-4", icon: "analytics", route: "/exam-analysis", @@ -40,7 +42,7 @@ const features = ref([ { id: 5, title: "单词听写", - desc: "自适应发音报词,智能追踪拼写薄弱点,让词汇记忆更高效。", + desc: "自适应发音报词,智能追踪拼写薄弱点", class: "card-5", icon: "spell", route: "/spell-practice", @@ -48,7 +50,7 @@ const features = ref([ { id: 6, title: "英语发音", - desc: "输入英语单词或句子,从 36 种音色(含美音、英音)中自由选择,一键合成标准发音并支持在线播放与下载保存。", + desc: "36 种音色自由选择,一键合成标准发音", class: "card-6", icon: "speaker", route: "/pronunciation", @@ -56,7 +58,7 @@ const features = ref([ { id: 7, title: "AI引导解题", - desc: "支持文本或图片上传题目,AI 以对话方式一步步引导思考,卡片式展示解题步骤,让解题过程清晰易懂,支持多学科通用解题。", + desc: "对话式引导思考,卡片式展示解题步骤", class: "card-7", icon: "puzzle", route: "/problem-solving", @@ -64,7 +66,7 @@ const features = ref([ { id: 8, title: "AI试题生成", - desc: "智能生成英语试题,支持选择题、填空题、翻译题和阅读理解,可自定义难度、数量和知识点,快速创建高质量题库。", + desc: "自定义难度、数量和知识点,快速创建题库", class: "card-8", icon: "document", route: "/question-generator", @@ -72,7 +74,7 @@ const features = ref([ { id: 9, title: "题目变式生成", - desc: "输入原始试题,AI 深度分析后生成同类变式题,保持题型不变,可调整难度,快速扩展题库,提升出题效率。", + desc: "AI 深度分析生成同类变式题,快速扩展题库", class: "card-9", icon: "variant", route: "/question-variant", @@ -80,24 +82,80 @@ const features = ref([ { id: 10, title: "音频转文字", - desc: "输入音频文件URL,AI自动识别并转换为文本,支持中英日多语种,适用于会议记录、采访整理等场景。", + desc: "支持中英日多语种,适用于会议记录、采访整理", class: "card-10", icon: "file-audio", route: "/audio-to-text", }, { - id: 12, + id: 11, title: "听读评测", - desc: "输入单词或句子列表,点击卡片听发音,按住按钮跟读录音,AI智能评测发音准确度,提供错误标注和改进建议。", + desc: "AI智能评测发音准确度,提供错误标注和改进建议", class: "card-12", icon: "mic", route: "/speaking-evaluation", }, ]); -// Hover effect for glassmorphism glare +// 统计数据 +const stats = ref([ + { value: "11+", label: "AI 学习工具" }, + { value: "100万+", label: "服务学习者" }, + { value: "36", label: "发音音色" }, + { value: "24/7", label: "AI 在线" }, +]); + +// 垂直翻页当前页码 +const currentPage = ref(0); +const totalPages = 2; + +// 横向滚动当前页码 +const currentHorizontalPage = ref(0); +const totalHorizontalPages = 2; // 每页6个卡片,11个功能需要2页 + +// 滚动到指定垂直页面 +const scrollToPage = (pageIndex) => { + if (pageIndex < 0 || pageIndex >= totalPages) return; + const container = document.querySelector(".snap-container"); + if (container) { + container.scrollTo({ + top: pageIndex * window.innerHeight, + behavior: "smooth", + }); + } +}; + +// 滚动到功能区域(第二页) +const scrollToFeatures = () => { + scrollToPage(1); +}; + +// 横向滚动到指定页 +const scrollToHorizontalPage = (pageIndex) => { + if (pageIndex < 0 || pageIndex >= totalHorizontalPages) return; + const container = document.querySelector(".horizontal-scroll-container"); + if (container) { + const pageWidth = container.clientWidth; + container.scrollTo({ + left: pageIndex * pageWidth, + behavior: "smooth", + }); + currentHorizontalPage.value = pageIndex; + } +}; + +// 处理功能卡片点击 +const handleCardClick = (feature) => { + if (feature.url) { + window.open(feature.url, "_blank"); + } else if (feature.route) { + router.push(feature.route); + } +}; + +// 鼠标跟随光效 const handleMouseMove = (e) => { - const cards = document.querySelectorAll(".homepage-feature-card"); + const cards = document.querySelectorAll(".feature-card"); for (const card of cards) { const rect = card.getBoundingClientRect(); const x = e.clientX - rect.left; @@ -107,359 +165,1023 @@ const handleMouseMove = (e) => { } }; -const handleCardClick = (feature) => { - if (feature.url) { - window.open(feature.url, "_blank"); - } else if (feature.route) { - router.push(feature.route); +// 监听垂直滚动更新当前页码 +const handleScroll = () => { + const container = document.querySelector(".snap-container"); + if (container) { + const scrollTop = container.scrollTop; + const pageHeight = window.innerHeight; + currentPage.value = Math.round(scrollTop / pageHeight); + } +}; + +// 监听横向滚动更新当前页码 +const handleHorizontalScroll = () => { + const container = document.querySelector(".horizontal-scroll-container"); + if (container) { + const scrollLeft = container.scrollLeft; + const pageWidth = container.clientWidth; + currentHorizontalPage.value = Math.round(scrollLeft / pageWidth); + } +}; + +// 键盘导航 +const handleKeyDown = (e) => { + if (currentPage.value === 1) { + // 在第二页时,左右键控制横向滚动 + if (e.key === "ArrowRight") { + e.preventDefault(); + scrollToHorizontalPage(currentHorizontalPage.value + 1); + return; + } else if (e.key === "ArrowLeft") { + e.preventDefault(); + scrollToHorizontalPage(currentHorizontalPage.value - 1); + return; + } + } + + if (e.key === "ArrowDown" || e.key === "PageDown") { + e.preventDefault(); + scrollToPage(currentPage.value + 1); + } else if (e.key === "ArrowUp" || e.key === "PageUp") { + e.preventDefault(); + scrollToPage(currentPage.value - 1); + } +}; + +// 垂直滚轮翻页控制 +let wheelTimeout = null; +const handleWheel = (e) => { + // 如果在第二页且横向滚动容器存在,检查是否在容器内 + if (currentPage.value === 1) { + const horizontalContainer = document.querySelector(".horizontal-scroll-container"); + if (horizontalContainer) { + // 让横向滚动容器自己处理滚轮事件 + return; + } + } + + e.preventDefault(); + if (wheelTimeout) return; + + wheelTimeout = setTimeout(() => { + wheelTimeout = null; + }, 800); + + if (e.deltaY > 0) { + scrollToPage(currentPage.value + 1); + } else if (e.deltaY < 0) { + scrollToPage(currentPage.value - 1); + } +}; + +// 横向滚轮处理 +const handleHorizontalWheel = (e) => { + e.preventDefault(); + if (e.deltaY > 0 || e.deltaX > 0) { + scrollToHorizontalPage(currentHorizontalPage.value + 1); + } else if (e.deltaY < 0 || e.deltaX < 0) { + scrollToHorizontalPage(currentHorizontalPage.value - 1); + } +}; + +// 触摸滑动支持 +let touchStartX = 0; +let touchStartY = 0; +let touchStartTime = 0; +let isTouching = false; + +const handleTouchStart = (e) => { + touchStartX = e.touches[0].clientX; + touchStartY = e.touches[0].clientY; + touchStartTime = Date.now(); + isTouching = true; +}; + +const handleTouchMove = (e) => { + if (!isTouching) return; + // 阻止默认行为防止页面滚动冲突 + const touchX = e.touches[0].clientX; + const touchY = e.touches[0].clientY; + const deltaX = Math.abs(touchX - touchStartX); + const deltaY = Math.abs(touchY - touchStartY); + + // 在功能区域(第2页)优先处理水平滑动 + if (currentPage.value === 1 && deltaX > deltaY && deltaX > 10) { + e.preventDefault(); + } +}; + +const handleTouchEnd = (e) => { + if (!isTouching) return; + isTouching = false; + + const touchEndX = e.changedTouches[0].clientX; + const touchEndY = e.changedTouches[0].clientY; + const touchEndTime = Date.now(); + + const deltaX = touchStartX - touchEndX; + const deltaY = touchStartY - touchEndY; + const deltaTime = touchEndTime - touchStartTime; + + // 快速滑动检测(250ms内) + const isQuickSwipe = deltaTime < 250; + const threshold = isQuickSwipe ? 25 : 60; + + // 判断是水平滑动还是垂直滑动 + if (Math.abs(deltaX) > Math.abs(deltaY)) { + // 水平滑动 - 控制横向滚动 + if (Math.abs(deltaX) > threshold) { + if (deltaX > 0) { + scrollToHorizontalPage(currentHorizontalPage.value + 1); + } else { + scrollToHorizontalPage(currentHorizontalPage.value - 1); + } + } + } else { + // 垂直滑动 - 控制页面翻页(仅在非横向滚动区域) + if (Math.abs(deltaY) > threshold && currentPage.value !== 1) { + if (deltaY > 0) { + scrollToPage(currentPage.value + 1); + } else { + scrollToPage(currentPage.value - 1); + } + } + } +}; + +// 横向滚动容器触摸处理 +const handleHorizontalTouchStart = (e) => { + touchStartX = e.touches[0].clientX; + touchStartTime = Date.now(); +}; + +const handleHorizontalTouchEnd = (e) => { + const touchEndX = e.changedTouches[0].clientX; + const deltaTime = Date.now() - touchStartTime; + const deltaX = touchStartX - touchEndX; + + const isQuickSwipe = deltaTime < 300; + const threshold = isQuickSwipe ? 30 : 60; + + if (Math.abs(deltaX) > threshold) { + if (deltaX > 0) { + scrollToHorizontalPage(currentHorizontalPage.value + 1); + } else { + scrollToHorizontalPage(currentHorizontalPage.value - 1); + } } }; onMounted(() => { - const container = document.querySelector(".homepage-container"); + const container = document.querySelector(".snap-container"); if (container) { - container.addEventListener("mousemove", handleMouseMove); + container.addEventListener("scroll", handleScroll); + container.addEventListener("wheel", handleWheel, { passive: false }); + container.addEventListener("touchstart", handleTouchStart, { passive: true }); + container.addEventListener("touchmove", handleTouchMove, { passive: false }); + container.addEventListener("touchend", handleTouchEnd, { passive: true }); + } + window.addEventListener("keydown", handleKeyDown); + + const page2 = document.querySelector(".page-2"); + if (page2) { + page2.addEventListener("mousemove", handleMouseMove); + } + + const horizontalContainer = document.querySelector(".horizontal-scroll-container"); + if (horizontalContainer) { + horizontalContainer.addEventListener("scroll", handleHorizontalScroll); + horizontalContainer.addEventListener("touchstart", handleHorizontalTouchStart, { passive: true }); + horizontalContainer.addEventListener("touchend", handleHorizontalTouchEnd, { passive: true }); } }); onUnmounted(() => { - const container = document.querySelector(".homepage-container"); + const container = document.querySelector(".snap-container"); if (container) { - container.removeEventListener("mousemove", handleMouseMove); + container.removeEventListener("scroll", handleScroll); + container.removeEventListener("wheel", handleWheel); + container.removeEventListener("touchstart", handleTouchStart); + container.removeEventListener("touchmove", handleTouchMove); + container.removeEventListener("touchend", handleTouchEnd); + } + window.removeEventListener("keydown", handleKeyDown); + + const page2 = document.querySelector(".page-2"); + if (page2) { + page2.removeEventListener("mousemove", handleMouseMove); + } + + const horizontalContainer = document.querySelector(".horizontal-scroll-container"); + if (horizontalContainer) { + horizontalContainer.removeEventListener("scroll", handleHorizontalScroll); + horizontalContainer.removeEventListener("touchstart", handleHorizontalTouchStart); + horizontalContainer.removeEventListener("touchend", handleHorizontalTouchEnd); } }); \ No newline at end of file