完成文章归纳基本布局

This commit is contained in:
Liu 宇阳
2024-10-25 16:10:29 +08:00
parent 2197f1f64f
commit f81908a7ea
4 changed files with 204 additions and 3 deletions

146
src/app/data/page.tsx Normal file
View File

@@ -0,0 +1,146 @@
"use client"
import { useEffect, useState } from "react"
import { getArticleListAPI } from '@/api/article'
import { Article } from "@/types/app/article"
import Swiper from "@/components/Swiper";
import Starry from "@/components/Starry";
import { Accordion, AccordionItem } from "@nextui-org/react";
import archiving from '@/assets/svg/other/archiving.svg'
interface MonthData {
total: number;
list: Article[];
wordCount: number;
}
interface YearData {
year: number;
total: number;
month: Record<number, MonthData>;
wordCount: number;
}
const Title = ({ data }: { data: YearData }) => {
return (
<div>
<div className="text-xl font-sans inline-block textMarkSty">{data.year} </div>
<div>{data.total} </div>
<div>{(data.wordCount / 1000).toFixed(2)}K</div>
</div>
)
}
export default () => {
const [list, setList] = useState<YearData[]>([])
const getArticleList = async () => {
const { data } = await getArticleListAPI()
const result = groupByYearAndMonth(data);
// 从早到晚排序
result.sort((a, b) => b.year - a.year)
setList(result)
}
// 将文章进行分组
function groupByYearAndMonth(data: Article[]): YearData[] {
const groupedData: Record<number, YearData> = {};
data.forEach(item => {
const date = new Date(+item.createTime!);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const wordCount = item.content ? item.content.length : 0;
if (!groupedData[year]) {
groupedData[year] = { year, total: 0, month: {}, wordCount: 0 };
}
if (!groupedData[year].month[month]) {
groupedData[year].month[month] = { total: 0, list: [], wordCount: 0 };
}
groupedData[year].month[month].list.push(item);
groupedData[year].month[month].total++;
groupedData[year].total++;
groupedData[year].wordCount += wordCount;
});
return Object.values(groupedData);
}
useEffect(() => {
getArticleList()
}, [])
const defaultContent =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
return (
<>
<title></title>
<meta name="description" content="数据统计" />
<Swiper isRipple={false}>
{/* 星空背景组件 */}
<Starry />
<div className="absolute top-[45%] left-[50%] transform -translate-x-1/2 flex flex-col items-center">
<div className="text-white text-[20px] xs:text-[25px] sm:text-[30px] whitespace-nowrap custom_text_shadow"></div>
</div>
</Swiper>
<div className="w-[1200px] mt-10 mx-auto bg-white p-10 rounded-xl border">
<h3 className="flex items-center text-2xl mb-3"><img src={archiving.src} alt="归档" className="w-9 mr-3"/> </h3>
<Accordion
className="[&>hr]:bg-[#eee] !px-0"
motionProps={{
variants: {
enter: {
y: 0,
opacity: 1,
height: "auto",
transition: {
height: {
type: "spring",
stiffness: 500,
damping: 30,
duration: 1,
},
opacity: {
easings: "ease",
duration: 1,
},
},
},
exit: {
y: -10,
opacity: 0,
height: 0,
transition: {
height: {
easings: "ease",
duration: 0.25,
},
opacity: {
easings: "ease",
duration: 0.3,
},
},
},
},
}}
>
{
list.map((item, index) => (
<AccordionItem key={index} aria-label={item.year + '年'} title={<Title data={item} />}>
{defaultContent}
</AccordionItem>
))
}
</Accordion>
</div>
</>
)
}

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="1729842746325" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7408" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M270.4 104.8c-33.4 0-60.4 27-60.4 60.4v30.2c0 16.7 13.5 30.2 30.2 30.2h543.5c16.7 0 30.2-13.5 30.2-30.2v-30.2c0-33.4-27-60.4-60.4-60.4H270.4z m-60.3 150.9c-33.4 0-60.4 27-60.4 60.4v30.2c0 8 3.2 15.7 8.8 21.4 5.7 5.7 13.3 8.8 21.4 8.8h664.3c8 0 15.7-3.2 21.3-8.8 5.7-5.7 8.8-13.3 8.8-21.4v-30.2c0-33.4-27-60.4-60.4-60.4H210.1z m0 0" fill="#FFAA44" p-id="7409"></path><path d="M512 527.5c55.2 0 103.4-37.4 117.1-90.9 4.1-16.2 17.2-29.9 33.9-29.9h211.4c33.3 0 60.4 27 60.4 60.4V769c0 32-12.7 62.8-35.4 85.4-22.7 22.7-53.4 35.4-85.4 35.4H210.1c-32 0-62.8-12.7-85.4-35.4-22.7-22.6-35.4-53.3-35.4-85.4V467.1c0-33.3 27-60.4 60.4-60.4H361c16.7 0 29.8 13.7 33.9 29.9 13.7 53.5 61.9 90.9 117.1 90.9z m0 0" fill="#FF7744" p-id="7410"></path><path d="M345.9 708.7c0-12 4.8-23.5 13.3-32s20-13.3 32-13.3h241.6c25 0 45.3 20.3 45.3 45.3S657.8 754 632.8 754H391.2c-25-0.1-45.3-20.3-45.3-45.3z m0 0" fill="#FFFFFF" p-id="7411"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -33,6 +33,21 @@ body {
animation: 5s linear infinite miniShape;
}
// 文本标记样式
.textMarkSty {
position: relative;
&::after {
content: "";
position: absolute;
top: 50%;
left: 0;
width: 50%;
height: 10px;
background-color: #539dfd80;
}
}
@keyframes miniShape {
0%,

View File

@@ -1,5 +1,5 @@
// const url = "http://localhost:9003/api"
const url = "https://api.liuyuyang.net/api"
const url = "http://localhost:9003/api"
// const url = "https://api.liuyuyang.net/api"
export default async <T>(method: string, api: string, data?: any, caching = true) => {
const res = await fetch(`${url}${api}`, {
@@ -16,4 +16,43 @@ export default async <T>(method: string, api: string, data?: any, caching = true
})
return res.json() as Promise<ResponseData<T>>;
}
}
// [
// {
// year: 2024,
// total: 3, // 结果是每个月份的total
// month: [
// {
// 1: {
// total: 2, // 文章的数量
// list: [文章数据1, 文章数据2]
// },
// 2: {
// total: 1,
// list: [文章数据1]
// },
// }
// ]
// },
// {
// year: 2023,
// total: 2, // 结果是每个月份的total
// month: [
// {
// 1: {
// total: 1, // 文章的数量
// list: [文章数据1]
// },
// 2: {
// total: 0,
// list: []
// },
// 3: {
// total: 1,
// list: [文章数据1]
// }
// }
// ]
// }
// ]