From 266310dea3e864949f0aa88b436073c24783a86d Mon Sep 17 00:00:00 2001 From: qingfeng1121 Date: Thu, 16 Oct 2025 16:11:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=96=87=E7=AB=A0?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=95=99=E8=A8=80=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加文章标题搜索功能,支持通过路由参数搜索 - 重构留言板组件,优化留言嵌套结构和交互 - 新增评论演示页面展示嵌套留言功能 - 调整主布局样式和导航菜单路由 - 修复留言板样式问题和数据字段不一致问题 --- src/components/LeftModule.vue | 3 +- src/layouts/MainLayout.vue | 163 +++++++++++++++++++- src/router/Router.js | 51 ++++++- src/services/articleService.js | 8 + src/styles/MainLayout.css | 5 +- src/views/aericle.vue | 2 +- src/views/commentDemo.vue | 262 +++++++++++++++++++++++++++++++++ src/views/home.vue | 20 ++- src/views/messageboard.vue | 20 +-- 9 files changed, 501 insertions(+), 33 deletions(-) create mode 100644 src/views/commentDemo.vue diff --git a/src/components/LeftModule.vue b/src/components/LeftModule.vue index 79336b2..c57dce4 100644 --- a/src/components/LeftModule.vue +++ b/src/components/LeftModule.vue @@ -145,7 +145,8 @@ onUnmounted(() => { background-color: rgba(0, 0, 0,0 ); /* 白色半透明背景 */ } .cont2 .el-menu-vertical-demo ul li:hover{ - background-color: rgba(255, 255, 255, 0.9); /* 白色半透明背景 */ + background-color: rgba(220, 53, 69, 0.9); /* 红色半透明背景 */ + color: white; } /* 分类列表样式 */ diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index 6d1c4a3..91fe6ff 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -9,7 +9,7 @@
- + 首页 @@ -28,7 +28,21 @@
- + +
+ +
+ +
+
@@ -75,7 +89,14 @@ const isScrollingleftmodlue = ref(false); const elrowtop = ref('transparent'); const classnonsenset = ref(false); const windowwidth = ref(true); -const activeIndex = ref('/:type'); +const activeIndex = ref('/home'); +const localhome= '/home'; + + +// 搜索相关状态 +const isSearchBoxOpen = ref(false); +const searchKeyword = ref(''); +let searchCloseTimer: number | undefined; // 打字机效果相关 const fullHeroText = '测试打字机效果'; @@ -107,11 +128,59 @@ const handleSelect = (key: string) => { router.push({ path: key }); }; +/** + * 切换搜索框显示/隐藏 + */ +const toggleSearchBox = () => { + isSearchBoxOpen.value = !isSearchBoxOpen.value; + + // 如果打开搜索框,清除之前的延时关闭定时器 + if (isSearchBoxOpen.value && searchCloseTimer) { + clearTimeout(searchCloseTimer); + } +}; + +/** + * 关闭搜索框 + */ +const closeSearchBox = () => { + isSearchBoxOpen.value = false; +}; + +/** + * 带延迟关闭搜索框(处理点击搜索按钮后的情况) + */ +const closeSearchBoxWithDelay = () => { + if (searchCloseTimer) { + clearTimeout(searchCloseTimer); + } + + searchCloseTimer = window.setTimeout(() => { + isSearchBoxOpen.value = false; + }, 300); +}; + +/** + * 执行搜索操作 + */ +const performSearch = () => { + if (searchKeyword.value.trim()) { + // 这里可以根据实际需求实现搜索逻辑 + console.log('搜索关键词:', searchKeyword.value); + router.push({ path: `/home/aericletitle/${searchKeyword.value}`}); + } + + // 搜索后保持搜索框打开状态 + if (searchCloseTimer) { + clearTimeout(searchCloseTimer); + } +}; + /** * 根据路由路径设置页面状态 */ const updatePageState = (url: string) => { - classhero.value = url !== '/:type'; + classhero.value = url !== localhome; classnonsenset.value = url === '/nonsense'; }; @@ -129,7 +198,7 @@ const handleResize = () => { windowwidth.value = window.innerWidth > 768; // 根据屏幕大小调整内容区可见性 - if (route.path === '/:type') { + if (route.path === localhome) { isconts.value = window.innerWidth <= 768 ? true : false; } }; @@ -152,7 +221,7 @@ const handleScroll = () => { } // 首页内容区滚动动画 - if (route.path === '/:type') { + if (route.path === localhome) { isconts.value = window.scrollY > 200; isScrollingleftmodlue.value = window.scrollY > 600; } @@ -169,13 +238,13 @@ watch(() => route.path, (newPath) => { window.scrollTo({ top: 0, behavior: 'smooth' }); // 首页内容区滚动动画仅大屏下生效 - if (newPath === '/:type') { + if (newPath === localhome) { isconts.value = window.innerWidth <= 768 ? true : false; // 首页时启动打字机 startTypewriter(); } else { isconts.value = true; - heroText.value = ''; + heroText.value =fullHeroText; if (heroTimer) clearInterval(heroTimer); } }, { immediate: true }); @@ -185,6 +254,8 @@ watch(() => route.path, (newPath) => { */ onMounted(() => { // 初始化窗口大小 + + handleResize(); // 添加事件监听器 @@ -203,4 +274,80 @@ onUnmounted(() => { \ No newline at end of file diff --git a/src/router/Router.js b/src/router/Router.js index 65200e5..bec299a 100644 --- a/src/router/Router.js +++ b/src/router/Router.js @@ -6,6 +6,7 @@ import NonsensePage from '../views/nonsense.vue' import MessageBoardPage from '../views/messageboard.vue' import AboutMePage from '../views/aboutme.vue' import ArticleContentPage from '../views/articlecontents.vue' +import CommentDemoPage from '../views/commentDemo.vue' /** * 路由配置数组 @@ -14,16 +15,56 @@ import ArticleContentPage from '../views/articlecontents.vue' const routes = [ { path: '/', - redirect: '/all' // 默认跳转到首页,显示所有文章 + redirect: '/home' // 默认跳转到首页,显示所有文章 }, { - path: '/:type', - name: 'home', - component: HomePage, + path: '/comment-demo', + name: 'commentDemo', + component: CommentDemoPage, meta: { - title: '首页' + title: '嵌套留言Demo' } }, + { + path: '/home', + name: 'home', + component: HomePage, + meta: { title: '首页' }, + children: [ + { + path: 'aericletype/:type', + name: 'homeByType' + }, + { + path: 'aericletitle/:title', + name: 'homeByTitle' + } + ] + }, + // { + // path: '/home', + // name: 'home', + // component: HomePage, + // meta: { + // title: '首页' + // } + // }, + // { + // path: '/home/aericletype/:type', + // name: 'homeByType', + // component: HomePage, + // meta: { + // title: '首页' + // } + // }, + // { + // path: '/home/aericletitle/:title', + // name: 'homeByTitle', + // component: HomePage, + // meta: { + // title: '首页' + // } + // }, { path: '/article-list', name: 'articleList', diff --git a/src/services/articleService.js b/src/services/articleService.js index b84d9da..19de966 100644 --- a/src/services/articleService.js +++ b/src/services/articleService.js @@ -23,6 +23,14 @@ class ArticleService { return apiService.get(`/articles/${id}`) } + /** + * 根据标题查询文章列表 + * @param {string} title - 文章标题 + * @returns {Promise} + */ + getArticlesByTitle(title) { + return apiService.get(`/articles/title/${title}`) + } /** diff --git a/src/styles/MainLayout.css b/src/styles/MainLayout.css index 33c3e9d..00f1f58 100644 --- a/src/styles/MainLayout.css +++ b/src/styles/MainLayout.css @@ -1,6 +1,6 @@ :root { /* 页面通用间距和圆角 */ - --main-padding: 100px 10%; + --main-padding: 8px 10%; /* 内容区内边距 */ --main-radius: 10px; /* 内容区圆角 */ @@ -132,7 +132,7 @@ p { font-family: 'Microsoft YaHei', 'Ma Shan Zheng', cursive; font-size: 1rem; font-weight: bold; - background: linear-gradient(94.75deg, rgb(60, 172, 247) 0%, rgb(131, 101, 253) 43.66%, rgb(255, 141, 112) 64.23%, rgb(247, 201, 102) 83.76%, rgb(172, 143, 100) 100%); + background: linear-gradient(94.75deg, rgb(60, 172, 247) 0%, rgb(131, 101, 253) 43.66%, rgb(255, 141, 112) 64.23%, rgb(247, 201, 102) 83.76%, rgb(172, 143, 100) 100%); -webkit-background-clip: text; background-clip: text; color: transparent; @@ -258,6 +258,7 @@ p { /* hero 收缩状态 */ .hero.newhero { height: var(--hero-height-small); + margin-top: 10%; } /* 打字机效果 */ diff --git a/src/views/aericle.vue b/src/views/aericle.vue index bd51bce..a719faa 100644 --- a/src/views/aericle.vue +++ b/src/views/aericle.vue @@ -90,7 +90,7 @@ const fetchCategories = async () => { */ const handleCategoryClick = (typeid: string) => { router.push({ - path: '/:type', + path: '/home/aericletype', query: { type: typeid } diff --git a/src/views/commentDemo.vue b/src/views/commentDemo.vue new file mode 100644 index 0000000..8780664 --- /dev/null +++ b/src/views/commentDemo.vue @@ -0,0 +1,262 @@ + + + + + \ No newline at end of file diff --git a/src/views/home.vue b/src/views/home.vue index b3e753a..d3c2f11 100644 --- a/src/views/home.vue +++ b/src/views/home.vue @@ -47,17 +47,31 @@ import { ElMessage } from 'element-plus' const router = useRouter() const route = useRoute() + + // 响应式状态 const datas = ref([]) const loading = ref(false) + /** * 获取文章列表 */ const fetchArticles = async () => { - try { + try { loading.value = true - const res = await articleService.getAllArticles() + let res = {} // 使用let而不是const + // 使用route对象获取参数,而不是检查pathname + if (route.params.type ) { + console.log('根据type获取文章列表成功') + res = await articleService.getAllArticlesByType(route.params.type) + } else if (route.params.title) { + res = await articleService.getAllArticlesByTitle(route.params.title) + console.log('根据title获取文章列表成功') + } else { + res = await articleService.getAllArticles() + console.log('获取所有文章列表成功') + } datas.value = res.data || [] console.log('获取文章列表成功:', datas.value) } catch (error) { @@ -65,10 +79,10 @@ const fetchArticles = async () => { ElMessage.error('获取文章列表失败,请稍后重试') } finally { loading.value = false - console.log('文章列表加载完成') } } + /** * 处理文章点击事件 * @param {Object} article - 文章对象 diff --git a/src/views/messageboard.vue b/src/views/messageboard.vue index e43ab37..d89cfd8 100644 --- a/src/views/messageboard.vue +++ b/src/views/messageboard.vue @@ -67,9 +67,9 @@ {{ articleGroup.articleTitle }}
-
+
-
头像 @@ -84,13 +84,13 @@
-
头像 { // 处理回复 const handleReply = (msg) => { replyingTo.value = { - id: msg.id, + id: msg.messageid, nickname: msg.nickname || '匿名用户', content: msg.content } - form.replyid = msg.id + form.parentid = msg.messageid form.content = `@${replyingTo.value.nickname} ` // 滚动到输入框 setTimeout(() => { @@ -282,11 +282,10 @@ onMounted(() => { }) const form = reactive({ - replyid: null, + parentid: null, content: '', nickname: '', email: '', - captcha: '' }) const onSubmit = async () => { @@ -358,7 +357,6 @@ const post_comment_reply_cancel = () => { .message-board { max-width: 1000px; margin: 0 auto; - padding: 20px; } .message-list { @@ -580,10 +578,6 @@ const post_comment_reply_cancel = () => { } } -.message-board { - padding: 24px 0; -} - .message-list { margin-bottom: 24px; background: #f8fafd;