feat: 添加博客分类和分页功能
为博客页面添加分类筛选功能,支持按分类查看文章 实现分页功能,优化文章列表展示 调整页面布局和样式,增加交互效果
This commit is contained in:
1
components.d.ts
vendored
1
components.d.ts
vendored
@ -37,6 +37,7 @@ declare module 'vue' {
|
|||||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
NModalProvider: typeof import('naive-ui')['NModalProvider']
|
NModalProvider: typeof import('naive-ui')['NModalProvider']
|
||||||
|
NPagination: typeof import('naive-ui')['NPagination']
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||||
NSelect: typeof import('naive-ui')['NSelect']
|
NSelect: typeof import('naive-ui')['NSelect']
|
||||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/favicon.ico">
|
<link rel="icon" href="/favicon.ico">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="这是柚子的网站,做一些分享类的功能"/>
|
<meta name="description" content="这是柚子的网站,做一些分享类的功能"/>
|
||||||
<title>柚子の网站</title>
|
<title>柚子の网站</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
! function (e, t, a) {
|
! function (e, t, a) {
|
||||||
|
|
||||||
|
|
||||||
function r() {
|
function r() {
|
||||||
for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[
|
for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[
|
||||||
e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x +
|
e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x +
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
|
|
||||||
|
<back-top />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import request from "@/util/request";
|
import request from "@/util/request";
|
||||||
|
|
||||||
//getBlogList
|
//getBlogList
|
||||||
export function getBlogList() {
|
export function getBlogList(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/art",
|
url: "/art",
|
||||||
method: "get",
|
method: "get",
|
||||||
|
params
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//getBlogDetail
|
//getBlogDetail
|
||||||
@ -14,3 +15,10 @@ export function getBlogDetail(id: string) {
|
|||||||
method: "get",
|
method: "get",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
//getCateList
|
||||||
|
export function getCateList() {
|
||||||
|
return request({
|
||||||
|
url: `/art/category`,
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="nav" class="main-nav hidden lg:flex justify-between bg-white">
|
<div ref="nav" class="main-nav hidden lg:flex justify-between bg-white">
|
||||||
<!-- 网站Logo -->
|
<!-- 网站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" />
|
<img :src="logo" alt="柚子的网站" class="h-9 align-middle" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 主导航菜单 -->
|
<!-- 主导航菜单 -->
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<n-menu :icon-size="12" v-model:value="activeKey" class="" mode="horizontal" :options="menuOptions" />
|
<n-menu :icon-size="12" v-model:value="activeKey" class="" mode="horizontal" :options="menuOptions" />
|
||||||
</div>
|
</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">
|
<span class="flex items-center location-info truncate">
|
||||||
<n-icon class="mr-1">
|
<n-icon class="mr-1">
|
||||||
<icon-loc class="w-[26px] h-[26px]"></icon-loc>
|
<icon-loc class="w-[26px] h-[26px]"></icon-loc>
|
||||||
@ -81,11 +81,11 @@
|
|||||||
// 从@/icon/menu引入所有的svg文件
|
// 从@/icon/menu引入所有的svg文件
|
||||||
import logo from '@/assets/images/logo.png';
|
import logo from '@/assets/images/logo.png';
|
||||||
import artiSvg from '@/icon/menu/arti.svg';
|
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 homeSvg from '@/icon/menu/home.svg';
|
||||||
import linkSvg from '@/icon/menu/link.svg';
|
import linkSvg from '@/icon/menu/link.svg';
|
||||||
import picSvg from '@/icon/menu/pic.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 loginModal from '@/components/Login.vue'
|
||||||
import masked from '@/components/mask.vue'
|
import masked from '@/components/mask.vue'
|
||||||
import type { UploadFileInfo } from 'naive-ui'
|
import type { UploadFileInfo } from 'naive-ui'
|
||||||
|
|||||||
1
src/icon/arti.svg
Normal file
1
src/icon/arti.svg
Normal 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 |
@ -25,7 +25,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<back-top />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|||||||
@ -1,14 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="article-page w-full flex" :style="boxStyle">
|
<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 class="blog-list w-3/4 pl-16">
|
</div>
|
||||||
<div v-for="item in blogList" :key="item.aid" class="rounded-lg p-4 w-3/4 my-8 shadow cursor-pointer"
|
</div>
|
||||||
|
</div>
|
||||||
|
<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}`)">
|
@click="$router.push(`/blog/${item.aid}`)">
|
||||||
<div class="flex justify-right gap-4 mb-2 text-gray-600">
|
<div class="flex justify-right gap-4 mb-2 text-gray-600">
|
||||||
<em class="flex items-center gap-1 text-sm">
|
<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日') }}
|
{{ formatTime(item.updated_at, 'YYYY年MM月DD日') }}
|
||||||
</em>
|
</em>
|
||||||
<em class="flex items-center gap-1 text-sm">
|
<em class="flex items-center gap-1 text-sm">
|
||||||
@ -19,7 +33,7 @@
|
|||||||
<n-icon><icon-author /></n-icon>
|
<n-icon><icon-author /></n-icon>
|
||||||
{{ item.nickname }}</em>
|
{{ item.nickname }}</em>
|
||||||
</div>
|
</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="text-sm text-gray-500 mb-4 line-clamp-3">{{ item.pro }}</div>
|
||||||
<div class="flex gap-4">
|
<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">
|
<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>
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -42,9 +59,31 @@ definePage({
|
|||||||
const blogList = ref<any[]>([])
|
const blogList = ref<any[]>([])
|
||||||
const boxStyle = ref({})
|
const boxStyle = ref({})
|
||||||
const nav: any = $store.nav.useNavStore()
|
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() {
|
async function getBlogList() {
|
||||||
const res = await $http.blog.getBlogList()
|
const res = await $http.blog.getBlogList({
|
||||||
console.log('>>> --> getBlogList --> res:', res.data)
|
category:category.value,page_size:page_size.value,page_num:page_num.value
|
||||||
|
})
|
||||||
blogList.value = res.data
|
blogList.value = res.data
|
||||||
}
|
}
|
||||||
// 计算markdown的文字数量
|
// 计算markdown的文字数量
|
||||||
@ -56,22 +95,47 @@ function getSize(str: string) {
|
|||||||
function getTags(str: any) {
|
function getTags(str: any) {
|
||||||
str = str.replaceAll(',', ',')
|
str = str.replaceAll(',', ',')
|
||||||
let tags = str.split(',').slice(0, 5)
|
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)
|
tags[idx] = tag.trim().slice(0, 3)
|
||||||
});
|
});
|
||||||
return tags
|
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(() => {
|
onMounted(() => {
|
||||||
boxStyle.value = {
|
boxStyle.value = {
|
||||||
height: `calc(100vh - ${nav.navH + 1}px)`,
|
height: `calc(100vh - ${nav.navH + 1}px)`,
|
||||||
}
|
}
|
||||||
getBlogList()
|
getBlogList()
|
||||||
|
getCateList()
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<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>
|
</style>
|
||||||
Reference in New Issue
Block a user