feat: 添加博客分类和分页功能

为博客页面添加分类筛选功能,支持按分类查看文章
实现分页功能,优化文章列表展示
调整页面布局和样式,增加交互效果
This commit is contained in:
2026-01-01 16:43:18 +08:00
parent a9d3854a55
commit e924fcca2b
8 changed files with 91 additions and 19 deletions
+1
View File
@@ -32,6 +32,7 @@
</n-modal>
<back-top />
</template>
<script setup lang="ts">
+9 -1
View File
@@ -1,10 +1,11 @@
import request from "@/util/request";
//getBlogList
export function getBlogList() {
export function getBlogList(params: any) {
return request({
url: "/art",
method: "get",
params
});
}
//getBlogDetail
@@ -13,4 +14,11 @@ export function getBlogDetail(id: string) {
url: `/art/${id}`,
method: "get",
});
}
//getCateList
export function getCateList() {
return request({
url: `/art/category`,
method: "get",
});
}
+4 -4
View File
@@ -1,7 +1,7 @@
<template>
<div ref="nav" class="main-nav hidden lg:flex justify-between bg-white">
<!-- 网站Logo -->
<div class="px-5 items-centerflex" slot="brand" @click="goHome">
<div class="px-5 items-centerflex cursor-pointer" slot="brand" @click="goHome">
<img :src="logo" alt="柚子的网站" class="h-9 align-middle" />
</div>
<!-- 主导航菜单 -->
@@ -9,7 +9,7 @@
<n-menu :icon-size="12" v-model:value="activeKey" class="" mode="horizontal" :options="menuOptions" />
</div>
<!-- 用户区域 -->
<div class="!text-[#ec66ab] flex items-center" @click="gotoHf">
<div class="!text-[#ec66ab] flex items-center cursor-pointer" @click="gotoHf">
<span class="flex items-center location-info truncate">
<n-icon class="mr-1">
<icon-loc class="w-[26px] h-[26px]"></icon-loc>
@@ -81,11 +81,11 @@
// 从@/icon/menu引入所有的svg文件
import logo from '@/assets/images/logo.png';
import artiSvg from '@/icon/menu/arti.svg';
import downSvg from '@/icon/menu/download.svg';
// import downSvg from '@/icon/menu/download.svg';
import homeSvg from '@/icon/menu/home.svg';
import linkSvg from '@/icon/menu/link.svg';
import picSvg from '@/icon/menu/pic.svg';
import settingSvg from '@/icon/menu/setting.svg';
// import settingSvg from '@/icon/menu/setting.svg';
import loginModal from '@/components/Login.vue'
import masked from '@/components/mask.vue'
import type { UploadFileInfo } from 'naive-ui'
+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767254162122" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5014" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M661.648 118H249.104c-39.088 0-70.304 31.752-70.304 70.304v647.392c0 39.096 31.752 70.304 70.304 70.304h525.8c38.552 0 70.296-31.752 70.296-70.304V301.56L661.648 118z m12.664 74.704l96.176 96.184H684.584c-5.408 0-10.272-4.864-10.272-10.272V192.704z m126.928 642.992a26.24 26.24 0 0 1-26.344 26.352H249.104a26.24 26.24 0 0 1-26.344-26.352V188.304a26.24 26.24 0 0 1 26.344-26.344v-0.464h381.328v117.192c0 29.824 24.416 54.16 54.16 54.16h116.656v502.848z" p-id="5015"></path><path d="M348.68 386.072h188.968a21.84 21.84 0 0 0 21.944-21.936 21.84 21.84 0 0 0-21.944-21.944H348.68a21.832 21.832 0 0 0-21.944 21.944 21.84 21.84 0 0 0 21.944 21.936zM674.848 644.8H348.68c-12.208 0-21.944 9.744-21.944 21.944s9.736 21.944 21.944 21.944h326.168c12.208 0 21.944-9.744 21.944-21.944S687.064 644.8 674.848 644.8zM348.68 495.856a21.824 21.824 0 0 0-21.944 21.944 21.832 21.832 0 0 0 21.944 21.936h326.168a21.832 21.832 0 0 0 21.944-21.936 21.832 21.832 0 0 0-21.944-21.944H348.68z" p-id="5016"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

