上篇文章写完了后端接口的开发,本篇就开始写前端的开发了。
效果:www.zngg.net/nav 开源地址:github.com/ZN-GG/ZNGG-...
上篇文章传送门:juejin.cn/post/726030...
整理上回思路:
- 所有导航信息使用一个json
这就说明一个问题:所有分类相关的切换均由前端完成,一次请求后不再与后端交互。
页面制作
抛开逻辑不谈,静态的html展示肯定是要先做到位的,现在我直接拿成品图来展示:
根据这个图片来写前端,实际上并不难。看下这个页面结构:
- 顶部Toolbar
- 一个标题
- 分类条
- 网址条目
顶部Toolbar
其中顶部Toolbar和标题较为简单,我目前已经可以在已有的框架中开发了,我封装好了顶部Toolbar,并且外层默认layout已经带有Toolbar了,所以这步就省略了。 这是默认layout:
html
<template>
<div>
<Toolbar />
<NuxtLoadingIndicator />
<NuxtPage class="mt-20" />
</div>
</template>
其中已经有Toolbar了。
一个标题
这个使用tailwindcss的container
和样式mx-auto
,即可把文字放在中间容器的左侧,随后略微调整一下字体即可:
html
<section class="bg-gray-100">
<div class="container px-4 mx-auto">
<div class="md:flex md:-mx-4 md:items-center py-8">
<div class="md:w-1/2 px-4">
<p class="text-2xl text-black">优质网址导航</p>
</div>
</div>
</div>
</section>
轻松搞定!
分类条
页面制作
分类条的设计我使用flex
布局,方便垂直居中对齐。第一步肯定还是需要创建一个container
样式的div,这样可以把内容聚集到中间容器中来;第二步就是在里面写一个父布局(flex
),并设置overflow-auto
,方便右侧超出后可以出现滚动条。第三步就是在这个flex布局里填充分类按钮,并稍微来点样式: tailwind.css
:
css
.btn-2 {
@apply bg-gray-100 custom-font-12 leading-5 py-1 px-4 rounded-md whitespace-nowrap cursor-pointer hover:bg-blue-600 hover:text-white mx-1;
}
页面:
html
<div class="bg-white">
<div class="container mx-auto px-4">
<div class="flex py-3 space-x-4 flex-none overflow-auto">
<div class="bg-blue-600 text-white btn-2">分类</div>
<div class="btn-2">分类</div>
<div class="btn-2">分类</div>
<div class="btn-2">分类</div>
<div class="btn-2">分类</div>
</div>
</div>
</div>
样式写好了就要开始写逻辑了,要用网络数据代替这些写死的数据。
逻辑
分类逻辑很简单:获取网络数据data ,使用v-for
遍历这个data 即可。稍微需要思考一下的就是这个已选择的分类,我决定默认选择第一个分类为已选择分类,这里需要添加一个变量categoryId
(不够严谨),这个变量默认为1
,点击分类时修改categoryId
的值即可。
首先使用Nuxt3获取网络数据:
typescript
import { api } from "~/api/api";
let categoryId = ref(0)
const { data: navLinks, pending, refresh, error } = await useAsyncData("web_NavLinks", () => api.web.getNavInfo());
if (navLinks.value!.success) {
console.log(navLinks.value);
}
async function selectCategoryId(id: number) {
categoryId.value = id
}
这里我封装了一下网络请求,具体封装代码可以看:github.com/ZN-GG/ZNGG-...
上述代码中定义了一个navLinks
常量存储json数据,并且将数据打印出来。这里定义了一个变量categoryId
和一个函数selectCategoryId
,下面会用到
随后将分类相关的html代码修改成动态的值:
html
<div class="bg-white">
<div class="container mx-auto px-4">
<div
class="flex py-3 space-x-4 flex-none overflow-auto">
<div v-for="(item, index) in navLinks?.data" :key="index" @click="selectCategoryId(index)"
class="btn-2" :class="categoryId == index ? 'bg-blue-600 text-white' : ''">
{{ item.name }}
</div>
</div>
</div>
</div>
上面的代码中使用v-for
循环遍历我们的内容navLinks
,并且将index
设置为索引,当categoryId
和index
相等时,给按钮增加一个选中的样式(背景变蓝色,字变成白色),并且使用@click
让分类被点击时调用selectCategoryId
函数修改categoryId
的值。
写到这里,分类相关功能就已经全部完成了,接下来只需要根据categoryId
来展示网址即可。
网址条目
根据已选择分类展示网址,开始之前思考一个问题,就是如何才能根据选择的分类展示这个分类下的网址信息呢?我的答案是"categId",这里的categoryId是真正的categoryId,就是那个雪花算法生成的id,并不是上面说到的index索引,那么如果这样做的话,就会需要两层遍历(因为json只能遍历),首先在完成遍历出正确的categoryId==categoryId,之后在内部遍历itemList
,这样会不会麻烦一点呢?或者有没有更简单的办法呢?这就是我想到的第二个办法:index。
json想要直接拿到某一条数据,那么只有一个办法,就是知道这个数据的位置(position),因此我上面代码里的categoryId
其实代表的是position,分类的位置。这样我就可以直接通过json[xxx].itemList的方式拿到网址数据,具体请看代码:
ini
<div class="container px-4 mx-auto">
<div class="py-5">
<ul class="w-full flex flex-wrap">
<li v-for="item in navLinks?.data[categoryId].itemList" class="w-full md:w-4/12">
<div class="transition duration-300 ease-in-out p-2">
<a :href="item.url" target="_blank">
<div class="bg-white rounded px-3 py-4 nav-item-anim">
<div class="flex">
<div class="w-2/12 flex items-center">
<img class="w-8 h-8 mx-auto" :src="item.img" alt="" srcset="">
</div>
<div class="px-2">
<div class="flex items-start">
<div class="grow">
<p class="text-gray-800 mb-1">{{ item.title }}</p>
<p class="text-gray-400 text-xs">
{{ item.description }}
</p>
</div>
</div>
</div>
</div>
</div>
</a>
</div>
</li>
</ul>
</div>
</div>
归纳总结
这篇文章的categoryId
意思表达的不算好,如果不理解的话只能多看看代码了。
完整代码地址:github.com/ZN-GG/ZNGG-...
到此,导航就已经制作完毕了,我将整个制作过程完完整整的记录了下来,也许几年以后再来看这些代码,就会觉得是一堆垃圾,但好在现在我觉得这代码可优化的地方并不算多。整个导航我大概花了一个下午的时间,这里很感谢我的女朋友可以在难得周末抽出一个下午的时间让我好好享受这写代码的过程,看一看时间,再不午休下午可就要犯困了~