新增文章

This commit is contained in:
liangzai 2024-09-26 03:06:37 +08:00
parent 0df3237d21
commit cc27475304
10 changed files with 362 additions and 20 deletions

View File

@ -16,7 +16,7 @@ const nextConfig = {
pathname: "/_next/image/**",
},
],
domains: ['www.typeframes.com'], // 允许从该域名加载图片资源
domains: ['www.typeframes.com', 'www.typeframes.ai', 'www.typeframes.com.cn', 'www.typeframes.cc', 'static.wixstatic.com', 'images.pexels.com'], // 允许从该域名加载图片资源
},
sassOptions: {

View File

@ -18,4 +18,15 @@ export const GetVideoList = () => {
}
export const GetMetadata = () => {
return request.get<any>('/website/');
}
export const GetPostList = (params) => {
return request.get<any>(`/article-list/`, {
params,
});
}
export const GetPost = (id, lang) => {
return request.get<any>(`/article/${id}/${lang}`);
}
export const GetRandomPosts = () => {
return request.get<any>('/random_articles/');
}

View File

@ -0,0 +1,7 @@
export default function Loading() {
return (
<div className="z-0 relative w-full h-screen flex justify-center items-center">
<span className="loading loading-spinner loading-lg text-secondary"></span>
</div>
);
}

View File

@ -0,0 +1,153 @@
import { GetPostList } from "@/apis/auth";
import { getLocale } from "next-intl/server";
import Image from "next/image";
import Link from "next/link";
export default async function ArticleList({ params, searchParams }) {
const page = parseInt(params.page, 10) || 1; // 获取当前页码
const locale = await getLocale();
const limit = 9; // 每页文章数
const tag = searchParams.tag || ""; // 获取 URL 中的 tag 参数
interface Article {
id: number;
title: string;
keywords: string;
image_url: string;
published_at: string;
}
let list: Article[] = [];
let totalPages = 1;
let keywords = [];
try {
const data = await GetPostList({
t: new Date().getTime(),
page,
limit,
tag, // 如果有 tag带上 tag 参数
});
if (data.code === 200) {
list = data.data.articles;
totalPages = Math.ceil(data.data.count / limit); // 计算总页数
keywords = data.data[`${locale}_keywords`];
}
} catch {}
return (
<div className="container mx-auto px-4 py-8">
{/* 关键词筛选部分 */}
<div className="mb-6">
<h3 className="text-xl font-bold text-white mb-4">
{" "}
{locale === "en" ? "Filtering tags" : "筛选标签"}:
</h3>
<div className="flex flex-wrap">
{/* 全部标签 */}
<Link
href={`/article-list/1`}
className={`mr-4 mb-2 px-4 py-1 rounded text-sm ${
!tag
? "bg-secondary text-white"
: "bg-gray-800 text-gray-300 hover:bg-gray-700"
}`}
>
{locale === "en" ? "all" : "全部"}
</Link>
{keywords.map((keyword, index) => (
<Link
href={`/article-list/1?tag=${keyword}`}
key={index}
className={`mr-4 mb-2 px-4 py-1 rounded text-sm ${
tag === keyword
? "bg-secondary text-white"
: "bg-gray-800 text-gray-300 hover:bg-gray-700"
}`}
>
#{keyword}
</Link>
))}
</div>
</div>
{/* 文章列表部分 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{list.map((item, index) => (
<div
className="bg-[#15171a] rounded-lg overflow-hidden shadow-lg"
key={index}
>
<Link href={`/article/${item.id}`}>
<Image
src={item["image_url"]}
width={1920}
height={1080}
alt={item[`${locale}_keywords`].join()}
className="w-full h-48 object-cover"
/>
</Link>
<div className="p-6">
<Link
href={`/article/${item.id}`}
className="text-white hover:text-secondary hover:underline"
>
<h2 className="text-xl font-bold mb-2">
{item[`${locale}_title`]}
</h2>
</Link>
<p className="text-gray-400 text-sm mb-4">
{new Date(
item["published_at"]
).toLocaleDateString(locale, {
year: "numeric",
month: "long",
day: "numeric",
})}
</p>
<p className="text-gray-300 mb-6">
{item[`${locale}_keywords`].map(
(kitem, kindex) => (
<Link
href={`/article-list/1?tag=${kitem}`}
key={kindex}
className="mr-2 text-sm hover:underline hover:text-secondary"
>
#{kitem}
</Link>
)
)}
</p>
<Link
href={`/article/${item.id}`}
className="text-secondary hover:underline"
>
Read More
</Link>
</div>
</div>
))}
</div>
{/* 分页按钮 */}
<div className="join mt-8 flex justify-center">
{Array.from({ length: totalPages }, (_, index) => (
<Link
href={`/article-list/${index + 1}${
tag ? `?tag=${tag}` : ""
}`}
key={index}
className={`join-item btn btn-md ${
page === index + 1
? "bg-secondary text-white"
: "bg-gray-800 text-gray-300 hover:bg-gray-700"
}`}
>
{index + 1}
</Link>
))}
</div>
</div>
);
}

View File

@ -0,0 +1,11 @@
import { redirect } from "next/navigation";
export default function Page({ params }) {
const { page } = params;
if (!page) {
// 如果page参数不存在重定向到第一页
redirect("/article-list/1");
}
return <></>;
}

View File

@ -0,0 +1,159 @@
import { GetPost, GetRandomPosts } from "@/apis/auth";
import PageRemark from "@/ui/page/page-remark";
import { Metadata } from "next";
import { getLocale } from "next-intl/server";
import Image from "next/image";
import Link from "next/link";
export const metadata: Metadata = {
title: "",
keywords: "",
};
export default async function Article({ params }) {
const postID = params.id;
const locale = await getLocale();
let postContent = "";
let title = "";
let keyword = "";
let time = "";
let imgUrl = "";
let randomPosts: any = [];
try {
const data = await GetPost(postID, locale);
if (data.code === 200) {
postContent = data.data.content;
title = data.data.title;
keyword = data.data.keywords;
time = data.data.published_at;
imgUrl = data.data.image_url;
console.log("imgUrl", imgUrl);
metadata.title = title;
metadata.keywords = keyword;
}
// 获取随机文章推荐
const randomData = await GetRandomPosts(); // 假设返回3篇随机文章
if (randomData.code === 200) {
randomPosts = randomData.data.articles;
}
} catch {}
return (
<>
<article className="rounded-lg px-4 sm:px-6 md:px-8 border border-[#252629] w-full bg-[#15171a] text-white ">
<div className="relative z-10 mx-auto py-12 lg:py-24">
<div className="space-y-4 mb-14">
<h2 className='font-["Euclid_Circular_A"] text-3xl lg:text-4xl xl:text-5xl text-center'>
{title}
</h2>
</div>
<Image
src={imgUrl}
alt={keyword}
width={1920}
height={1080}
className="w-[90%] lg:w-[85%] xl:w-[75%] mx-auto my-4"
/>
<article className="w-[90%] lg:w-[85%] xl:w-[75%] mx-auto space-y-6">
<section>
<div
className="whitespace-pre-line indent-[2em] leading-relaxed text-lg text-gray-200"
dangerouslySetInnerHTML={{
__html: postContent.replace(
/<p>/g,
'<p class="mb-4">'
),
}}
></div>
</section>
</article>
<div className="w-[90%] lg:w-[85%] xl:w-[75%] mx-auto mt-10 text-gray-400">
<div>
{keyword.split(",").map((item, index) => {
return (
<Link
href={`/article-list/1?tag=${item}`}
key={index}
className=" underline mr-4"
>
#{item}
</Link>
);
})}
</div>
<div>{time}</div>
</div>
<PageRemark />
{/* 随机文章推荐 */}
<div className="w-[90%] lg:w-[85%] xl:w-[75%] mx-auto mt-14">
<h3 className="text-2xl font-bold mb-6 text-white">
{locale === "en" ? "Related Articles" : "相关文章"}
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{randomPosts.map((item, index) => (
<div
className="bg-[#15171a] rounded-lg overflow-hidden shadow-lg"
key={index}
>
<Link href={`/article/${item.id}`}>
<Image
src={item["image_url"]}
width={1920}
height={1080}
alt={item["keywords"]}
className="w-full h-48 object-cover"
/>
</Link>
<div className="p-6">
<Link
href={`/article/${item.id}`}
className="text-white hover:text-secondary hover:underline"
>
<h2 className="text-xl font-bold mb-2">
{item[`${locale}_title`]}
</h2>
</Link>
<p className="text-gray-400 text-sm mb-4">
{new Date(
item["published_at"]
).toLocaleDateString(locale, {
year: "numeric",
month: "long",
day: "numeric",
})}
</p>
<p className="text-gray-300 mb-6">
{item[`${locale}_keywords`].map(
(kitem, kindex) => (
<Link
href={`/article-list/1?tag=${kitem}`}
key={kindex}
className="mr-2 text-sm hover:underline hover:text-secondary"
>
#{kitem}
</Link>
)
)}
</p>
<Link
href={`/article/${item.id}`}
className="text-secondary hover:underline"
>
Read More
</Link>
</div>
</div>
))}
</div>
</div>
</div>
</article>
</>
);
}

View File

@ -0,0 +1,7 @@
export default function Loading() {
return (
<div className="z-0 relative w-full h-screen flex justify-center items-center">
<span className="loading loading-spinner loading-lg text-secondary"></span>
</div>
);
}

View File

@ -10,7 +10,7 @@ export default function MainLayout({
<>
<main
data-theme="typeframes"
className="z-0 bg-almostblack relative flex flex-col items-center overflow-x-hidden"
className="min-h-screen z-0 bg-almostblack relative flex flex-col items-center overflow-x-hidden"
>
<PageHeader />
<section className="max-w-screen-2xl w-full relative py-24 px-4 md:px-16 ">

View File

@ -32,6 +32,14 @@ export default function PageHeader() {
>
<div className="lg:pr-4 lg:ml-8">
<ul className="divide-y lg:divide-y-0 divide-white/5 gap-8 py-4 lg:hover:bg-transparent lg:hover:pl-0 tracking-wide lg:flex lg:space-y-0 lg:text-sm lg:items-center">
<li>
<Link
className="btn-tf hover:underline lg:flex"
href="/article-list/1"
>
{t("blog")}
</Link>
</li>
<li>
<Link
className="btn-tf btn-tf-secondary lg:flex"
@ -43,20 +51,6 @@ export default function PageHeader() {
</ul>
</div>
</div>
{/* <button
aria-label="hamburger"
id="hamburger"
className="relative -mr-6 p-6 lg:hidden"
>
<div
aria-hidden="true"
className="m-auto h-0.5 w-5 rounded bg-sky-50 transition duration-300 dark:bg-gray-300"
></div>
<div
aria-hidden="true"
className="m-auto mt-1.5 h-0.5 w-5 rounded bg-sky-50 transition duration-300 dark:bg-gray-300"
></div>
</button> */}
</div>
</div>
</div>

View File

@ -1,4 +1,3 @@
import { redirect } from 'next/navigation'; // 如果在 SSR 中需要使用
import queryString from 'query-string';
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
@ -32,8 +31,8 @@ class Request {
? cacheTime > 0
? { next: { revalidate: cacheTime } }
: { cache: 'no-store' }
: { cache: 'force-cache' };
: { cache: 'no-store' };
// 最后一行 : { cache: 'no-store' }; 原本是 force-cache
if (method === 'GET' || method === 'DELETE') {
//fetch对GET请求等不支持将参数传在body上只能拼接url
if (params) {
@ -97,13 +96,14 @@ class Request {
});
}
async httpFactory<T>({ url = '', params = {}, method }: Props): Promise<T> {
async httpFactory<T>({ url = '', params = { cacheTime: 0 }, method }: Props): Promise<T> {
const req = this.interceptorsRequest({
url: process.env.NEXT_PUBLIC_BASEURL + url,
method,
params: params.params,
cacheTime: params.cacheTime,
});
console.log("req", req)
const res = await fetch(req.url, req.options);
return this.interceptorsResponse<T>(res);
}