-1
View File
@@ -25,7 +25,6 @@
</div>
</div>
</div>
<back-top />
</template>
<script setup lang="ts">
+74 -10
View File
@@ -1,14 +1,28 @@
<template>
<div class="article-page w-full flex" :style="boxStyle">
<div class="w-1/4 shadow">
<div class="w-1/4 shadow relative">
<div class="card w-2/3 absolute top-16 left-1/6">
<div @click="clickCate(i, idx)" v-for="(i, idx) in cateList" :key="i.cid"
:class="{ '!bg-primary text-white': idx == currentCateIdx }"
class="relative cate my-4 py-8 px-16 text-center text-xl font-bold rounded text-gray-600 bg-gray-200 cursor-pointer">
{{ i.name }}
<div class="absolute bottom-1 right-4 ext-sm text-deepp flex items-center gap-2">
<icon-arti class="w-4 h-4 !fill-deepp"></icon-arti>
{{ i.total || 0 }}篇文章
</div>
</div>
</div>
</div>
<div class="blog-list w-3/4 pl-16">
<div v-for="item in blogList" :key="item.aid" class="rounded-lg p-4 w-3/4 my-8 shadow cursor-pointer"
<div class="blog-list w-3/4 mt-10">
<div v-show="blogList.length == 0" class="ml-50 text-gray-500 mt-20">
什么都没有呢~ ~
</div>
<div v-for="item in blogList" :key="item.aid" class="rounded-lg p-4 w-[80%] ml-1/10 my-8 shadow cursor-pointer"
@click="$router.push(`/blog/${item.aid}`)">
<div class="flex justify-right gap-4 mb-2 text-gray-600">
<em class="flex items-center gap-1 text-sm">
<n-icon><icon-date/></n-icon>
<n-icon><icon-date /></n-icon>
{{ formatTime(item.updated_at, 'YYYY年MM月DD日') }}
</em>
<em class="flex items-center gap-1 text-sm">
@@ -19,7 +33,7 @@
<n-icon><icon-author /></n-icon>
{{ item.nickname }}</em>
</div>
<div class="text-xl text-bold text-primary mb-2">{{ item.title }}</div>
<div class="text-xl font-bold text-primary mb-2">{{ item.title }}</div>
<div class="text-sm text-gray-500 mb-4 line-clamp-3">{{ item.pro }}</div>
<div class="flex gap-4">
<div v-for="i in getTags(item.tags)" :key="i.tid" class="flex items-center gap-1 text-deepp cursor-pointer">
@@ -27,6 +41,9 @@
</div>
</div>
</div>
<div class="mt-20 w-[80%] ml-1/10 cursor-pointer flex justify-center">
<n-pagination v-model:page="page_num" :page-count="total" :page-slot="5" @update:page="getBlogList" />
</div>
</div>
</div>
</template>
@@ -42,9 +59,31 @@ definePage({
const blogList = ref<any[]>([])
const boxStyle = ref({})
const nav: any = $store.nav.useNavStore()
const cateList = ref<any[]>([])
const currentCateIdx = ref(-1)
const page_size = ref(5)
const page_num = ref(1)
const total = ref(0)
const category = ref('')
function clickCate(item: any, idx: number) {
if (currentCateIdx.value == idx) {
currentCateIdx.value = -1
category.value = ''
getBlogList()
total.value = cateList.value.reduce((acc: any, cur: any) => acc + cur.total, 0)
return
}
currentCateIdx.value = idx
category.value = item.cid
getBlogList()
total.value = item.total
console.log('>>> --> clickCate --> total.value:', total.value)
}
async function getBlogList() {
const res = await $http.blog.getBlogList()
console.log('>>> --> getBlogList --> res:', res.data)
const res = await $http.blog.getBlogList({
category:category.value,page_size:page_size.value,page_num:page_num.value
})
blogList.value = res.data
}
// 计算markdown的文字数量
@@ -56,22 +95,47 @@ function getSize(str: string) {
function getTags(str: any) {
str = str.replaceAll('', ',')
let tags = str.split(',').slice(0, 5)
tags.forEach((tag:any,idx:number) => {
tags.forEach((tag: any, idx: number) => {
tags[idx] = tag.trim().slice(0, 3)
});
return tags
}
async function getCateList() {
const res = await $http.blog.getCateList()
console.log('>>> --> getCateList --> res:', res.data)
cateList.value = res.data
total.value = cateList.value.reduce((acc: any, cur: any) => acc + cur.total, 0)
}
onMounted(() => {
boxStyle.value = {
height: `calc(100vh - ${nav.navH + 1}px)`,
}
getBlogList()
getCateList()
})
</script>
<style scoped>
/* 文章页样式 */
@keyframes fangda {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
.cate:hover {
animation: fangda 01s ease-in-out 1;
}
</style>