交互优化与数据动态化
前言
前五天的开发工作完成了项目的基础架构、首页开发、文章列表页面、文章详情页面、公共组件抽离和功能完善。第六天的开发工作将重点关注交互优化与数据动态化,包括顶部导航激活状态优化、热门分类动态数据完善、热门文章动态数据获取以及UI细节优化,进一步提升用户体验和代码质量。
编辑
一、顶部导航交互优化
1. 一级栏目点击效果优化
问题描述
之前的实现中,有二级导航的一级栏目使用了 cursor-not-allowed 样式来表示不可点击,但这个效果不够美观,影响用户体验。
解决方案
移除 cursor-not-allowed 样式,改用 @click.prevent 阻止默认跳转行为,同时保持鼠标划过效果:
<!-- 桌面导航 - 有二级导航的一级栏目 -->
<div v-if="item.children && item.children.length > 0" class="relative group">
<a href="#" @click.prevent
:class="['px-3 py-2 text-sm font-medium flex items-center gap-1 relative',
activeNavId === item.id ? 'text-bank-primary bg-slate-50 rounded-md' : 'text-slate-600 hover:text-bank-primary hover:bg-slate-50 rounded-md transition-colors']">
{{ item.name }}
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</a>
</div>
移动端菜单优化
移动端的一级栏目使用 @click.prevent 和 cursor-pointer 样式:
<!-- 移动菜单 - 有二级导航的一级栏目 -->
<div v-if="item.children && item.children.length > 0">
<div @click.prevent :class="['block px-3 py-2 text-base font-medium cursor-pointer',
activeNavId === item.id ? 'text-bank-primary bg-slate-50 rounded-md' : 'text-slate-700 hover:text-bank-primary']">
{{ item.name }}
</div>
<a v-for="child in item.children" :key="child.id" :href="`/list?categoryId=${child.id}`"
:class="['block px-6 py-2 text-base font-medium pl-4 border-l-2 border-slate-200',
activeNavId === child.id ? 'text-bank-primary bg-slate-50' : 'text-slate-600 hover:text-bank-primary']">
{{ child.name }}
</a>
</div>
2. 路由感知的激活状态
使用 Vue Router 的 useRoute 获取当前路由信息,根据路由路径和参数动态判断激活状态:
import { useRoute } from 'vue-router';
const route = useRoute();
const activeNavId = computed(() => {
// 首页路径
if (route.path === '/') {
return '2045904451398049793'; // 首页栏目ID
}
// 文章列表页面,从URL参数获取categoryId
if (route.path === '/list' && route.query.categoryId) {
const categoryId = route.query.categoryId;
// 如果是二级栏目,返回对应的一级栏目ID
if (childToParentMap.value[categoryId]) {
return childToParentMap.value[categoryId];
}
return categoryId;
}
return '';
});
3. 二级栏目到一级栏目的映射
构建映射表,实现选择二级栏目时高亮对应的一级栏目:
const childToParentMap = ref({});
navList.value.forEach(parent => {
if (parent.children) {
parent.children.forEach(child => {
childToParentMap.value[child.id] = parent.id;
});
}
});
二、热门分类动态数据
1. 功能需求
首页的热门分类板块需要动态显示每个一级栏目下的前四个二级栏目及其文章数量,点击"查看全部"跳转到所有栏目的文章列表。
2. 核心函数实现
// 根据栏目名称获取前四个二级栏目
function getTopFourSubcategories(categoryName) {
const category = fullNavList.value.find(item => item.name === categoryName && item.level === 1);
if (category) {
const childCategories = fullNavList.value.filter(item => item.pid === category.id);
return childCategories
.sort((a, b) => (categoryArticleCounts.value[b.id] || 0) - (categoryArticleCounts.value[a.id] || 0))
.slice(0, 4);
}
return [];
}
// 根据栏目ID获取文章数量
function getArticleCountById(categoryId) {
return categoryArticleCounts.value[categoryId] || 0;
}
// 根据栏目名称获取栏目ID
function getCategoryIdByName(categoryName) {
const category = fullNavList.value.find(item => item.name === categoryName && item.level === 1);
return category ? category.id : '';
}
3. 模板修改
<!-- Tech Category -->
<div class="bg-white rounded-xl bank-card-shadow overflow-hidden">
<div class="p-4 flex items-center gap-3 bg-blue-500">
<div class="w-10 h-10 rounded-lg bg-white/20 flex items-center justify-center">
<i data-lucide="database" class="w-5 h-5 text-white"></i>
</div>
<div>
<h3 class="font-semibold text-white">技术实战</h3>
<p class="text-xs text-white/80">{{ getCategoryCountByName('技术实战') }} 篇文章</p>
</div>
</div>
<div class="p-3">
<div class="grid grid-cols-2 gap-2">
<a v-for="subcategory in getTopFourSubcategories('技术实战')" :key="subcategory.id"
:href="`/list?categoryId=${subcategory.id}`"
class="flex items-center justify-between p-2.5 rounded-lg hover:bg-slate-50 transition-colors">
<span class="text-sm text-slate-600 hover:text-bank-primary">{{ subcategory.name }}</span>
<span class="text-xs text-slate-400">{{ getArticleCountById(subcategory.id) }}</span>
</a>
</div>
<a href="/list" class="flex items-center justify-center gap-1 mt-3 pt-3 border-t text-sm text-slate-500 hover:text-bank-primary transition-colors">
查看全部 <i data-lucide="chevron-right" class="w-4 h-4"></i>
</a>
</div>
</div>
三、底部组件复用
1. 组件抽离
将首页的底部 footer 代码抽离成独立的 Footer.vue 组件,便于统一管理和维护。
2. 组件使用
修改 Home.vue,移除重复的 footer 代码,改为引用 Footer 组件:
<template>
<div class="font-sans antialiased bg-slate-50 min-h-screen">
<!-- Header -->
<Header />
<!-- Main Content -->
<main>
<!-- 内容区域 -->
</main>
<!-- Footer -->
<Footer />
</div>
</template>
<script setup>
import Header from '../components/Header.vue';
import Footer from '../components/Footer.vue';
</script>
四、热门文章动态数据
1. API 函数添加
在 article.ts 中添加通过 iz_hot 字段筛选热门文章的 API 函数:
/**
* 获取热门文章(通过iz_hot字段筛选)
* @param limit 数量限制
* @returns Promise<any>
*/
export const getHotArticlesByHotFlag = (limit: number = 5) => {
return defHttp.get({
url: Api.GetArticleList,
params: {
izHot: '1',
pageNo: 1,
pageSize: limit,
column: 'publishTime',
order: 'desc'
}
});
};
2. ArticleList.vue 实现
// 热门文章数据
const hotArticles = ref([]);
// 获取热门文章(通过iz_hot字段筛选)
async function fetchHotArticles() {
try {
const response = await getHotArticlesByHotFlag(5);
if (response.success && response.result) {
hotArticles.value = response.result.records || [];
} else {
hotArticles.value = [];
}
} catch (error) {
console.error('获取热门文章失败:', error);
hotArticles.value = [];
}
}
// 组件挂载时执行
onMounted(() => {
fetchNavList();
fetchArticles();
fetchHotArticles();
initLucideIcons();
});
3. ArticleDetail.vue 实现
// 热门文章
const hotArticles = ref([]);
// 获取热门文章(通过iz_hot字段筛选)
async function fetchHotArticles() {
try {
const response = await getHotArticlesByHotFlag(5);
if (response.success && response.result) {
hotArticles.value = (response.result.records || []).map(item => ({
id: item.id,
title: item.title,
views: item.clickCount || 0
}));
} else {
hotArticles.value = [];
}
} catch (error) {
console.error('获取热门文章失败:', error);
hotArticles.value = [];
}
}
4. 模板修改
<!-- Hot Articles -->
<div class="bg-white rounded-xl border border-slate-200 p-4">
<h3 class="text-sm font-semibold text-slate-800 mb-4">热门文章</h3>
<ul class="space-y-4">
<li v-for="(article, index) in hotArticles" :key="article.id" class="flex items-start gap-3">
<span :class="['flex-shrink-0 w-6 h-6 rounded-full flex items-center justify-center text-xs font-medium',
index === 0 ? 'bg-bank-primary text-white' : 'bg-slate-200 text-slate-600']">{{ index + 1 }}</span>
<div class="flex-1 min-w-0">
<a :href="'/article/' + article.id" class="text-sm text-slate-700 hover:text-bank-primary transition-colors line-clamp-2">
{{ article.title }}
</a>
<div class="text-xs text-slate-400 mt-1">{{ article.views || 0 }} 阅读</div>
</div>
</li>
</ul>
</div>
五、UI 细节优化
1. 热门文章间距调整
增加文章标题之间的上下行间距,提升阅读体验:
<ul class="space-y-4">
<!-- 之前是 space-y-3 -->
...
</ul>
2. 热门文章卡片样式
使用 flex-1 min-w-0 确保长标题正确换行:
<div class="flex-1 min-w-0">
<a :href="'/article/' + article.id" class="text-sm text-slate-700 hover:text-bank-primary transition-colors line-clamp-2">
{{ article.title }}
</a>
<div class="text-xs text-slate-400 mt-1">{{ article.views || 0 }} 阅读</div>
</div>
六、项目结构
经过六天的开发,项目结构如下:
fintech-vue3/
├── src/
│ ├── api/
│ │ ├── article.ts # 文章相关API
│ │ └── nav.ts # 导航相关API
│ ├── components/
│ │ ├── Footer.vue # 底部公共组件
│ │ └── Header.vue # 顶部导航公共组件
│ ├── pages/
│ │ ├── Home.vue # 首页
│ │ ├── ArticleList.vue # 文章列表页
│ │ └── ArticleDetail.vue # 文章详情页
│ ├── router/
│ │ └── index.js # 路由配置
│ ├── utils/
│ │ └── http/
│ │ └── axios.ts # HTTP请求封装
│ ├── style.css # 全局样式
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── public/
│ └── index.html
├── package.json
└── vite.config.js
七、后续计划
1. 功能扩展
- 添加用户登录注册功能
- 实现文章评论功能
- 添加文章收藏功能
- 实现用户个人中心
- 添加文章搜索功能
2. 性能优化
- 实现图片懒加载
- 添加骨架屏提升首屏体验
- 优化列表滚动性能
- 实现路由级别的代码分割
3. SEO优化
- 添加 meta 标签
- 实现服务端渲染(SSR)
- 生成 sitemap.xml
- 优化页面加载速度
4. 移动端适配
- 优化移动端布局
- 添加手势操作
- 实现 PWA 功能
- 适配深色模式
5. 部署上线
- 配置 CI/CD 自动化部署
- 设置生产环境配置
- 配置 CDN 加速
- 实施监控和日志系统