删除关于我页面的技术栈容器,重命名获取用户数据的 API 为获取作者数据,优化朋友圈页面的组件结构,移除不必要的 API 调用,简化记录页面的渲染逻辑,替换图片列表组件为记录卡片组件,并更新数据注入逻辑以获取作者信息。

This commit is contained in:
宇阳
2025-09-13 20:14:14 +08:00
parent f56d9b3f4e
commit e94fae7ca4
14 changed files with 211 additions and 161 deletions

View File

@@ -2,6 +2,6 @@ import { User } from '@/types/app/user'
import Request from '@/utils/request'
// 获取作者信息
export const getUserDataAPI = async () => {
export const getAuthorDataAPI = async () => {
return await Request<User>('GET', '/user/author')
}

View File

@@ -1,13 +1,14 @@
import { getUserDataAPI } from '@/api/user';
import { User } from '@/types/app/user';
'use client';
import { useAuthorStore } from '@/stores';
const Copyright = async () => {
const { data } = (await getUserDataAPI()) || { data: {} as User };
const author = useAuthorStore((state) => state.author);
return (
<div className="p-3 space-y-2 border-l-[3px] border-primary bg-[#ecf7fe] rounded-md text-sm text-black-b">
<p>{data?.name}</p>
<p> {data?.name} !</p>
<p>{author?.name}</p>
<p> {author?.name} !</p>
</div>
);
};

97
src/app/friend/index.tsx Normal file
View File

@@ -0,0 +1,97 @@
'use client';
import Link from 'next/link';
import { ToastContainer } from 'react-toastify';
import Slide from '@/components/Slide';
import Starry from '@/components/Starry';
import ApplyForAdd from './components/ApplyForAdd';
import CopyableText from './components/CopyableText';
import { Web } from '@/types/app/web';
import { useConfigStore, useAuthorStore } from '@/stores';
export default ({ data }: { data: { [string: string]: { order: number; list: Web[] } } }) => {
const web = useConfigStore((state) => state.web);
const author = useAuthorStore((state) => state.author);
return (
<>
<Slide isRipple={false}>
{/* 星空背景组件 */}
<Starry />
<div className="absolute top-[30%] 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 className="mt-4 sm:mt-8">
<ApplyForAdd />
</div>
</div>
</Slide>
<div className="bg-[linear-gradient(180deg,#edf6ff_0%,#ffffff_100%)] dark:bg-[linear-gradient(to_right,#232931_0%,#232931_100%)]">
<div className="relative -top-20 xs:-top-20 sm:-top-32 md:-top-36 w-[90%] xl:w-[1200px] p-10 pt-2 mx-auto bg-white dark:bg-black-b border dark:border-black-b rounded-2xl space-y-8 ">
<div>
<h3 className="w-full text-center text-xl p-4 dark:text-white "></h3>
<div className="mx-auto p-3 space-y-2 border-l-[3px] border-primary bg-[#ecf7fe] dark:bg-[#333b48] rounded-md text-sm text-black-b dark:text-gray-300">
<p>
<CopyableText text={web?.title}>{web?.title}</CopyableText>
</p>
<p>
<CopyableText text={web?.description}>{web?.description}</CopyableText>
</p>
<p>
<CopyableText text={author?.avatar || ''}>{author?.avatar}</CopyableText>
</p>
<p>
<CopyableText text={web?.url}>{web?.url}</CopyableText>
</p>
<p>
Rss地址<CopyableText text={web?.url + '/api/rss'}>{web?.url + '/api/rss'}</CopyableText>
</p>
</div>
</div>
{Object.keys(data)?.map((type, index) => (
<div key={index}>
<h3 className="w-full text-center text-xl p-4 dark:text-white ">{type}</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
{type === '全站置顶' && (
<Link href="https://liuyuyang.net" target="_blank" className="group">
<div className="flex items-center p-3 border group-hover:border-2 dark:border-[#3d4653] group-hover:!border-primary group-hover:shadow-[0_10px_20px_1px_rgb(83,157,253,.1)] rounded-md ">
<img src="https://q1.qlogo.cn/g?b=qq&nk=3311118881&s=640" alt="项目作者" className="w-14 h-14 mr-4 rounded-full" />
<div className="flex flex-col space-y-2">
<h4 className="text-sm text-gray-700 dark:text-white group-hover:text-primary"></h4>
<p className="text-xs text-[#8c9ab1] line-clamp-2">ThriveX </p>
</div>
</div>
</Link>
)}
{data[type].list?.map((item: Web) => (
<Link key={item.id} href={item.url} target="_blank" className="group">
<div key={item.id} className="flex items-center p-3 border group-hover:border-2 dark:border-[#3d4653] group-hover:!border-primary group-hover:shadow-[0_10px_20px_1px_rgb(83,157,253,.1)] rounded-md ">
<img src={item.image} alt={item.title} className="w-14 h-14 mr-4 rounded-full" />
<div className="flex flex-col space-y-2">
<h4 className="text-sm text-gray-700 dark:text-white group-hover:text-primary">{item.title}</h4>
<p className="text-xs text-[#8c9ab1] line-clamp-2">{item.description}</p>
</div>
</div>
</Link>
))}
</div>
</div>
))}
</div>
</div>
<ToastContainer />
</>
);
};

View File

@@ -1,19 +1,9 @@
import Link from 'next/link';
import { Metadata } from 'next';
import { getWebConfigDataAPI } from '@/api/config';
import { getWebListAPI, getWebTypeListAPI } from '@/api/web';
import { Web as WebLink, WebType } from '@/types/app/web';
import Slide from '@/components/Slide';
import Starry from '@/components/Starry';
import ApplyForAdd from './components/ApplyForAdd';
import CopyableText from './components/CopyableText';
import { ToastContainer } from 'react-toastify';
import { getUserDataAPI } from '@/api/user';
import { User } from '@/types/app/user';
import { Web } from '@/types/app/config';
import Friend from './index';
export const metadata: Metadata = {
title: '😇 朋友圈',
@@ -21,10 +11,6 @@ export const metadata: Metadata = {
};
export default async () => {
const { data: user } = (await getUserDataAPI()) || { data: {} as User };
const {
data: { value: web },
} = (await getWebConfigDataAPI<{ value: Web }>('web')) || { data: { value: {} as Web } };
const { data: linkList } = (await getWebListAPI()) || { data: [] as WebLink[] };
const { data: typeList } = (await getWebTypeListAPI()) || { data: [] as WebType[] };
@@ -49,81 +35,5 @@ export default async () => {
dataTemp.sort((a, b) => a[1].order - b[1].order);
data = Object.fromEntries(dataTemp);
return (
<>
<Slide isRipple={false}>
{/* 星空背景组件 */}
<Starry />
<div className="absolute top-[30%] 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 className="mt-4 sm:mt-8">
<ApplyForAdd />
</div>
</div>
</Slide>
<div className="bg-[linear-gradient(180deg,#edf6ff_0%,#ffffff_100%)] dark:bg-[linear-gradient(to_right,#232931_0%,#232931_100%)]">
<div className="relative -top-20 xs:-top-20 sm:-top-32 md:-top-36 w-[90%] xl:w-[1200px] p-10 pt-2 mx-auto bg-white dark:bg-black-b border dark:border-black-b rounded-2xl space-y-8 ">
<div>
<h3 className="w-full text-center text-xl p-4 dark:text-white "></h3>
<div className="mx-auto p-3 space-y-2 border-l-[3px] border-primary bg-[#ecf7fe] dark:bg-[#333b48] rounded-md text-sm text-black-b dark:text-gray-300">
<p>
<CopyableText text={web?.title}>{web?.title}</CopyableText>
</p>
<p>
<CopyableText text={web?.description}>{web?.description}</CopyableText>
</p>
<p>
<CopyableText text={user?.avatar || ''}>{user?.avatar}</CopyableText>
</p>
<p>
<CopyableText text={web?.url}>{web?.url}</CopyableText>
</p>
<p>
Rss地址<CopyableText text={web?.url + '/api/rss'}>{web?.url + '/api/rss'}</CopyableText>
</p>
</div>
</div>
{Object.keys(data)?.map((type, index) => (
<div key={index}>
<h3 className="w-full text-center text-xl p-4 dark:text-white ">{type}</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
{type === '全站置顶' && (
<Link href="https://liuyuyang.net" target="_blank" className="group">
<div className="flex items-center p-3 border group-hover:border-2 dark:border-[#3d4653] group-hover:!border-primary group-hover:shadow-[0_10px_20px_1px_rgb(83,157,253,.1)] rounded-md ">
<img src="https://q1.qlogo.cn/g?b=qq&nk=3311118881&s=640" alt="项目作者" className="w-14 h-14 mr-4 rounded-full" />
<div className="flex flex-col space-y-2">
<h4 className="text-sm text-gray-700 dark:text-white group-hover:text-primary"></h4>
<p className="text-xs text-[#8c9ab1] line-clamp-2">ThriveX </p>
</div>
</div>
</Link>
)}
{data[type].list?.map((item: WebLink) => (
<Link key={item.id} href={item.url} target="_blank" className="group">
<div key={item.id} className="flex items-center p-3 border group-hover:border-2 dark:border-[#3d4653] group-hover:!border-primary group-hover:shadow-[0_10px_20px_1px_rgb(83,157,253,.1)] rounded-md ">
<img src={item.image} alt={item.title} className="w-14 h-14 mr-4 rounded-full" />
<div className="flex flex-col space-y-2">
<h4 className="text-sm text-gray-700 dark:text-white group-hover:text-primary">{item.title}</h4>
<p className="text-xs text-[#8c9ab1] line-clamp-2">{item.description}</p>
</div>
</div>
</Link>
))}
</div>
</div>
))}
</div>
</div>
<ToastContainer />
</>
);
return <Friend data={data} />;
};

View File

@@ -10,17 +10,19 @@ interface Props {
export default ({ list }: Props) => {
return (
<>
<div className={`flex justify-center ${list.length && 'mt-4'} w-full sm:w-3/6`}>
<PhotoProvider speed={() => 800} easing={(type) => (type === 2 ? 'cubic-bezier(0.36, 0, 0.66, -0.56)' : 'cubic-bezier(0.34, 1.56, 0.64, 1)')}>
<div className={`grid gap-2 ${list.length === 1 ? 'justify-center' : 'grid-cols-2'}`}>
{list?.map((url, index) => (
<PhotoView key={index} src={url}>
<img src={url} alt="闪念图片" className="rounded-2xl w-full h-full object-cover cursor-pointer" />
</PhotoView>
))}
</div>
</PhotoProvider>
</div>
{!!list?.length && (
<div className={`flex justify-center mt-4 w-full sm:w-3/6`}>
<PhotoProvider speed={() => 800} easing={(type) => (type === 2 ? 'cubic-bezier(0.36, 0, 0.66, -0.56)' : 'cubic-bezier(0.34, 1.56, 0.64, 1)')}>
<div className={`grid gap-2 ${list.length === 1 ? 'grid-cols-1 justify-center' : 'grid-cols-2 md:grid-cols-3'}`}>
{list.map((url, index) => (
<PhotoView key={index} src={url}>
<img src={url} alt="闪念图片" className="rounded-2xl w-full h-full object-cover cursor-pointer" />
</PhotoView>
))}
</div>
</PhotoProvider>
</div>
)}
</>
);
};

View File

@@ -0,0 +1,44 @@
import ImageList from './ImageList';
import Editor from './Editor';
import { dayFormat } from '@/utils';
import { User } from '@/types/app/user';
interface RecordItemProps {
id: number | string;
content: string;
images: string | string[] | null;
createTime?: string | number | Date;
user: Pick<User, 'avatar' | 'name'> | null;
}
export default function RecordCard({ id, content, images, createTime, user }: RecordItemProps) {
const imageList: string[] = Array.isArray(images) ? images : JSON.parse((images as string) || '[]');
return (
<div key={id} className="flex flex-col sm:flex-row">
<img src={user?.avatar} alt="作者头像" width={56} height={56} className="hidden sm:block rounded-lg border dark:border-black-b h-14 mr-2" />
<div className="flex sm:hidden">
<img src={user?.avatar} alt="作者头像" width={44} height={44} className="rounded-lg border dark:border-black-b h-11 mr-2" />
<div className="flex sm:hidden items-center my-1.5 ml-2 space-x-4">
<h3>{user?.name}</h3>
<span className="text-xs">{dayFormat(createTime as any)}</span>
</div>
</div>
<div className="mt-2 sm:mt-0 w-full">
<div className="hidden sm:flex items-center my-1.5 ml-4 space-x-4">
<h3>{user?.name}</h3>
<span className="text-xs">{dayFormat(createTime as any)}</span>
</div>
<div className="w-full p-4 border dark:border-black-b rounded-3xl rounded-tl-none bg-[rgba(255,255,255,0.7)] dark:bg-[rgba(30,36,46,0.9)] backdrop-blur-sm">
<Editor value={content} />
<ImageList list={imageList} />
</div>
</div>
</div>
);
}

View File

@@ -1,15 +1,13 @@
import ImageList from './components/ImageList';
import RecordCard from './components/RecordCard';
import { getRecordPagingAPI } from '@/api/record';
import { getUserDataAPI } from '@/api/user';
import { getAuthorDataAPI } from '@/api/user';
import { Record } from '@/types/app/record';
import { User } from '@/types/app/user';
import { dayFormat } from '@/utils';
import Pagination from '@/components/Pagination';
import Empty from '@/components/Empty';
import Show from '@/components/Show';
import { getWebConfigDataAPI } from '@/api/config';
import { Theme } from '@/types/app/config';
import Editor from './components/Editor';
interface Props {
searchParams: Promise<{ page: number }>;
@@ -19,7 +17,7 @@ export default async (props: Props) => {
const searchParams = await props.searchParams;
const page = searchParams.page || 1;
const { data: user } = (await getUserDataAPI()) || { data: {} as User };
const { data: user } = (await getAuthorDataAPI()) || { data: {} as User };
const { data: record } = (await getRecordPagingAPI({ pagination: { page, size: 8 } })) || { data: {} as Paginate<Record[]> };
const {
data: { value: theme },
@@ -41,32 +39,14 @@ export default async (props: Props) => {
<div className="space-y-12">
{!!record?.result?.length &&
record?.result.map((item) => (
<div key={item.id} className="flex flex-col sm:flex-row">
<img src={user?.avatar} alt="作者头像" width={56} height={56} className="hidden sm:block rounded-lg border dark:border-black-b h-14 mr-2 " />
<div className="flex sm:hidden">
<img src={user?.avatar} alt="作者头像" width={44} height={44} className="rounded-lg border dark:border-black-b h-11 mr-2 " />
<div className="flex sm:hidden items-center my-1.5 ml-2 space-x-4">
<h3>{user?.name}</h3>
<span className="text-xs">{dayFormat(item?.createTime)}</span>
</div>
</div>
<div className="mt-2 sm:mt-0 w-full">
<div className="hidden sm:flex items-center my-1.5 ml-4 space-x-4">
<h3>{user?.name}</h3>
<span className="text-xs">{dayFormat(item?.createTime)}</span>
</div>
<div className="w-full p-4 border dark:border-black-b rounded-3xl rounded-tl-none bg-[rgba(255,255,255,0.7)] dark:bg-[rgba(30,36,46,0.9)] backdrop-blur-sm ">
<Editor value={item?.content} />
<ImageList list={JSON.parse((item?.images as string) || '[]')} />
{/* <Comment /> */}
</div>
</div>
</div>
<RecordCard
key={item.id}
id={item.id as any}
content={item.content as any}
images={item.images as any}
createTime={item.createTime as any}
user={user as any}
/>
))}
<Show is={!record?.result?.length}>

View File

@@ -1,7 +1,7 @@
import Link from 'next/link';
import Image from 'next/image';
import { getWebConfigDataAPI } from '@/api/config';
import { getUserDataAPI } from '@/api/user';
import { getAuthorDataAPI } from '@/api/user';
import { User } from '@/types/app/user';
import { Web } from '@/types/app/config';
import Tooltip from './components/Tooltip';
@@ -10,7 +10,7 @@ import animals from './images/animals.webp';
import ICP from './images/ICP.png';
export default async () => {
const { data: user } = (await getUserDataAPI()) || { data: {} as User };
const { data: user } = (await getAuthorDataAPI()) || { data: {} as User };
const {
data: { value: web },
} = (await getWebConfigDataAPI<{ value: Web }>('web')) || { data: { value: {} as Web } };

View File

@@ -3,10 +3,20 @@
import { useEffect } from 'react';
import { getWebConfigDataAPI } from '@/api/config';
import { useConfigStore } from '@/stores';
import { useAuthorStore, useConfigStore } from '@/stores';
import { Web, Theme, Other } from '@/types/app/config';
import { getAuthorDataAPI } from '@/api/user';
import { User } from '@/types/app/user';
export default () => {
const setAuthor = useAuthorStore((state) => state.setAuthor);
// 获取作者信息
const getAuthorData = async () => {
const { data: user } = (await getAuthorDataAPI()) || { data: {} as User };
setAuthor(user);
};
const { setWeb, setTheme, setOther } = useConfigStore();
// 获取项目配置
@@ -28,6 +38,7 @@ export default () => {
};
useEffect(() => {
getAuthorData();
getConfigData();
}, []);

View File

@@ -9,13 +9,13 @@ import Juejin from '@/assets/svg/socializing/Juejin.svg';
import QQ from '@/assets/svg/socializing/QQ.svg';
import Weixin from '@/assets/svg/socializing/Weixin.svg';
import { getUserDataAPI } from '@/api/user';
import { getAuthorDataAPI } from '@/api/user';
import { getWebConfigDataAPI } from '@/api/config';
import { User } from '@/types/app/user';
import { Social, Theme } from '@/types/app/config';
const Author = async () => {
const { data: user } = (await getUserDataAPI()) || { data: {} as User };
const { data: user } = (await getAuthorDataAPI()) || { data: {} as User };
const {
data: { value: theme },
} = (await getWebConfigDataAPI<{ value: Theme }>('theme')) || { data: { value: {} as Theme } };

View File

@@ -1,8 +1,9 @@
'use client';
import { ReactNode } from 'react';
import Ripple from '@/components/Ripple';
import { getRandom } from '@/utils';
import { getWebConfigDataAPI } from '@/api/config';
import { Theme } from '@/types/app/config';
import { useConfigStore } from '@/stores';
interface Props {
src?: string; // 图片列表
@@ -10,12 +11,9 @@ interface Props {
children?: ReactNode;
}
export default async ({ src, isRipple = true, children }: Props) => {
const {
data: { value: data },
} = (await getWebConfigDataAPI<{ value: Theme }>('theme')) || { data: { value: {} as Theme } };
const covers = data.covers || [];
export default ({ src, isRipple = true, children }: Props) => {
const theme = useConfigStore((state) => state.theme);
const covers = theme.covers || [];
const sty = {
backgroundImage: `url(${src ? src : covers[getRandom(0, covers.length - 1)]})`,

View File

@@ -1,3 +1,4 @@
import useConfigStore from './modules/config'
import useAuthorStore from './modules/author'
export { useConfigStore }
export { useConfigStore, useAuthorStore };

View File

@@ -0,0 +1,13 @@
import { create } from 'zustand';
import { User } from '@/types/app/user';
interface AuthorState {
// 作者信息
author: User;
setAuthor: (data: User) => void;
}
export default create<AuthorState>((set) => ({
author: {} as User,
setAuthor: (data: User) => set(() => ({ author: data })),
}));

View File

@@ -1,7 +0,0 @@
一、调整关于我页面技术栈容器宽度
二、调整我的履历页面 项目描述、亮点和难点的渲染逻辑,确保在有内容时才显示相关信息。
三、解决移动端点击导航跳转到分类 BUG
四、放行所有图片来源,不用再手动配置