setActive(false)}
126 | />
127 |
132 |
setActive(!avtive)}
135 | >
136 |
137 |
138 |
139 |
140 | {navList?.map((v) => navItem(v))}
141 |
142 |
themeSwitch("click")}>
143 |
149 |
155 |
156 |
157 |
158 | >
159 | );
160 | }
161 |
--------------------------------------------------------------------------------
/app/components/NavBar/navBar.module.css:
--------------------------------------------------------------------------------
1 | .nav {
2 | width: 100%;
3 | height: 70px;
4 | display: flex;
5 | align-items: center;
6 | justify-content: space-between;
7 | padding: 0 30px;
8 | position: fixed;
9 | top: 0;
10 | left: 0;
11 | z-index: 10;
12 | -webkit-user-select: none; /* Safari */
13 | -moz-user-select: none; /* Firefox */
14 | -ms-user-select: none; /* Internet Explorer/Edge */
15 | user-select: none; /* Non-prefixed version */
16 | }
17 |
18 | .nav_left {
19 | display: flex;
20 | align-items: center;
21 | padding-left: 24px;
22 | }
23 |
24 | .title {
25 | font-family: "Times New Roman", Times, serif;
26 | font-size: 20px;
27 | white-space: nowrap;
28 | color: var(--txt-w-pure);
29 | }
30 |
31 | .title:hover {
32 | color: var(--txt-w-pure);
33 | }
34 |
35 | .nav_right {
36 | flex: 1;
37 | display: flex;
38 | align-items: center;
39 | justify-content: flex-end;
40 | }
41 |
42 | .nav_list {
43 | display: flex;
44 | align-items: center;
45 | margin-right: 20px;
46 | justify-content: flex-end;
47 | }
48 |
49 | .nav_item {
50 | margin: 0 5px;
51 | padding: 5px 10px;
52 | display: flex;
53 | align-items: center;
54 | color: var(--txt-w-pure);
55 | }
56 |
57 | /* 当前选中导航的样式 */
58 | .nav_item_active{
59 | background-color: var(--b-alpha-5);
60 | border-radius: 5px;
61 | }
62 |
63 | .nav_item:hover {
64 | background-color: var(--b-alpha-5);
65 | border-radius: 5px;
66 | color: var(--txt-w-pure);
67 | }
68 |
69 | .nav_item_icon {
70 | margin-right: 4px;
71 | font-size: 18px;
72 | }
73 |
74 | .nav_item_title {
75 | font-size: 16px;
76 | white-space: nowrap;
77 | }
78 |
79 | .nav_type {
80 | padding: 0 10px;
81 | display: flex;
82 | align-items: center;
83 | background: var(--ct-theme);
84 | border: var(--ct-theme-border);
85 | border-radius: 20px;
86 | overflow: hidden;
87 | }
88 |
89 | .nav_type_item {
90 | padding: 5px;
91 | font-size: 24px;
92 | transform: translateY(-40px);
93 | transition: 1s;
94 | }
95 |
96 | .nav_type_item_active {
97 | transform: translateY(0);
98 | }
99 |
100 | /* mobile */
101 | .nav_mobile {
102 | flex: 1;
103 | height: 100%;
104 | display: none;
105 | align-items: center;
106 | justify-content: flex-end;
107 | }
108 |
109 | .nav_mobile_btn {
110 | padding: 0 20px;
111 | height: 100%;
112 | display: flex;
113 | align-items: center;
114 | font-size: 24px;
115 | color: var(--b-alpha);
116 | }
117 |
118 | .nav_mobile_mask {
119 | position: fixed;
120 | right: 0;
121 | top: 0;
122 | width: 100vw;
123 | height: 100vh;
124 | z-index: 10;
125 | background-color: rgba(0, 0, 0, 0.45);
126 | backdrop-filter: blur(10px);
127 | justify-content: flex-end;
128 | visibility: hidden;
129 | opacity: 0;
130 | transition: 0.25s;
131 | }
132 |
133 | .nav_mobile_content {
134 | position: fixed;
135 | right: 0;
136 | top: 0;
137 | z-index: 20;
138 | width: 66vw;
139 | max-width: 300px;
140 | height: 100vh;
141 | background-color: var(--w-alpha-80);
142 | -webkit-backdrop-filter: blur(30px) saturate(180%);
143 | backdrop-filter: blur(30px) saturate(180%);
144 | flex-direction: column;
145 | visibility: hidden;
146 | transform: translateX(100%);
147 | transition: 0.25s;
148 | }
149 |
150 | .nav_mobile_content .nav_mobile_btn {
151 | display: flex;
152 | align-items: center;
153 | justify-content: flex-end;
154 | height: 70px;
155 | }
156 |
157 | .nav_mobile_list {
158 | flex: 1;
159 | display: flex;
160 | align-items: center;
161 | justify-content: flex-end;
162 | padding: 10px;
163 | flex-direction: column;
164 | }
165 |
166 | .nav_mobile_list .nav_list {
167 | width: 100%;
168 | align-items: center;
169 | flex-direction: column;
170 | margin-right: 0;
171 | border-bottom: 1px solid var(--b-alpha-5);
172 | }
173 |
174 | .nav_mobile_list .nav_item {
175 | width: 100%;
176 | padding: 15px 30px;
177 | margin: 0;
178 | color: var(--b-alpha) !important;
179 | }
180 |
181 | .nav_mobile_list .nav_type {
182 | margin-top: 20px;
183 | }
184 |
185 | @media screen and (max-width: 1040px) {
186 | .nav {
187 | padding: 0;
188 | }
189 | }
190 |
191 | @media screen and (max-width: 980px) {
192 | .nav {
193 | padding: 0;
194 | }
195 | .nav_right {
196 | display: none;
197 | }
198 |
199 | .nav_mobile {
200 | display: flex;
201 | }
202 |
203 | .nav_mobile_mask_active {
204 | visibility: visible;
205 | opacity: 1;
206 | }
207 |
208 | .nav_mobile_content_active {
209 | visibility: visible;
210 | transform: translateX(0);
211 | }
212 | }
213 |
214 | @media print {
215 | .nav {
216 | padding: 0;
217 | }
218 |
219 | .nav_right {
220 | display: none;
221 | }
222 |
223 | .nav_mobile {
224 | display: flex;
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/app/components/NavBar/routes.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Descripttion:
3 | * @version:
4 | * @Author: WangPeng
5 | * @Date: 2023-01-12 14:27:58
6 | * @LastEditors: WangPeng
7 | * @LastEditTime: 2023-12-26 17:03:03
8 | */
9 | export const navList = [
10 | {
11 | key: "home",
12 | href: "/",
13 | icon: "icon-zhuye",
14 | title: "首页",
15 | },
16 | {
17 | key: "blog",
18 | href: "/blog/1",
19 | icon: "icon-16",
20 | title: "文章",
21 | },
22 | {
23 | key: "archive",
24 | href: "/archive",
25 | icon: "icon-guidang",
26 | title: "文归档",
27 | },
28 | {
29 | key: "tree-hole",
30 | href: "/tree-hole",
31 | icon: "icon--_liaotian",
32 | title: "树洞",
33 | },
34 | {
35 | key: "photography",
36 | href: "/photography",
37 | icon: "icon-sheying",
38 | title: "摄影",
39 | },
40 | {
41 | key: "friendly-links",
42 | href: "/friendly-links",
43 | icon: "icon-icon_xinyong_xianxing_jijin-",
44 | title: "友情链接",
45 | },
46 | {
47 | key: "about",
48 | href: "/about",
49 | icon: "icon-geren",
50 | title: "关于",
51 | },
52 | {
53 | key: "more",
54 | href: "/more",
55 | icon: "icon-fenlei",
56 | title: "更多",
57 | },
58 | ];
59 |
--------------------------------------------------------------------------------
/app/components/PagerComponent/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | import SysIcon from "@components/SysIcon";
5 | import styles from "./pager.module.css";
6 |
7 | interface Props {
8 | total: number;
9 | pageSize: number;
10 | current: number;
11 | onChange: (page: number) => void;
12 | }
13 |
14 | const PagerComponent = (props: Props) => {
15 | const { total, pageSize, current, onChange } = props;
16 | const totalPage = Math.ceil(total / pageSize);
17 |
18 | const clickPage = (type) => {
19 | if (type === "prev" && current > 1) {
20 | onChange(current - 1);
21 | }
22 | if (type === "next" && current < totalPage) {
23 | onChange(current + 1);
24 | }
25 | };
26 |
27 | return (
28 |
29 |
onChange(1)}
34 | >
35 | 首页
36 |
37 |
clickPage("prev")}
42 | >
43 |
44 |
45 |
46 | {current}
47 | /
48 | {totalPage}
49 |
50 |
= totalPage ? styles.btn_disabled : ""
53 | }`}
54 | onClick={() => clickPage("next")}
55 | >
56 |
57 |
58 |
= totalPage ? styles.btn_disabled : ""
61 | }`}
62 | onClick={() => onChange(totalPage)}
63 | >
64 | 尾页
65 |
66 |
67 | );
68 | };
69 |
70 | export default PagerComponent;
71 |
--------------------------------------------------------------------------------
/app/components/PagerComponent/pager.module.css:
--------------------------------------------------------------------------------
1 | .gaper{
2 | display: flex;
3 | align-items: center;
4 | }
5 |
6 | .prev_btn,.next_btn{
7 | width: 32px;
8 | height: 32px;
9 | display: flex;
10 | align-items: center;
11 | justify-content: center;
12 | font-size: 24px;
13 | margin: 0 8px;
14 | border-radius: 8px;
15 | transition: 0.25s;
16 | cursor: pointer;
17 | color: var(--b-alpha);
18 | }
19 |
20 | .prev_btn:hover,.next_btn:hover{
21 | background-color: var(--purple-text-hover);
22 | }
23 |
24 |
25 | .prev_btn:hover .icon,.next_btn:hover .icon{
26 | color: var(--w-alpha);
27 | }
28 |
29 | .show{
30 | display: flex;
31 | align-items: center;
32 | }
33 |
34 | .text{
35 | font-size: 14px;
36 | color: var(--b-alpha-80);
37 | padding: 0 2px;
38 | }
39 |
40 | .first_page,.last_page{
41 | font-size: 14px;
42 | cursor: pointer;
43 | color: var(--b-alpha-80);
44 | padding: 0 8px;
45 | border-radius: 8px;
46 | height: 32px;
47 | display: flex;
48 | align-items: center;
49 | justify-content: center;
50 | transition: 0.25s;
51 | }
52 |
53 | .first_page:hover,.last_page:hover{
54 | background-color: var(--purple-text-hover);
55 | color: var(--w-alpha);
56 | }
57 |
58 | .btn_disabled{
59 | cursor:not-allowed;
60 | color: var(--b-alpha-40);
61 | }
62 |
--------------------------------------------------------------------------------
/app/components/Permit/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | /*
4 | * @Descripttion:知识共享许可协议
5 | * @version:
6 | * @Author: WangPeng
7 | * @Date: 2023-05-24 13:14:12
8 | * @LastEditors: WangPeng
9 | * @LastEditTime: 2024-01-03 15:15:20
10 | */
11 |
12 | import Link from "next/link";
13 | import React from "react";
14 | import style from "./permit.module.css";
15 | import SysIcon from "@components/SysIcon";
16 |
17 | type Props = {
18 | id: number | string;
19 | user: string;
20 | };
21 |
22 | const Permit = (props: Props) => {
23 | const { id, user } = props;
24 | return (
25 |
26 |
27 |
28 |
版权所属:{user}
29 |
30 |
31 |
32 |
33 | 本文链接:https://wp-boke.work/blog-details/{id}
34 |
35 |
36 |
37 |
38 |
39 | 作品许可:本作品采用
40 |
46 | 知识共享署名-相同方式共享 4.0 国际许可协议
47 |
48 | 进行许可。
49 |
50 |
51 |
52 | );
53 | };
54 |
55 | export default Permit;
56 |
--------------------------------------------------------------------------------
/app/components/Permit/permit.module.css:
--------------------------------------------------------------------------------
1 | .permit{
2 | width: 100%;
3 | margin: 0 auto;
4 | padding: 20px;
5 | border-radius: 8px;
6 | background-color: var(--color-blue-bg);
7 | max-width: 800px;
8 | }
9 |
10 | .copyright_owner,.article_link,.license_agreement{
11 | padding: 6px 0px;
12 | display: flex;
13 | }
14 |
15 | .icon{
16 | flex-shrink: 0;
17 | width: 28px;
18 | height: 28px;
19 | font-size: 12px;
20 | background-color: var(--purple-border-hover);
21 | display: flex;
22 | align-items: center;
23 | justify-content: center;
24 | border-radius: 50%;
25 | color: var(--txt-w-pure);
26 | margin-right: 16px;
27 | }
28 |
29 | .text{
30 | font-size: 14px;
31 | color: var(--b-alpha-60);
32 | line-height: 28px;
33 | }
34 |
35 | .link{
36 | color: var(--color-blue);
37 | }
38 |
39 | .link:hover{
40 | text-decoration: underline;
41 | }
--------------------------------------------------------------------------------
/app/components/ScrollComponent/index.module.css:
--------------------------------------------------------------------------------
1 | .scroll-container {
2 | width: 100%;
3 | max-height: 100vh;
4 | height: 100%;
5 | overflow-y: auto;
6 | scroll-behavior: smooth;
7 | }
8 |
9 | .scroll-item {
10 | width: 100%;
11 | height: auto;
12 | box-sizing: border-box;
13 | }
14 |
--------------------------------------------------------------------------------
/app/components/ScrollComponent/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect, useState } from "react";
2 | import styles from "./index.module.css";
3 |
4 | type ListItem = string | number | boolean | object | null | undefined;
5 |
6 | interface ScrollListProps
{
7 | className?: string;
8 | items: T[];
9 | renderItem: (v: ListItem, index: number, current: number) => React.ReactNode;
10 | }
11 |
12 | type Props = {
13 | className?: string;
14 | data: ListItem[];
15 | renderItem: (v: ListItem, index: number, current: number) => React.ReactNode;
16 | };
17 |
18 | const ScrollList = ({
19 | className,
20 | items,
21 | renderItem,
22 | }: ScrollListProps) => {
23 | const containerRef = useRef(null);
24 | const scrollTimeoutRef = useRef(null);
25 | const [currentIndex, setCurrentIndex] = useState(0);
26 | const startYRef = useRef(0);
27 | const endYRef = useRef(0);
28 | const deltaThreshold = 80; // 滚动距离阈值
29 |
30 | const scrollToIndex = (index) => {
31 | if (containerRef.current) {
32 | const itemHeight = containerRef.current.children[0].clientHeight;
33 | containerRef.current.scrollTo({
34 | top: index * itemHeight,
35 | behavior: "smooth",
36 | });
37 | setCurrentIndex(index);
38 | }
39 | };
40 |
41 | const handleWheel = (event) => {
42 | event.preventDefault();
43 | clearTimeout(scrollTimeoutRef.current);
44 | scrollTimeoutRef.current = setTimeout(() => {
45 | if (event.deltaY > 0) {
46 | scrollToIndex(Math.min(currentIndex + 1, items.length - 1));
47 | } else {
48 | scrollToIndex(Math.max(currentIndex - 1, 0));
49 | }
50 | }, 100);
51 | };
52 |
53 | const handleTouchStart = (event) => {
54 | startYRef.current = event.touches[0].clientY;
55 | };
56 |
57 | const handleTouchMove = (event) => {
58 | endYRef.current = event.touches[0].clientY;
59 | };
60 |
61 | const handleTouchEnd = () => {
62 | const deltaY = startYRef.current - endYRef.current;
63 | if (deltaY > deltaThreshold) {
64 | scrollToIndex(Math.min(currentIndex + 1, items.length - 1));
65 | } else if (deltaY < -deltaThreshold) {
66 | scrollToIndex(Math.max(currentIndex - 1, 0));
67 | }
68 | };
69 |
70 | useEffect(() => {
71 | const container = containerRef.current;
72 | if (container) {
73 | container.addEventListener("wheel", handleWheel);
74 | container.addEventListener("touchstart", handleTouchStart);
75 | container.addEventListener("touchmove", handleTouchMove);
76 | container.addEventListener("touchend", handleTouchEnd);
77 |
78 | return () => {
79 | container.removeEventListener("wheel", handleWheel);
80 | container.removeEventListener("touchstart", handleTouchStart);
81 | container.removeEventListener("touchmove", handleTouchMove);
82 | container.removeEventListener("touchend", handleTouchEnd);
83 | };
84 | }
85 | }, [currentIndex, items.length]);
86 |
87 | return (
88 |
92 | {items.map((item, index) => (
93 |
94 | {renderItem(item, index, currentIndex)}
95 |
96 | ))}
97 |
98 | );
99 | };
100 |
101 | const ScrollComponent = (props: Props) => {
102 | const { data, renderItem, className } = props;
103 | return (
104 |
105 | );
106 | };
107 |
108 | export default ScrollComponent;
109 |
--------------------------------------------------------------------------------
/app/components/SysIcon/index.md:
--------------------------------------------------------------------------------
1 | # SysIcon 图标组件
2 |
3 | ## 代码演示
4 |
5 | ### 图标访问 https://www.iconfont.cn/manage/index?spm=a313x.7781069.1998910419.20&manage_type=myprojects&projectId=3968880&keyword=&project_type=&page=
6 |
7 | ```javascript
8 | import SysIcon from '@/components/SysIcon';
9 |
10 | export default class TestPage extends Component {
11 | render() {
12 | return (
13 |
14 |
15 |
16 | );
17 | }
18 | }
19 | ```
20 |
--------------------------------------------------------------------------------
/app/components/SysIcon/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { createFromIconfontCN } from "@ant-design/icons";
3 |
4 | const SysIcon = createFromIconfontCN({
5 | scriptUrl: [
6 | "//at.alicdn.com/t/c/font_3968880_yu7d3orxs38.js",
7 | "//wp-1302605407.cos.ap-beijing.myqcloud.com/font_3968880_qi74h90o7s/iconfont.js",
8 | ],
9 | });
10 |
11 | export default SysIcon;
12 |
--------------------------------------------------------------------------------
/app/components/VideoPlay/index.module.css:
--------------------------------------------------------------------------------
1 | .video_item{
2 | width: 100%;
3 | height: 100%;
4 | }
5 |
6 | .player {
7 | padding-top: 0 !important;
8 | width: 100% !important;
9 | height: 100% !important;
10 | }
11 |
12 | .player video {
13 | border: none;
14 | }
--------------------------------------------------------------------------------
/app/components/VideoPlay/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from "react";
2 | import Player from "xgplayer";
3 | import styles from "./index.module.css";
4 |
5 | type Props = {
6 | className?: string;
7 | url: string;
8 | keyId?: string | number;
9 | current?: string | number;
10 | isShow?: Function;
11 | };
12 |
13 | const VideoPlay = (props: Props) => {
14 | const { className, url, keyId, current, isShow } = props;
15 |
16 | const playerRef = useRef(null);
17 |
18 | const init = () => {
19 | if (current !== keyId) return;
20 | if (playerRef.current) {
21 | playerRef.current.destroy();
22 | }
23 | playerRef.current = new Player({
24 | id: `video-play-${keyId}`,
25 | url: url,
26 | autoplay: false,
27 | volume: 1,
28 | muted: false,
29 | fluid: true,
30 | ignores: ["fullscreen"],
31 | cssFullscreen: true,
32 | rotateFullscreen: true,
33 | // 倍速播放
34 | playbackRate: [0.5, 0.75, 1, 1.5, 2],
35 | defaultPlaybackRate: 1,
36 | });
37 | };
38 |
39 | useEffect(() => {
40 | init();
41 | }, [url, keyId, current]);
42 |
43 | return (
44 |
45 | {(!isShow || (isShow && isShow(current, keyId))) && (
46 |
47 | )}
48 |
49 | );
50 | };
51 |
52 | export default VideoPlay;
53 |
--------------------------------------------------------------------------------
/app/components/VirtuallyItem/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect, useRef, useState } from "react";
3 | import { useSize, useGetState } from "ahooks";
4 | import { bindHandleScroll, removeScroll } from "@/utils/elementUtils";
5 | import style from "./virtuallyItem.module.css";
6 |
7 | const VirtuallyItem = (props) => {
8 | const size = useSize(() => document.querySelector("body"));
9 | // 用于记录当前元素的高度
10 | const [itemHeight, setItemHeight, getItemHeight] = useGetState(
11 | null
12 | );
13 | // 用户保存当前的元素
14 | const itemRef = useRef(null);
15 | // 判断当前元素是否在可视窗口
16 | const [isVisual, setIsVisual] = useState(true);
17 |
18 | const scrollCallback = () => {
19 | // get position relative to viewport
20 | const rect = itemRef.current?.getBoundingClientRect();
21 |
22 | if (!rect) return;
23 | const distanceFromTop = rect.top;
24 | const distanceFromBottom = rect.bottom;
25 | // 可视区域高度
26 | const viewportHeight =
27 | window.innerHeight || document.documentElement.clientHeight;
28 | if (
29 | (distanceFromTop > -200 && distanceFromTop < viewportHeight + 200) ||
30 | (distanceFromBottom > -200 && distanceFromBottom < viewportHeight + 200)
31 | ) {
32 | setIsVisual(true);
33 | } else {
34 | setIsVisual(false);
35 | }
36 | };
37 |
38 | useEffect(() => {
39 | bindHandleScroll(scrollCallback);
40 |
41 | return () => {
42 | removeScroll(scrollCallback);
43 | };
44 | }, []);
45 |
46 | useEffect(() => {
47 | if (itemRef.current && getItemHeight() !== itemRef.current?.offsetHeight) {
48 | setItemHeight(itemRef.current?.offsetHeight);
49 | }
50 | }, [isVisual]);
51 |
52 | useEffect(() => {
53 | setItemHeight(null);
54 | }, [size?.width]);
55 |
56 | return (
57 |
64 | {isVisual && props.children}
65 |
66 | );
67 | };
68 |
69 | export default VirtuallyItem;
70 |
--------------------------------------------------------------------------------
/app/components/VirtuallyItem/virtuallyItem.module.css:
--------------------------------------------------------------------------------
1 | .virtually_item{
2 | display: inline-block;
3 | width: 100%;
4 | }
--------------------------------------------------------------------------------
/app/components/WithLoading/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import Loading from "../../loading";
3 |
4 | const withLoading = (WrappedComponent) => {
5 | const WithLoadingComponent = (props) => {
6 | const [isLoading, setIsLoading] = useState(true);
7 |
8 | const handleLoaded = () => {
9 | setIsLoading(false);
10 | };
11 |
12 | return (
13 | <>
14 | {isLoading && }
15 |
20 | >
21 | );
22 | };
23 |
24 | // 设置 displayName 帮助调试
25 | const wrappedComponentName =
26 | WrappedComponent.displayName || WrappedComponent.name || "Component";
27 | WithLoadingComponent.displayName = `WithLoading(${wrappedComponentName})`;
28 |
29 | return WithLoadingComponent;
30 | };
31 |
32 | export default withLoading;
33 |
--------------------------------------------------------------------------------
/app/components/WithLoading/useChangeLoading.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { sessionSet, sessionGet } from "@utils/local";
3 |
4 | const useChangeLoading = (props) => {
5 | useEffect(() => {
6 | const isCache = sessionGet(`${props.name}_cache`);
7 |
8 | // 模拟一个异步加载过程
9 | const timer = setTimeout(
10 | () => {
11 | props.onLoaded();
12 | sessionSet(`${props.name}_cache`, true);
13 | },
14 | isCache ? 0 : 1500
15 | ); // 最少加载时间
16 |
17 | return () => clearTimeout(timer);
18 | }, []);
19 |
20 | return null;
21 | };
22 |
23 | export default useChangeLoading;
24 |
--------------------------------------------------------------------------------
/app/copyright-notice/copyrightNotice.module.css:
--------------------------------------------------------------------------------
1 | .copyright_notice {
2 | width: 100%;
3 | flex: 1;
4 | padding-top: 70px;
5 | background-color: var(--bg-w-pure);
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | }
10 |
11 | .title {
12 | padding: 36px;
13 | font-size: 24px;
14 | font-weight: 700;
15 | color: var(--b-alpha-80);
16 | text-align: center;
17 | }
18 |
19 | .content {
20 | width: 60%;
21 | max-width: 800px;
22 | padding-bottom: 24px;
23 | }
24 |
25 | .content .p {
26 | font-size: 15px;
27 | font-weight: 500;
28 | line-height: 1.6em;
29 | padding: 4px 0;
30 | color: var(--b-alpha-90);
31 | margin-bottom: 12px;
32 | }
33 |
34 | .footer {
35 | font-size: 16px;
36 | font-weight: 500;
37 | line-height: 1.6em;
38 | margin-top: 24px;
39 | color: var(--b-alpha-80);
40 | border: 1px solid var(--b-alpha-30);
41 | border-radius: 12px;
42 | padding: 24px;
43 | }
44 |
45 | .link {
46 | color: var(--color-blue);
47 | height: 23px;
48 | }
49 |
50 | .link_click {
51 | cursor: pointer;
52 | transition: 0.4s;
53 | }
54 |
55 | .link_click:hover {
56 | color: var(--color-blue-hover);
57 | }
58 |
59 | @media screen and (max-width: 800px) {
60 | .content{
61 | width: 90%;
62 | }
63 | }
--------------------------------------------------------------------------------
/app/copyright-notice/page.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import Link from "next/link";
3 | import React, { useEffect } from "react";
4 | import {
5 | addNavItemStyle,
6 | bindHandleScroll,
7 | removeNavItemStyle,
8 | removeScroll,
9 | } from "@/utils/elementUtils";
10 | import withLoading from "@components/WithLoading";
11 | import useChangeLoading from "@components/WithLoading/useChangeLoading";
12 | import style from "./copyrightNotice.module.css";
13 |
14 | const CopyrightNotice = (props) => {
15 | useEffect(() => {
16 | addNavItemStyle();
17 | bindHandleScroll();
18 |
19 | return () => {
20 | removeNavItemStyle();
21 | removeScroll();
22 | };
23 | }, []);
24 |
25 | useChangeLoading({ ...props, name: "copyright_notice" });
26 |
27 | return (
28 |
33 |
版权声明
34 |
35 |
36 | 1、本博客(域名为wp-boke.work)的所有内容(包括但不限于文字、图片、音频、视频等),除特别注明外,其余均由shimmer创作或原创,版权归shimmer个人所有。
37 |
38 |
39 | 2、未经本人授权,任何人或机构不得复制、转载、摘编或以任何其他形式使用本站内容。如需转载,请在摘要或正文部分注明出处。
40 |
41 |
42 | 3、本博客允许授权使用部分文案,需注明原作者及网址,如用于商业用途,需与原作者确认。任何未授权使用内容的行为都将被视为侵权行为,shimmer保留追究法律责任的权利。
43 |
44 |
45 | 4、本站不承担用户因使用本站所提供的服务而产生的任何直接、间接或者连带的责任和赔偿。
46 |
47 |
48 | 5、本声明的解释权及修改权归shimmer所有,并保留随时更新网站内容和服务的权利,在不做事先通知的情况下,修改本声明产生效力。
49 |
50 |
51 | 如对本博客版权声明有任何疑问或建议,请联系shimmer的
52 |
58 | 电子邮箱
59 |
60 | 或通过本博客页面上的相关联系方式进行联系。
61 |
62 |
63 |
64 | );
65 | };
66 |
67 | export default withLoading(CopyrightNotice);
68 |
--------------------------------------------------------------------------------
/app/disclaimers/disclaimers.module.css:
--------------------------------------------------------------------------------
1 | .disclaimers {
2 | width: 100%;
3 | flex: 1;
4 | padding-top: 70px;
5 | background-color: var(--bg-w-pure);
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | }
10 |
11 | .title {
12 | padding: 36px;
13 | font-size: 24px;
14 | font-weight: 700;
15 | color: var(--b-alpha-80);
16 | text-align: center;
17 | }
18 |
19 | .content {
20 | width: 60%;
21 | max-width: 800px;
22 | padding-bottom: 24px;
23 | }
24 |
25 | .content .p {
26 | font-size: 14px;
27 | font-weight: 400;
28 | line-height: 1.6em;
29 | padding: 4px 0;
30 | color: var(--b-alpha-70);
31 | margin-bottom: 12px;
32 | }
33 |
34 | .content_title {
35 | font-size: 14px;
36 | font-weight: 500;
37 | color: var(--b-alpha-60);
38 | border-left: 3px solid var(--b-alpha-50);
39 | padding-left: 12px;
40 | margin-bottom: 24px;
41 | }
42 |
43 | .desc_title {
44 | font-size: 16px;
45 | font-weight: 500;
46 | color: var(--b-alpha-90);
47 | padding-bottom: 8px;
48 | }
49 |
50 | .footer {
51 | font-size: 16px;
52 | font-weight: 500;
53 | line-height: 1.6em;
54 | margin-top: 24px;
55 | color: var(--b-alpha-80);
56 | border: 1px solid var(--b-alpha-30);
57 | border-radius: 12px;
58 | padding: 24px;
59 | }
60 |
61 | .link {
62 | color: var(--color-blue);
63 | height: 23px;
64 | }
65 |
66 | .link_click {
67 | cursor: pointer;
68 | transition: 0.4s;
69 | }
70 |
71 | .link_click:hover {
72 | color: var(--color-blue-hover);
73 | }
74 |
75 | @media screen and (max-width: 800px) {
76 | .content{
77 | width: 90%;
78 | }
79 | }
--------------------------------------------------------------------------------
/app/disclaimers/page.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import Link from "next/link";
3 | import React, { useEffect } from "react";
4 | import {
5 | addNavItemStyle,
6 | bindHandleScroll,
7 | removeNavItemStyle,
8 | removeScroll,
9 | } from "@/utils/elementUtils";
10 | import withLoading from "@components/WithLoading";
11 | import useChangeLoading from "@components/WithLoading/useChangeLoading";
12 | import style from "./disclaimers.module.css";
13 |
14 | const Disclaimers = (props) => {
15 | useEffect(() => {
16 | addNavItemStyle();
17 | bindHandleScroll();
18 |
19 | return () => {
20 | removeNavItemStyle();
21 | removeScroll();
22 | };
23 | }, []);
24 |
25 | useChangeLoading({ ...props, name: "disclaimers" });
26 |
27 | return (
28 |
31 |
免责声明
32 |
33 |
34 | 欢迎访问我的个人博客,本站提供的所有信息均在善意和合理的基础上发布和使用,但本人无法保证信息的准确性、完整性和实时性。
35 |
36 |
37 |
信息准确性和完整性
38 | 本人尽力确保本站提供的所有信息、内容和资料的准确性、完整性和实时性,但本人无法保证其完全正确、完整和即时更新。本人不能保证所有信息和内容的精确性、可靠性和适用性,因此任何使用本站提供的信息造成的后果和损失由用户自行承担。
39 |
40 |
41 |
知识产权
42 | 本人发布的所有信息、内容和资料,包括文字、图片、图形、标识、标志、图表和编程代码等,均受到相关的知识产权法律的保护。未经本人的明确授权,任何人不得使用、复制、传播、更改或销售本人的信息和内容。
43 |
44 |
45 |
免责条款
46 | 本人不对因本站内容和服务的误导性或不准确性而导致的任何直接、间接、偶然、特殊或结果性损失负责,并且对于本站上其他网站的链接或外部资源的内容、广告、产品或其他资料不负任何责任。
47 |
48 |
49 |
合法性
50 | 本人承诺所有内容都是合法的,并未侵犯他人的知识产权或其他合法权利。对于任何侵权行为或侵犯隐私和安全的行为,本人不负责并保留追究法律责任的权利。
51 |
52 |
53 |
变更和更新
54 | 本人保留对本免责声明进行随时变更、更新和修改的权利。本人建议您在访问和使用本人的博客时定期查看本免责声明并了解最新更新。
55 |
56 |
57 | 如对本博客免责声明有任何疑问或建议,请联系shimmer的
58 |
64 | 电子邮箱
65 |
66 | 或通过本博客页面上的相关联系方式进行联系。
67 |
68 |
69 |
70 | );
71 | };
72 |
73 | export default withLoading(Disclaimers);
74 |
--------------------------------------------------------------------------------
/app/error.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Link from "next/link";
4 | import { useEffect } from "react";
5 | import { addLayoutNavStyle, removeLayoutNavStyle } from "@utils/elementUtils";
6 | import styles from "@styles/error404.module.css";
7 |
8 | export default function Custom500({
9 | error,
10 | reset,
11 | }: {
12 | error: Error & { digest?: string };
13 | reset: () => void;
14 | }) {
15 | useEffect(() => {
16 | addLayoutNavStyle();
17 |
18 | return () => {
19 | removeLayoutNavStyle();
20 | };
21 | }, []);
22 |
23 | return (
24 |
25 |
26 |
500
27 |
28 | 很抱歉,服务器发生了预期之外的错误,请联系管理员修复。
29 |
30 |
31 | Sorry, the server encountered an unexpected error. Please contact the
32 | administrator to fix it.
33 |
34 |
35 |
36 |
37 | 刷新
38 |
39 |
40 | 回到首页
41 |
42 |
47 | 联系我
48 |
49 |
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/app/friendly-links/friendlyLinks.module.css:
--------------------------------------------------------------------------------
1 | .friendly_links {
2 | width: 100%;
3 | flex: 1;
4 | padding-top: 70px;
5 | background-color: var(--bg-w-pure);
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | }
10 |
11 | .title {
12 | padding: 36px;
13 | font-size: 24px;
14 | font-weight: 700;
15 | color: var(--b-alpha-80);
16 | text-align: center;
17 | }
18 |
19 | .demo {
20 | width: 60%;
21 | background-color: var(--color-blue-bg);
22 | padding: 16px;
23 | border-radius: 8px;
24 | }
25 |
26 | .demo_item {
27 | font-size: 14px;
28 | padding: 4px 0;
29 | color: var(--b-alpha-80);
30 | }
31 |
32 | .desc {
33 | width: 60%;
34 | font-size: 16px;
35 | font-weight: 700;
36 | padding: 16px;
37 | margin: 24px 0;
38 | background-color: var(--purple-border);
39 | color: var(--b-alpha-70);
40 | border-radius: 8px;
41 | }
42 |
43 | .content {
44 | width: 60%;
45 | border-radius: 8px;
46 | display: grid;
47 | grid-gap: 8px;
48 | padding-bottom: 16px;
49 | gap: 12px;
50 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
51 | }
52 |
53 | .blog_item {
54 | padding: 24px;
55 | border-radius: 8px;
56 | display: flex;
57 | align-items: flex-start;
58 | transition: 0.3s;
59 | }
60 |
61 | .blog_item:hover {
62 | /* transform: translateY(-5px) scale(1.05, 1.05); */
63 | box-shadow: 0 1px 10px 5px var(--b-alpha-30);
64 | z-index: 1;
65 | }
66 |
67 | .blog_item:hover .blog_item_logo {
68 | transform: rotate(360deg);
69 | }
70 |
71 | .blog_item_logo {
72 | width: 50px;
73 | height: 50px;
74 | border-radius: 50%;
75 | margin-right: 16px;
76 | flex-shrink: 0;
77 | transition: 0.3s;
78 | }
79 |
80 | .blog_item_title {
81 | color: var(--b-alpha-80);
82 | font-size: 16px;
83 | font-weight: 700;
84 | padding-bottom: 8px;
85 | }
86 |
87 | .blog_item_desc {
88 | color: var(--b-alpha-70);
89 | font-size: 14px;
90 | }
91 |
92 | .comment {
93 | width: 60%;
94 | }
95 |
96 | @media screen and (min-width: 1200px) {
97 | .demo,
98 | .desc,
99 | .content,
100 | .comment {
101 | width: 80%;
102 | max-width: 800px;
103 | }
104 | }
105 |
106 | @media screen and (max-width: 1300px) {
107 | .demo,
108 | .desc,
109 | .content,
110 | .comment {
111 | width: 75%;
112 | max-width: 800px;
113 | }
114 | }
115 |
116 | @media screen and (max-width: 1100px) {
117 | .demo,
118 | .desc,
119 | .content,
120 | .comment {
121 | width: 80%;
122 | }
123 | }
124 |
125 | @media screen and (max-width: 900px) {
126 | .demo,
127 | .desc,
128 | .content,
129 | .comment {
130 | width: 90%;
131 | }
132 | }
133 |
134 | @media screen and (max-width: 600px) {
135 | .demo,
136 | .desc,
137 | .content {
138 | width: 90%;
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/app/friendly-links/page.tsx:
--------------------------------------------------------------------------------
1 | import PostClient from './post-client'
2 | import type { Metadata } from 'next'
3 | import getData from "@/utils/httpClient/request";
4 |
5 | export const metadata: Metadata = {
6 | title: '友情链接',
7 | description: '存放友链地址',
8 | }
9 |
10 | const FriendlyLinks = async () => {
11 | const { data } = await getData({type: 'all_user_friendly_Links'});
12 |
13 | return ;
14 | };
15 |
16 | export default FriendlyLinks;
17 |
--------------------------------------------------------------------------------
/app/friendly-links/post-client.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Link from "next/link";
3 | import React, { useContext, useEffect } from "react";
4 | import {
5 | addNavItemStyle,
6 | bindHandleScroll,
7 | removeNavItemStyle,
8 | removeScroll,
9 | } from "@/utils/elementUtils";
10 | import { getRandomColor } from "@utils/dataUtils";
11 | import { LayoutContext } from "@/store/layoutStore";
12 | import Comment from "@components/Comment";
13 | import withLoading from "@components/WithLoading";
14 | import useChangeLoading from "@components/WithLoading/useChangeLoading";
15 | import style from "./friendlyLinks.module.css";
16 |
17 | const FriendlyLinks = (props) => {
18 | const { theme } = useContext(LayoutContext);
19 |
20 | useEffect(() => {
21 | addNavItemStyle();
22 | bindHandleScroll();
23 |
24 | return () => {
25 | removeNavItemStyle();
26 | removeScroll();
27 | };
28 | }, []);
29 |
30 | useChangeLoading({ ...props, name: "friendly_links" });
31 |
32 | return (
33 |
38 |
友情链接
39 |
40 |
网站名:shimmer
41 |
42 | 站点头像:https://wp-boke.work/images/logo.png
43 |
44 |
网站链接:https://wp-boke.work
45 |
46 | 网站描述:欲买桂花同载酒,终不似,少年游。
47 |
48 |
联系邮箱:webwp0403@163.com
49 |
50 |
不定期清理失效网站,拒绝无效互链。
51 |
52 | {props?.data?.map((v) => (
53 |
64 |

65 |
66 |
{v.title}
67 |
{v.desc}
68 |
69 |
70 | ))}
71 |
72 |
73 |
74 |
75 |
76 | );
77 | };
78 |
79 | export default withLoading(FriendlyLinks);
80 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Viewport } from "next";
3 | import { Analytics } from "@vercel/analytics/react";
4 | import { SpeedInsights } from "@vercel/speed-insights/next";
5 | import { LayoutContextProvider } from "@store/layoutStore";
6 | import NavBar from "@components/NavBar";
7 | import Footer from "@components/Footer";
8 | import "./styles/globals.css";
9 |
10 | export const metadata: Metadata = {
11 | title: "shimmer的博客",
12 | description: "shimmer的个人博客站,旨在记录生活,分享知识",
13 | keywords: ["shimmer博客", "shimmer的博客", "shimmer", "博客", "个人博客"],
14 | authors: [{ name: "shimmer", url: "https://wp-boke.work/about" }],
15 | };
16 |
17 | export const viewport: Viewport = {
18 | width: "device-width",
19 | initialScale: 1,
20 | maximumScale: 1,
21 | userScalable: false,
22 | };
23 |
24 | export default function RootLayout({
25 | children,
26 | }: {
27 | children: React.ReactNode;
28 | }) {
29 | return (
30 |
31 |
32 |
33 |
34 | {children}
35 |
36 |
37 |
38 |
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/app/loading.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | const LoadingCom = () => {
3 | return (
4 |
5 |
6 |
7 |
8 | S
9 | H
10 | I
11 | M
12 | M
13 | E
14 | R
15 |
16 |
17 | );
18 | };
19 |
20 | export default LoadingCom;
21 |
--------------------------------------------------------------------------------
/app/more/more.module.css:
--------------------------------------------------------------------------------
1 | .more {
2 | width: 100%;
3 | padding-top: 70px;
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | background-color: var(--bg-w-pure);
8 | flex: 1;
9 | }
10 |
11 | .content {
12 | margin: 0 auto;
13 | padding: 24px;
14 | display: flex;
15 | flex-wrap: wrap;
16 | }
17 |
18 | .item {
19 | width: calc(50% - 48px);
20 | height: 200px;
21 | margin: 24px;
22 | background-color: var(--b-alpha-10);
23 | padding: 24px;
24 | display: flex;
25 | flex-direction: column;
26 | border-radius: 8px;
27 | transition: 0.25s;
28 | }
29 |
30 | .item:hover {
31 | box-shadow: 0 0 10px 5px var(--b-alpha-10);
32 | }
33 |
34 | .item_title {
35 | font-size: 24px;
36 | font-weight: 500;
37 | color: var(--b-alpha);
38 | height: 30px;
39 | display: flex;
40 | align-items: center;
41 | }
42 |
43 | .item_desc {
44 | flex: 1;
45 | padding: 12px 0;
46 | color: var(--b-alpha-80);
47 | line-height: 1.6em;
48 | margin-bottom: 8px;
49 | overflow: hidden;
50 | text-overflow: ellipsis;
51 | display: -webkit-box;
52 | -webkit-line-clamp: 2;
53 | -webkit-box-orient: vertical;
54 | }
55 |
56 | .item_footer {
57 | border-top: 1px solid var(--b-alpha-20);
58 | display: flex;
59 | align-items: center;
60 | padding-top: 24px;
61 | }
62 |
63 | .item_btn {
64 | font-weight: 700;
65 | color: var(--b-alpha-70);
66 | cursor: pointer;
67 | }
68 |
69 | .item_btn:hover {
70 | font-weight: 700;
71 | color: var(--b-alpha);
72 | }
73 |
74 | @media screen and (min-width: 1200px) {
75 | .content {
76 | width: 1200px;
77 | }
78 | }
79 |
80 | @media screen and (max-width: 1200px) {
81 | .content {
82 | width: 90%;
83 | }
84 |
85 | .item {
86 | width: calc(50% - 48px);
87 | }
88 | }
89 |
90 | @media screen and (max-width: 850px) {
91 | .content {
92 | width: 85%;
93 | }
94 | .item {
95 | width: 100%;
96 | height: auto;
97 | max-height: 200px;
98 | margin: 24px 0;
99 | }
100 | }
101 |
102 | @media screen and (max-width: 400px) {
103 | .content {
104 | width: 90%;
105 | }
106 | .item {
107 | width: 100%;
108 | margin: 24px 0;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/app/more/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Link from "next/link";
3 | import React, { useEffect } from "react";
4 | import {
5 | addNavItemStyle,
6 | bindHandleScroll,
7 | removeNavItemStyle,
8 | removeScroll,
9 | } from "@utils/elementUtils";
10 | import { moreList } from "@/utils/dict";
11 | import withLoading from "@components/WithLoading";
12 | import useChangeLoading from "@components/WithLoading/useChangeLoading";
13 | import style from "./more.module.css";
14 |
15 | const More = (props) => {
16 | useEffect(() => {
17 | addNavItemStyle();
18 | bindHandleScroll();
19 |
20 | return () => {
21 | removeNavItemStyle();
22 | removeScroll();
23 | };
24 | }, []);
25 |
26 | useChangeLoading({ ...props, name: "more" });
27 |
28 | const randerItem = (v) => {
29 | return (
30 |
31 |
{v.title}
32 |
{v.desc}
33 |
34 |
39 | Come Here
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | return (
47 |
48 |
{moreList?.map((v) => randerItem(v))}
49 |
50 | );
51 | };
52 |
53 | export default withLoading(More);
54 |
--------------------------------------------------------------------------------
/app/news/news.module.css:
--------------------------------------------------------------------------------
1 | .news{
2 | width: 100%;
3 | flex: 1;
4 | padding-top: 70px;
5 | background-color: var(--bg-w-pure);
6 | }
7 |
8 | .news_content{
9 | width: 100%;
10 | padding: 24px;
11 | display: grid;
12 | gap: 24px;
13 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
14 | }
15 |
16 | .news_type_box{
17 | width: 100%;
18 | height: 500px;
19 | display: flex;
20 | flex-direction: column;
21 | border-radius: 12px;
22 | overflow: hidden;
23 | background-color: var(--w-alpha);
24 | }
25 |
26 | .news_type_box_content{
27 | flex: 1;
28 | overflow-y: auto;
29 | padding: 0 24px 24px;
30 | border: 1px solid var(--purple-primary);
31 | border-top: none;
32 | border-radius: 0 0 12px 12px;
33 | }
34 |
35 | .news_type_title{
36 | width: 100%;
37 | height: 48px;
38 | display: flex;
39 | align-items: center;
40 | justify-content: center;
41 | padding-left: 12px;
42 | background-color: var(--purple-primary);
43 | color: var(--w-alpha);
44 | font-weight: 500;
45 | font-size: 18px;
46 | }
47 |
48 | .news_item{
49 | display: flex;
50 | align-items: center;
51 | }
52 |
53 | .news_item_link{
54 | min-width: 10px;
55 | flex: 1;
56 | white-space: nowrap;
57 | overflow: hidden;
58 | text-overflow: ellipsis;
59 | height: 32px;
60 | line-height: 32px;
61 | color: var(--purple-primary);
62 | font-weight: 500;
63 | }
64 |
65 | .news_item_link:hover{
66 | text-decoration: underline;
67 | color: var(--purple-primary-active);
68 | }
69 |
70 | .news_item_hot{
71 | display: flex;
72 | align-items: center;
73 | color: var(--b-alpha-80);
74 | }
75 |
76 | .news_item_hot .icon{
77 | font-size: 16px;
78 | margin-right: 4px;
79 | }
--------------------------------------------------------------------------------
/app/news/newsItem.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useState, useEffect } from "react";
3 | import Link from "next/link";
4 | import { Spin } from "antd";
5 | import getDataApi from "@utils/httpClient/request";
6 | import SysIcon from "@components/SysIcon";
7 | import { newsApiType } from "@utils/httpClient/apis/news";
8 | import styles from "./news.module.css";
9 |
10 | function isPC() {
11 | return window.innerWidth >= 1000; // 假设宽度大于等于 1024 像素为 PC
12 | }
13 |
14 | const NewsItem = (props: { apikey: keyof typeof newsApiType }) => {
15 | const { apikey } = props;
16 |
17 | const [list, setList] = useState([]);
18 | const [loading, setLoading] = useState(true);
19 |
20 | useEffect(() => {
21 | const getData = async () => {
22 | setLoading(true);
23 | const { data } = await getDataApi({ type: apikey });
24 | setList(data || []);
25 | setLoading(false);
26 | console.log(data);
27 | };
28 | getData();
29 | }, [apikey]);
30 |
31 | return (
32 |
33 |
{newsApiType[apikey]}
34 |
40 | {list?.map((item) => (
41 |
42 |
46 | {item.title}
47 |
48 | {item.hot && (
49 |
50 |
51 | {item.hot}
52 |
53 | )}
54 |
55 | ))}
56 |
57 |
58 | );
59 | };
60 |
61 | export default NewsItem;
62 |
--------------------------------------------------------------------------------
/app/news/page.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PostClient from "./post-client";
3 |
4 | const News = () => {
5 | return ;
6 | };
7 |
8 | export default News;
9 |
--------------------------------------------------------------------------------
/app/news/post-client.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect } from "react";
3 | import {
4 | addNavItemStyle,
5 | bindHandleScroll,
6 | removeNavItemStyle,
7 | removeScroll,
8 | } from "@utils/elementUtils";
9 | import { newsApiType } from "@utils/httpClient/apis/news";
10 | import withLoading from "@components/WithLoading";
11 | import useChangeLoading from "@components/WithLoading/useChangeLoading";
12 | import NewsItem from "./newsItem";
13 | import styles from "./news.module.css";
14 |
15 | const PostClient = (props) => {
16 | useEffect(() => {
17 | addNavItemStyle();
18 | bindHandleScroll();
19 |
20 | return () => {
21 | removeNavItemStyle();
22 | removeScroll();
23 | };
24 | }, []);
25 |
26 | useChangeLoading({ ...props, name: "news" });
27 |
28 | return (
29 |
30 |
31 | {Object.keys(newsApiType).map((item) => (
32 |
33 | ))}
34 |
35 |
36 | );
37 | };
38 |
39 | export default withLoading(PostClient);
40 |
--------------------------------------------------------------------------------
/app/not-found.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Link from "next/link";
3 | import { useEffect } from "react";
4 | import { addLayoutNavStyle, removeLayoutNavStyle } from "@utils/elementUtils";
5 | import styles from "@styles/error404.module.css";
6 |
7 | export default function Custom404() {
8 | useEffect(() => {
9 | addLayoutNavStyle();
10 |
11 | return () => {
12 | removeLayoutNavStyle();
13 | };
14 | }, []);
15 |
16 | return (
17 |
18 |
19 |
404
20 |
21 | 很抱歉,您访问的页面资源不存在。请您确认网址无误后重试。
22 |
23 |
24 | Sorry, the page resource you visited does not exist. Please confirm
25 | that the URL is correct and try again.
26 |
27 |
28 |
29 |
30 | 回到首页
31 |
32 |
37 | 联系我
38 |
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Image from "next/image";
4 | import Link from "next/link";
5 | import { useEffect, useRef } from "react";
6 | import Typed from "typed.js";
7 | import { bindHandleScroll, removeScroll } from "@utils/elementUtils";
8 | import { scrollTo } from "@utils/element";
9 | import SysIcon from "@components/SysIcon";
10 | import bgImgLight from "@public/images/bg00005.jpeg";
11 | import { timeAixsList } from "@utils/dict";
12 | import { loadingImag } from "@utils/dataImage";
13 | import styles from "@styles/home.module.css";
14 |
15 | export default function Home() {
16 | const typeTarget = useRef(null);
17 | const aboutDom = useRef(null);
18 |
19 | const goAbout = () => {
20 | const aboutTop = aboutDom.current.offsetTop;
21 | scrollTo(aboutTop, {
22 | getContainer: () => document.body || window,
23 | });
24 | };
25 |
26 | useEffect(() => {
27 | bindHandleScroll();
28 | const typed = new Typed(typeTarget.current, {
29 | strings: [
30 | "年少时,春风得意马蹄疾,不信人间有别离。",
31 | "收余恨、免娇嗔、且自新、改性情、休恋逝水、苦海回身、早悟兰因。",
32 | ],
33 | typeSpeed: 60,
34 | backSpeed: 40,
35 | loop: true,
36 | loopCount: Infinity,
37 | autoInsertCss: false,
38 | backDelay: 2000,
39 | showCursor: false,
40 | });
41 |
42 | return () => {
43 | removeScroll();
44 | typed.destroy();
45 | };
46 | }, []);
47 |
48 | return (
49 |
50 |
51 |
59 |
60 |
61 |
62 |
世人万千,再难遇我
63 |
66 |
67 |
72 |
73 |
74 | {/*
75 |
BLOG GROWTH RECORD
76 |
更多的作品
77 |
78 |
*/}
79 |
80 |
GROWTH ABILITY
81 |
博客项目更多功能入口
82 |
83 |
84 |
96 |
97 | 壁 纸
98 |
99 |
100 |
101 |
113 |
114 | 热 点
115 |
116 |
117 |
118 |
119 |
120 |
GROWTH RECORD
121 |
「 左右滑动查看 」
122 |
123 |
124 |
125 | {timeAixsList?.map((v) => (
126 |
127 |
{v.time}
128 |
{v.title}
129 |
130 | ))}
131 |
132 |
GROWING...
133 |
COMING SOON
134 |
135 |
136 |
137 |
138 |
139 |
140 | );
141 | }
142 |
--------------------------------------------------------------------------------
/app/photography/Photography.module.css:
--------------------------------------------------------------------------------
1 | .photography {
2 | width: 100%;
3 | flex: 1;
4 | padding-top: 70px;
5 | background-color: var(--bg-w-pure);
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | }
10 |
11 | .photography_no{
12 | position: absolute;
13 | left: 0;
14 | top: 0;
15 | z-index: -10;
16 | opacity: 0;
17 | }
18 |
19 | .photography_content {
20 | width: 80%;
21 | max-width: 1200px;
22 | flex: 1;
23 | min-height: 300px;
24 | margin: 0 auto;
25 | display: flex;
26 | flex-direction: column;
27 | align-items: center;
28 | overflow: hidden;
29 | }
30 |
31 | .photography_content :global .ant-spin-nested-loading {
32 | width: 100%;
33 | height: 100%;
34 | }
35 |
36 | .photography_content :global .ant-spin-nested-loading>div>.ant-spin{
37 | max-height: none;
38 | }
39 |
40 | .photography_content :global .ant-spin-nested-loading .ant-spin-blur::after{
41 | display: none;
42 | }
43 |
44 | .photography_content :global .ant-spin-nested-loading .ant-spin-blur{
45 | width: 100%;
46 | height: 100%;
47 | }
48 |
49 | .photography_content :global .ant-spin-nested-loading .ant-spin-dot-spin{
50 | font-size: 20px;
51 | }
52 |
53 | .photography_content :global .ant-spin-nested-loading .ant-spin-dot-spin .ant-spin-dot-item{
54 | width: 60px;
55 | height: 60px;
56 | }
57 |
58 | .photography_item {
59 | width: 100%;
60 | display: flex;
61 | flex-direction: column;
62 | padding-bottom: 32px;
63 | }
64 |
65 | .photography_item:not(:last-child) {
66 | border-bottom: 1px var(--b-alpha-10) solid;
67 | }
68 |
69 | .photography_item:not(:first-child) {
70 | padding-top: 24px;
71 | }
72 |
73 | .photography_item .header {
74 | display: flex;
75 | flex-direction: column;
76 | padding: 14px 4px;
77 | }
78 |
79 | .photography_item .header * {
80 | white-space: nowrap;
81 | }
82 |
83 | .photography_item .header .header_top {
84 | display: flex;
85 | align-items: center;
86 | flex-wrap: wrap;
87 | padding-bottom: 8px;
88 | }
89 |
90 | .photography_item .header .header_top .title {
91 | font-size: 18px;
92 | color: var(--b-alpha-90);
93 | font-weight: 500;
94 | padding-right: 20px;
95 | }
96 |
97 | .photography_item .header .header_top .place {
98 | font-size: 12px;
99 | color: var(--b-alpha-60);
100 | padding: 0 4px;
101 | border-radius: 4px;
102 | background-color: var(--purple-primary-hover);
103 | margin-right: 8px;
104 | line-height: 16px;
105 | }
106 |
107 | .photography_item .header .header_top .time {
108 | font-size: 12px;
109 | color: var(--b-alpha-60);
110 | }
111 |
112 | .photography_item .header .header_top .username {
113 | font-size: 14px;
114 | color: var(--b-alpha-60);
115 | flex: 1;
116 | display: flex;
117 | align-items: center;
118 | justify-content: flex-end;
119 | padding-right: 3px;
120 | }
121 |
122 | .photography_item .header .header_top .username span {
123 | font-size: 14px;
124 | color: var(--b-alpha-90);
125 | }
126 |
127 | .photography_item .header .desc {
128 | font-size: 14px;
129 | color: var(--b-alpha-70);
130 | line-height: 24px;
131 | padding-right: 3px;
132 | }
133 |
134 | .photography_image {
135 | object-fit: cover;
136 | padding: 4px;
137 | }
138 |
139 | .content {
140 | display: grid;
141 | grid-template-columns: repeat(3, 1fr);
142 | }
143 |
144 | .loading_box {
145 | display: flex;
146 | align-items: center;
147 | justify-content: center;
148 | width: 100%;
149 | height: 100%;
150 | color: var(--b-alpha-90);
151 | opacity: 0;
152 | }
153 |
154 | .pagination {
155 | width: 100%;
156 | height: 80px;
157 | display: flex;
158 | align-items: center;
159 | justify-content: center;
160 | }
161 |
162 | @media screen and (max-width: 1000px) {
163 | .photography_content {
164 | width: 85%;
165 | }
166 | }
167 |
168 | @media screen and (max-width: 800px) {
169 | .photography_content {
170 | width: 90%;
171 | }
172 | }
173 |
174 | @media screen and (max-width: 600px) {
175 | .photography_item .header {
176 | padding: 14px 2px;
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/app/photography/page.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PostClient from "./post-client";
3 |
4 | const page = () => {
5 | return ;
6 | };
7 |
8 | export default page;
9 |
--------------------------------------------------------------------------------
/app/photography/post-client.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useGetState, useDebounceEffect, useMount, useSize } from "ahooks";
3 | import { Image, Spin } from "antd";
4 | import React, { useEffect, useRef, useState } from "react";
5 | import getDataApi from "@/utils/httpClient/request";
6 | import { formatDate } from "@utils/dataUtils";
7 | import {
8 | addNavItemStyle,
9 | bindHandleScroll,
10 | removeNavItemStyle,
11 | removeScroll,
12 | routeChangeComplete,
13 | } from "@utils/elementUtils";
14 | import LazyCom from "@components/LazyCom";
15 | import PagerComponent from "@components/PagerComponent";
16 | import withLoading from "@components/WithLoading";
17 | import useChangeLoading from "@components/WithLoading/useChangeLoading";
18 | import style from "./Photography.module.css";
19 |
20 | const Photography = (props) => {
21 | const dom = useRef(null);
22 | const content = useRef(null);
23 | // 列表
24 | const [data, setData] = useState([]);
25 | // 当前页
26 | const [page, setPage, getPage] = useGetState(1);
27 | // 每页条数
28 | const [page_size, setPageSize] = useState(8);
29 | const [loading, setLoading] = useGetState(true);
30 | const [total, setTotal] = useState(0);
31 |
32 | const getData = async () => {
33 | setLoading(true);
34 | const posts = await getDataApi({
35 | type: "all_photography_List",
36 | params: { page: getPage(), page_size: page_size },
37 | });
38 | setData(posts.data);
39 | getPage() === 1 && setTotal(posts.meta.total);
40 | setLoading(false);
41 | };
42 |
43 | useMount(() => {
44 | getData();
45 | });
46 |
47 | // 获取列表数据
48 | useDebounceEffect(
49 | () => {
50 | getData();
51 | },
52 | [page],
53 | {
54 | wait: 800,
55 | }
56 | );
57 |
58 | useEffect(() => {
59 | addNavItemStyle();
60 | bindHandleScroll();
61 |
62 | return () => {
63 | removeNavItemStyle();
64 | removeScroll();
65 | };
66 | }, []);
67 |
68 | useChangeLoading({ ...props, name: "photography" });
69 |
70 | const size = useSize(dom.current);
71 |
72 | const randerItem = (v) => (
73 |
79 |
80 |
81 |
{v.title}
82 | {v.place &&
{v.place}
}
83 |
84 | ({formatDate(v.create_time, "yyyy-MM-dd HH:ss")})
85 |
86 |
87 | 上传人:{v.userInfo?.name}
88 |
89 |
90 | {v.desc &&
{v.desc}
}
91 |
92 | {!props.loading && (
93 |
94 |
95 | {v?.imgs?.map((v1) => (
96 |
102 | ))}
103 |
104 |
105 | )}
106 |
107 | );
108 |
109 | return (
110 |
116 |
117 |
118 | {data && Boolean(data?.length) && data?.map((v) => randerItem(v))}
119 | {(!data || !data?.length) && (
120 | 暂无数据
121 | )}
122 |
123 |
124 | {Boolean(total) && (
125 |
126 |
{
131 | setPage(v);
132 | routeChangeComplete();
133 | }}
134 | />
135 |
136 | )}
137 |
138 | );
139 | };
140 |
141 | export default withLoading(Photography);
142 |
--------------------------------------------------------------------------------
/app/resume/resume.module.css:
--------------------------------------------------------------------------------
1 | .resume {
2 | width: 100%;
3 | flex: 1;
4 | padding-top: 70px;
5 | background-color: var(--bg-w-pure);
6 | }
7 |
8 | .resume_content {
9 | width: 50%;
10 | max-width: 750px;
11 | margin: 0 auto;
12 | padding-bottom: 32px;
13 | }
14 |
15 | .title {
16 | padding: 24px 0;
17 | font-size: 24px;
18 | font-weight: 700;
19 | color: var(--b-alpha-80);
20 | display: flex;
21 | align-items: center;
22 | justify-content: center;
23 | }
24 |
25 | .main {
26 | padding: 24px 0 0;
27 | }
28 |
29 | .main_title {
30 | font-size: 20px;
31 | color: var(--b-alpha-80);
32 | font-weight: 700;
33 | padding: 12px 0;
34 | border-bottom: 2px solid var(--b-alpha-20);
35 | display: flex;
36 | align-items: center;
37 | }
38 |
39 | .border {
40 | border-bottom: 1px solid var(--b-alpha-10);
41 | }
42 |
43 | .main_item {
44 | padding: 16px 0;
45 | overflow: hidden;
46 | transition: 0.6s ease-in-out;
47 | }
48 |
49 | .main_item > * {
50 | transition: 1s;
51 | }
52 |
53 | .main_item_expand {
54 | max-height: 2000px;
55 | }
56 |
57 | .main_item_retract {
58 | max-height: 64px;
59 | }
60 |
61 | .main_item > li {
62 | font-size: 15px;
63 | font-weight: 500;
64 | color: var(--b-alpha-80);
65 | line-height: 1.6em;
66 | }
67 |
68 | .main_item_desc {
69 | font-size: 15px;
70 | text-indent: 2em;
71 | line-height: 1.6em;
72 | padding: 16px 0;
73 | color: var(--b-alpha-80);
74 | font-weight: 500;
75 | }
76 |
77 | .main_item_title,
78 | .main_item_info {
79 | display: flex;
80 | align-items: center;
81 | justify-content: space-between;
82 | padding-bottom: 6px;
83 | }
84 |
85 | .company_name,
86 | .project_name {
87 | font-size: 18px;
88 | font-weight: 500;
89 | color: var(--b-alpha-80);
90 | display: flex;
91 | align-items: center;
92 | }
93 |
94 | .education_item,
95 | .work_time,
96 | .role,
97 | .place,
98 | .project_time {
99 | font-size: 15px;
100 | color: var(--b-alpha-90);
101 | font-weight: 500;
102 | }
103 |
104 | .main_item_content {
105 | font-size: 15px;
106 | color: var(--b-alpha-90);
107 | line-height: 2em;
108 | /* font-weight: 500; */
109 | }
110 |
111 | .project_desc {
112 | text-indent: 2em;
113 | color: var(--b-alpha-80);
114 | font-weight: 500;
115 | line-height: 1.6em;
116 | font-size: 15px;
117 | opacity: 1;
118 | }
119 |
120 | .box_none {
121 | opacity: 0;
122 | }
123 |
124 | .main_item_lectotype {
125 | display: flex;
126 | align-items: center;
127 | font-size: 15px;
128 | font-weight: 500;
129 | color: var(--b-alpha-80);
130 | padding: 16px 0 0;
131 | }
132 |
133 | .main_item_title {
134 | font-size: 15px;
135 | color: var(--b-alpha-80);
136 | font-weight: 700;
137 | display: flex;
138 | flex-wrap: wrap;
139 | }
140 |
141 | .education_item {
142 | white-space: nowrap;
143 | flex-shrink: 0;
144 | }
145 |
146 | .top16 {
147 | padding-top: 16px;
148 | }
149 |
150 | .tootip {
151 | font-size: 14px;
152 | color: var(--b-alpha-50);
153 | }
154 |
155 | .main_item_jobOrAchievement {
156 | font-size: 15px;
157 | color: var(--b-alpha-80);
158 | line-height: 1.6em;
159 | text-indent: 2em;
160 | }
161 |
162 | .main_item_about{
163 | font-size: 15px;
164 | color: var(--b-alpha-80);
165 | line-height: 1.8em;
166 | padding: 16px 0;
167 | }
168 |
169 | .cursor_pointer{
170 | cursor: pointer;
171 | }
172 |
173 | @media screen and (max-width: 1200px) {
174 | .resume_content {
175 | width: 55%;
176 | }
177 | }
178 |
179 | @media screen and (max-width: 1000px) {
180 | .resume_content {
181 | width: 65%;
182 | }
183 | }
184 |
185 | @media screen and (max-width: 600px) {
186 | .resume_content {
187 | width: 80%;
188 | }
189 |
190 | .main_title {
191 | font-size: 18px;
192 | }
193 |
194 | .main_item_about,
195 | .main_item_jobOrAchievement,
196 | .education_item,
197 | .main_item_title,
198 | .main_item_lectotype,
199 | .main_item > li,
200 | .main_item_desc,
201 | .main_item_content,
202 | .work_time,
203 | .role,
204 | .place,
205 | .project_time,
206 | .project_desc {
207 | font-size: 14px;
208 | }
209 | }
210 |
211 | @media screen and (max-width: 400px) {
212 | .resume_content {
213 | width: 90%;
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/app/store/layoutStore.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import React, { createContext, useState } from "react";
4 |
5 | type Props = {
6 | children: any;
7 | };
8 |
9 | export type LayoutContextStore = {
10 | theme: 1 | 2;
11 | changeTheme: (v: 1 | 2) => any;
12 | };
13 |
14 | const LayoutContext = createContext({} as LayoutContextStore);
15 |
16 | const LayoutContextProvider = (props: Props) => {
17 | // 主题
18 | const [theme, setTheme] = useState(1);
19 |
20 | return (
21 | setTheme(v) }}
23 | >
24 | {props.children}
25 |
26 | );
27 | };
28 |
29 | export { LayoutContextProvider, LayoutContext };
30 |
--------------------------------------------------------------------------------
/app/styles/comment.css:
--------------------------------------------------------------------------------
1 | .twikoo{
2 | width: 100%;
3 | }
4 |
5 | .tk-comments{
6 |
7 | }
--------------------------------------------------------------------------------
/app/styles/error404.module.css:
--------------------------------------------------------------------------------
1 | .error_404 {
2 | width: 100%;
3 | flex: 1;
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | justify-content: center;
8 | background-color: var(--bg-w-pure);
9 | padding: 150px 0;
10 | }
11 |
12 | .content {
13 | display: flex;
14 | flex-direction: column;
15 | margin-bottom: 24px;
16 | padding: 0 20px;
17 | }
18 |
19 | .title {
20 | font-size: 100px;
21 | font-family: monospace;
22 | display: flex;
23 | justify-content: flex-start;
24 | color: var(--b-alpha-80);
25 | }
26 |
27 | .cn {
28 | font-size: 16px;
29 | letter-spacing: 0;
30 | color: var(--b-alpha-80);
31 | }
32 |
33 | .en {
34 | font-size: 14px;
35 | letter-spacing: 0;
36 | color: var(--b-alpha-80);
37 | }
38 |
39 | .btn {
40 | width: 300px;
41 | display: flex;
42 | justify-content: center;
43 | }
44 |
45 | .btn_item {
46 | padding: 5px 15px;
47 | background-color: var(--b-alpha-60);
48 | display: flex;
49 | align-items: center;
50 | justify-content: center;
51 | margin: 0 10px;
52 | color: var(--w-alpha);
53 | border-radius: 5px;
54 | font-weight: 500;
55 | white-space: nowrap;
56 | cursor: pointer;
57 | }
58 |
59 | .btn_item:hover {
60 | background-color: var(--b-alpha-50);
61 | color: var(--w-alpha);
62 | }
63 |
64 | @media screen and (max-width: 800px) {
65 | .title{
66 | justify-content: center;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/styles/font/VjlkfcVsDrtK.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/app/styles/font/VjlkfcVsDrtK.woff
--------------------------------------------------------------------------------
/app/styles/font/VjlkfcVsDrtK.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/app/styles/font/VjlkfcVsDrtK.woff2
--------------------------------------------------------------------------------
/app/styles/globals.css:
--------------------------------------------------------------------------------
1 | @import url(xgplayer/dist/index.min.css);
2 | @import url(./themeColor.css);
3 | @import url(./reset.css);
4 | @import url(./id.css);
5 | @import url(./markdown.css);
6 | @import url(./loading.css);
7 | @font-face {
8 | font-family: "阿里妈妈东方大楷 Regular";
9 | font-weight: 400;
10 | src: url("./font/VjlkfcVsDrtK.woff2")
11 | format("woff2"),
12 | url("./font/VjlkfcVsDrtK.woff")
13 | format("woff");
14 | font-display: swap;
15 | }
16 |
17 | body,
18 | h1,
19 | h2,
20 | h3,
21 | h4,
22 | h5,
23 | h6,
24 | hr,
25 | p,
26 | blockquote,
27 | dl,
28 | dt,
29 | dd,
30 | ul,
31 | ol,
32 | li,
33 | pre,
34 | form,
35 | fieldset,
36 | legend,
37 | button,
38 | input,
39 | textarea,
40 | th,
41 | td {
42 | margin: 0;
43 | padding: 0;
44 | }
45 |
46 | body,
47 | button,
48 | input,
49 | select,
50 | textarea {
51 | font: 12px/1.5tahoma, arial, \5b8b\4f53;
52 | }
53 |
54 | h1,
55 | h2,
56 | h3,
57 | h4,
58 | h5,
59 | h6 {
60 | font-size: 100%;
61 | }
62 |
63 | address,
64 | cite,
65 | dfn,
66 | em,
67 | var {
68 | font-style: normal;
69 | }
70 |
71 | code,
72 | kbd,
73 | pre,
74 | samp {
75 | font-family: couriernew, courier, monospace;
76 | }
77 |
78 | small {
79 | font-size: 12px;
80 | }
81 |
82 | a {
83 | text-decoration: none;
84 | }
85 |
86 | a:hover {
87 | text-decoration: none;
88 | }
89 |
90 | sup {
91 | vertical-align: text-top;
92 | }
93 |
94 | sub {
95 | vertical-align: text-bottom;
96 | }
97 |
98 | legend {
99 | color: #000;
100 | }
101 |
102 | fieldset,
103 | img {
104 | border: 0;
105 | }
106 |
107 | button,
108 | input,
109 | select,
110 | textarea {
111 | font-size: 100%;
112 | }
113 |
114 | table {
115 | border-collapse: collapse;
116 | border-spacing: 0;
117 | }
118 |
119 | ul {
120 | list-style: circle inherit;
121 | }
122 |
123 | ol {
124 | list-style: decimal inherit;
125 | }
126 |
127 | * {
128 | margin: 0;
129 | padding: 0;
130 | box-sizing: border-box;
131 | -webkit-font-smoothing: antialiased;
132 | letter-spacing: 0.1em;
133 | }
134 |
135 | html,
136 | body {
137 | width: 100%;
138 | height: 100%;
139 | overflow: hidden;
140 | font-family: var(--ct-body-font-family);
141 | }
142 |
143 | body {
144 | overflow-x: hidden;
145 | overflow-y: auto;
146 | display: flex;
147 | flex-direction: column;
148 | scroll-behavior: smooth;
149 | align-items: center;
150 | }
151 |
152 | #__next {
153 | width: 100vw;
154 | flex: 1;
155 | display: flex;
156 | flex-direction: column;
157 | }
158 |
159 | /* nav start */
160 | .nav_active {
161 | background-color: var(--w-alpha-40);
162 | backdrop-filter: blur(8px);
163 | box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
164 | transition: 0.4s;
165 | }
166 |
167 | .logo-light{
168 | display: block;
169 | }
170 |
171 | .logo-auto{
172 | display: none;
173 | }
174 |
175 | .nav_active .logo-auto,.nav_active_border .logo-auto{
176 | display: block;
177 | }
178 |
179 | .nav_active .logo-light,.nav_active_border .logo-light{
180 | display: none;
181 | }
182 |
183 | #layout_nav.nav_none {
184 | opacity: 0;
185 | transition: 0.4s;
186 | pointer-events: none;
187 | }
188 | /* nav end */
189 |
190 | /* page loading */
191 | .all-page-loading{
192 | position: absolute;
193 | left: 0;
194 | top: 0;
195 | z-index: -10;
196 | opacity: 0;
197 | }
198 |
199 | @media screen and (max-width: 900px) {
200 | * {
201 | letter-spacing: 0;
202 | }
203 | }
204 |
205 | @media print {
206 | body {
207 | height: auto;
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/app/styles/home.module.css:
--------------------------------------------------------------------------------
1 | .home {
2 | width: 100%;
3 | }
4 |
5 | .bg_card {
6 | width: 100%;
7 | height: 100vh;
8 | position: fixed;
9 | left: 0;
10 | top: 0;
11 | z-index: -100;
12 | animation: gradient 1.5s 1;
13 | }
14 |
15 | .bg_card_img {
16 | width: 100%;
17 | height: 100%;
18 | object-fit: cover;
19 | }
20 |
21 | .bg_mask {
22 | width: 100%;
23 | height: 100vh;
24 | position: fixed;
25 | left: 0;
26 | top: 0;
27 | z-index: -99;
28 | }
29 |
30 | .bg_content {
31 | width: 100%;
32 | height: 100vh;
33 | display: flex;
34 | flex-direction: column;
35 | align-items: center;
36 | justify-content: center;
37 | padding: 70px 0;
38 | position: relative;
39 | }
40 |
41 | .bg_content .title {
42 | width: 80%;
43 | display: flex;
44 | align-items: center;
45 | justify-content: center;
46 | height: 80px;
47 | font-size: 60px;
48 | text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5); /* 添加黑色阴影 */
49 | font-family: "阿里妈妈东方大楷 Regular", serif;
50 | color: var(--txt-w-pure);
51 | margin-bottom: 40px;
52 | }
53 |
54 | .bg_content .description_box {
55 | width: 80%;
56 | height: 72px;
57 | display: flex;
58 | justify-content: center;
59 | }
60 |
61 | .description_box .description {
62 | line-height: 1.3;
63 | color: var(--txt-w-pure);
64 | font-size: 26px;
65 | font-weight: bold;
66 | text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5); /* 添加黑色阴影 */
67 | font-family: "楷体","华文楷体";
68 | text-align: center;
69 | }
70 |
71 | .bg_content .jiantou {
72 | position: absolute;
73 | bottom: 0;
74 | width: 100%;
75 | height: 70px;
76 | display: flex;
77 | align-items: center;
78 | justify-content: center;
79 | z-index: 1;
80 | }
81 |
82 | .bg_content .jiantou_icon {
83 | font-size: 36px;
84 | color: var(--txt-w-pure);
85 | font-weight: bold;
86 | opacity: 0.7;
87 | transition: 0.45s;
88 | cursor: pointer;
89 | }
90 |
91 | .bg_content .jiantou_icon:hover {
92 | opacity: 1;
93 | }
94 |
95 | .project_box {
96 | width: 100%;
97 | background-color: var(--bg-w-235);
98 | display: flex;
99 | flex-direction: column;
100 | padding: 70px 0;
101 | }
102 |
103 | .page_box {
104 | width: 100%;
105 | background-color: var(--w-alpha-80);
106 | display: flex;
107 | flex-direction: column;
108 | padding: 70px 0;
109 | backdrop-filter: blur(10px);
110 | }
111 |
112 | .page_list {
113 | width: 100%;
114 | max-width: 1000px;
115 | display: grid;
116 | gap: 16px;
117 | grid-template-columns: repeat(2, 1fr);
118 | padding: 24px 24px 0;
119 | margin: 0 auto;
120 | }
121 |
122 | .page_item {
123 | width: 100%;
124 | position: relative;
125 | border-radius: 12px;
126 | overflow: hidden;
127 | background-color: rgba(0, 0, 0, 0.6);
128 | }
129 |
130 | .page_item:hover .page_item_link {
131 | height: 100%;
132 | font-size: 32px;
133 | color: rgba(255, 255, 255, 0.9);
134 | }
135 |
136 | .page_item_bg {
137 | width: 100%;
138 | height: auto;
139 | }
140 |
141 | .page_item_link {
142 | position: absolute;
143 | left: 0;
144 | bottom: 0;
145 | width: 100%;
146 | background-color: rgba(0, 0, 0, 0.6);
147 | backdrop-filter: blur(5px);
148 | display: flex;
149 | height: 20%;
150 | align-items: center;
151 | justify-content: center;
152 | text-align: center;
153 | font-size: 18px;
154 | font-weight: 500;
155 | color: rgba(255, 255, 255, 0.8);
156 | transition: height 0.25s;
157 | }
158 |
159 | .timeAixs_box {
160 | width: 100%;
161 | background-color: var(--bg-w-245);
162 | display: flex;
163 | flex-direction: column;
164 | padding: 70px 0;
165 | }
166 |
167 | .timeAixs_title,
168 | .page_title {
169 | width: 80%;
170 | margin: 30px auto;
171 | white-space: nowrap;
172 | font-size: 30px;
173 | color: var(--b-alpha);
174 | font-weight: bolder;
175 | text-align: center;
176 | position: relative;
177 | }
178 |
179 | .timeAixs_title::after,
180 | .page_title::after {
181 | position: absolute;
182 | content: "";
183 | z-index: 11;
184 | left: 50%;
185 | transform: translateX(-50%);
186 | bottom: -20px;
187 | width: 100px;
188 | height: 3px;
189 | background-color: var(--purple-primary);
190 | }
191 |
192 | .timeAixs_desc,
193 | .page_desc {
194 | margin: 0 auto;
195 | height: 50px;
196 | font-size: 12px;
197 | color: var(--b-alpha-60);
198 | padding-bottom: 20px;
199 | display: flex;
200 | align-items: center;
201 | justify-content: center;
202 | }
203 |
204 | .timeAixs {
205 | width: 80%;
206 | position: relative;
207 | margin: 0 auto;
208 | }
209 |
210 | .timeAixs_content {
211 | display: flex;
212 | overflow-y: auto;
213 | align-items: flex-start;
214 | padding-bottom: 12px;
215 | }
216 |
217 | .timeAixs_content::-webkit-scrollbar {
218 | width: 5px;
219 | height: 5px;
220 | }
221 |
222 | .timeAixs_content::-webkit-scrollbar-thumb {
223 | background-color: var(--b-alpha-10);
224 | border-radius: 3px;
225 | }
226 |
227 | .timeAixs_content::-webkit-scrollbar-thumb:hover {
228 | background-color: var(--b-alpha-70);
229 | }
230 |
231 | .timeAixs_item {
232 | display: flex;
233 | flex-direction: column;
234 | justify-content: flex-start;
235 | margin: 0 60px;
236 | flex-shrink: 0;
237 | width: 240px;
238 | }
239 |
240 | .timeAixs_item_time {
241 | font-size: 20px;
242 | font-weight: 700;
243 | color: var(--b-alpha-90);
244 | padding-bottom: 8px;
245 | }
246 |
247 | .timeAixs_item_title {
248 | font-size: 14px;
249 | color: var(--b-alpha-90);
250 | line-height: 1.5em;
251 | }
252 |
253 | .timeAixs_item_desc {
254 | font-size: 22px;
255 | color: var(--b-alpha-30);
256 | font-weight: 900;
257 | }
258 |
259 | .timeAixs_left {
260 | position: absolute;
261 | left: 0;
262 | top: 0;
263 | z-index: 1;
264 | width: 80px;
265 | height: 100%;
266 | background-image: linear-gradient(to left, var(--w-alpha-0), var(--bg-w-245));
267 | }
268 |
269 | .timeAixs_right {
270 | position: absolute;
271 | right: 0;
272 | top: 0;
273 | z-index: 1;
274 | width: 80px;
275 | height: 100%;
276 | background-image: linear-gradient(
277 | to right,
278 | var(--w-alpha-0),
279 | var(--bg-w-245)
280 | );
281 | }
282 |
283 | @media screen and (max-width: 700px) {
284 | .bg_content .title {
285 | width: 100%;
286 | font-size: 28px;
287 | }
288 |
289 | .description_box .description{
290 | font-size: 20px;
291 | }
292 |
293 | .timeAixs {
294 | width: 90%;
295 | }
296 |
297 | .page_list {
298 | grid-template-columns: repeat(1, 1fr);
299 | }
300 | }
301 |
302 | @keyframes gradient {
303 | from {
304 | transform: scale(1.05);
305 | opacity: 0.5;
306 | }
307 | to {
308 | transform: scale(1);
309 | opacity: 1;
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/app/styles/id.css:
--------------------------------------------------------------------------------
1 | .dark #bg_mask {
2 | background-color: rgba(0, 0, 0, 0.2);
3 | }
4 |
5 | .nav_item_text.nav_item_block{
6 | color: var(--b-alpha-60);
7 | font-weight: 400;
8 | }
9 |
10 | .nav_active_border{
11 | border-bottom: 1px solid var(--b-alpha-5);
12 | }
13 |
14 | #nav_mobile_list .nav_item_text,
15 | .nav_active .nav_item_text {
16 | color: var(--b-alpha-60);
17 | font-weight: 400;
18 | }
19 |
20 | #nav_mobile_list .nav_item_text:hover,
21 | .nav_active .nav_item_text:hover {
22 | color: var(--b-alpha-80);
23 | }
24 |
25 | /* 打字机光标颜色 */
26 | /* .typed-cursor{
27 | color: var(--txt-w-pure);
28 | } */
29 |
--------------------------------------------------------------------------------
/app/styles/loading.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css?family=Baloo+Bhaijaan&display=swap");
2 | .preloader_box {
3 | position: fixed;
4 | top: 0;
5 | left: 0;
6 | width: 100%;
7 | height: 100%;
8 | overflow: hidden;
9 | z-index: 9999;
10 | transition: opacity 0.65s;
11 | display: flex;
12 | height: 100vh;
13 | justify-content: center;
14 | align-items: center;
15 | text-align: center;
16 | }
17 |
18 | .preloader_box_none{
19 | animation: none 0.3s ease-out;
20 | animation-fill-mode: forwards;
21 | }
22 |
23 | .preloader_left{
24 | position: absolute;
25 | left: 0;
26 | top: 0;
27 | width: 51%;
28 | height: 100%;
29 | background: var(--purple-primary);
30 | }
31 |
32 | .preloader_left_up{
33 | animation: fadeIn1 0.3s ease-out;
34 | animation-fill-mode: forwards;
35 | }
36 |
37 | .preloader_left_down{
38 | animation: fadeUp1 0.3s ease-out;
39 | animation-fill-mode: forwards;
40 | }
41 |
42 | .preloader_right{
43 | position: absolute;
44 | right: 0;
45 | top: 0;
46 | width: 51%;
47 | height: 100%;
48 | background: var(--purple-primary);
49 | }
50 |
51 | .preloader_right_up{
52 | animation: fadeIn2 0.3s ease-out;
53 | animation-fill-mode: forwards;
54 | }
55 |
56 | .preloader_right_down{
57 | animation: fadeUp2 0.3s ease-out;
58 | animation-fill-mode: forwards;
59 | }
60 |
61 | .preloader {
62 | display: flex;
63 | color: white;
64 | font-size: 5em;
65 | font-family: "Baloo Bhaijaan", "initial"; /* text-transform: uppercase; */
66 | }
67 |
68 | .preloader_up{
69 | animation: amplify 0.3s ease-out;
70 | animation-fill-mode: forwards;
71 | }
72 |
73 | .preloader_down{
74 | animation: reduce 0.3s ease-out;
75 | animation-fill-mode: forwards;
76 | }
77 |
78 | .preloader .inner {
79 | text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb, 0 5px #bbb,
80 | 0 6px transparent, 0 7px transparent, 0 8px transparent, 0 9px transparent,
81 | 0 10px 10px rgba(0, 0, 0, 0.4);
82 | transform: translateY(20px);
83 | margin-left: 2px;
84 | margin-right: 2px;
85 | }
86 | .preloader .inner:nth-child(1) {
87 | animation: prebounce 0.4s infinite alternate;
88 | }
89 | .preloader .inner:nth-child(2) {
90 | animation: prebounce 0.4s 0.1s infinite alternate;
91 | }
92 | .preloader .inner:nth-child(3) {
93 | animation: prebounce 0.4s 0.2s infinite alternate;
94 | }
95 | .preloader .inner:nth-child(4) {
96 | animation: prebounce 0.4s 0.3s infinite alternate;
97 | }
98 | .preloader .inner:nth-child(5) {
99 | animation: prebounce 0.4s 0.4s infinite alternate;
100 | }
101 | .preloader .inner:nth-child(6) {
102 | animation: prebounce 0.4s 0.5s infinite alternate;
103 | }
104 | .preloader .inner:nth-child(7) {
105 | animation: prebounce 0.4s 0.6s infinite alternate;
106 | }
107 | @keyframes prebounce {
108 | to {
109 | text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb, 0 5px #bbb,
110 | 0 6px #bbb, 0 7px #bbb, 0 8px #bbb, 0 9px #bbb,
111 | 0 50px 25px rgba(0, 0, 0, 0.2);
112 | transform: translateY(-20px);
113 | }
114 | }
115 |
116 | @media screen and (max-width: 800px) {
117 | .preloader {
118 | font-size: 3em;
119 | }
120 | }
121 |
122 | @keyframes fadeIn1 {
123 | 0% {
124 | opacity: 0;
125 | transform: translateX(-100%);
126 | }
127 | 100% {
128 | opacity: 1;
129 | transform: translateX(0);
130 | }
131 | }
132 |
133 | @keyframes fadeIn2 {
134 | 0% {
135 | opacity: 0;
136 | transform: translateX(100%);
137 | }
138 | 100% {
139 | opacity: 1;
140 | transform: translateX(0);
141 | }
142 | }
143 |
144 | @keyframes fadeUp1 {
145 | 0% {
146 | opacity: 1;
147 | transform: translateX(0);
148 | }
149 | 100% {
150 | opacity: 0;
151 | transform: translateX(-100%);
152 | }
153 | }
154 |
155 | @keyframes fadeUp2 {
156 | 0% {
157 | opacity: 1;
158 | transform: translateX(0);
159 | }
160 | 100% {
161 | opacity: 0;
162 | transform: translateX(100%);
163 | }
164 | }
165 |
166 | @keyframes reduce {
167 | 0% {
168 | opacity: 1;
169 | transform: scale(1,1);
170 | }
171 | 100% {
172 | opacity: 0;
173 | transform: scale(0.8,0.8);
174 | }
175 | }
176 |
177 | @keyframes amplify {
178 | 0% {
179 | opacity: 0;
180 | transform: scale(0.8,0.8);
181 | }
182 | 100% {
183 | opacity: 1;
184 | transform: scale(1,1);
185 | }
186 | }
187 |
188 | @keyframes none {
189 | 0% {
190 | display: block;
191 | }
192 | 100% {
193 | display: none;
194 | z-index: -1;
195 | visibility: hidden;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/app/styles/markdown.css:
--------------------------------------------------------------------------------
1 | /* 图片样式 */
2 | .markdown_body .blog_img {
3 | width: 100% !important;
4 | padding: 0 !important;
5 | }
6 |
7 | .markdown_body {
8 | margin: 0;
9 | font-family: -apple-system, system-ui, Segoe UI, Roboto, Ubuntu, Cantarell,
10 | Noto Sans, sans-serif, BlinkMacSystemFont, Helvetica Neue, PingFang SC,
11 | Hiragino Sans GB, Microsoft YaHei, Arial !important;
12 | width: 100%;
13 | word-break: break-word;
14 | line-height: 1.75;
15 | font-weight: 400;
16 | font-size: 16px;
17 | overflow-x: hidden;
18 | color: var(--b-alpha-90);
19 | }
20 |
21 | /* 自定义blockquote样式 */
22 | .markdown_body blockquote {
23 | color: var(--b-alpha-70);
24 | padding: 1px 23px;
25 | margin: 22px 0;
26 | background-color: var(--b-alpha-5);
27 | border-left: 4px solid var(--b-alpha-20);
28 | }
29 |
30 | .markdown_body blockquote > .tagName-p {
31 | margin: 10px 0;
32 | }
33 |
34 | /* 标题样式 */
35 | .markdown_body h1 {
36 | font-size: 24px;
37 | line-height: 38px;
38 | margin-bottom: 5px;
39 | }
40 |
41 | .markdown_body h2 {
42 | font-size: 22px;
43 | line-height: 34px;
44 | padding-bottom: 12px;
45 | border-bottom: 1px solid #ececec;
46 | }
47 |
48 | .markdown_body h3 {
49 | font-size: 20px;
50 | line-height: 28px;
51 | margin-top: 35px;
52 | margin-bottom: 10px;
53 | padding-bottom: 5px;
54 | }
55 |
56 | .markdown_body h4 {
57 | font-size: 18px;
58 | line-height: 26px;
59 | }
60 |
61 | .markdown_body h5 {
62 | font-size: 17px;
63 | line-height: 24px;
64 | }
65 |
66 | .markdown_body h6 {
67 | font-size: 16px;
68 | line-height: 22px;
69 | }
70 |
71 | .markdown_body h1,
72 | .markdown_body h2,
73 | .markdown_body h3,
74 | .markdown_body h4,
75 | .markdown_body h5,
76 | .markdown_body h6 {
77 | line-height: 1.5;
78 | margin-top: 35px;
79 | margin-bottom: 10px;
80 | padding-bottom: 5px;
81 | }
82 |
83 | /* 代码块 */
84 | .markdown_body pre {
85 | position: relative;
86 | color: var(--b-alpha-80);
87 | background: var(--b-alpha-5);
88 | overflow: auto;
89 | line-height: 1.75;
90 | font-size: 12px;
91 | margin: 1em 0;
92 | }
93 |
94 | .markdown_body code {
95 | padding: 0.2em 0.5em;
96 | font-weight: 700;
97 | font-family: -apple-system-font, BlinkMacSystemFont, Helvetica Neue,
98 | PingFang SC, Hiragino Sans GB, Microsoft YaHei UI, Microsoft YaHei, Arial,
99 | sans-serif;
100 | font-size: 1em;
101 | color: var(--code-color-5);
102 | word-break: break-word;
103 | overflow-x: auto;
104 | background-color: none;
105 | border-radius: 2px;
106 | }
107 |
108 | .markdown_body pre code {
109 | font-family: Menlo, Monaco, Consolas, Courier New, monospace;
110 | }
111 |
112 | .markdown_body pre .code_copy {
113 | display: flex;
114 | user-select: none;
115 | height: 28px;
116 | align-items: center;
117 | justify-content: space-between;
118 | box-shadow: 0px 4px 5px -6px var(--b-alpha-50);
119 | margin-bottom: 3px;
120 | padding: 0 16px;
121 | }
122 |
123 | .markdown_body pre .code_copy .code_language {
124 | font-size: 12px;
125 | color: var(--b-alpha-60);
126 | }
127 |
128 | .markdown_body pre .code_copy .code_btn {
129 | font-size: 12px;
130 | cursor: pointer;
131 | color: var(--b-alpha-60);
132 | }
133 |
134 | /* 列表 */
135 | .markdown_body ol,
136 | .markdown_body ul {
137 | padding-left: 28px;
138 | }
139 | .markdown_body ol li,
140 | .markdown_body ul li {
141 | margin-bottom: 0;
142 | list-style: inherit;
143 | }
144 |
145 | .markdown_body ol ol,
146 | .markdown_body ol ul,
147 | .markdown_body ul ol,
148 | .markdown_body ul ul {
149 | margin-top: 3px;
150 | }
151 | .markdown_body ol li {
152 | padding-left: 6px;
153 | }
154 |
155 | /* 代码块样式 */
156 |
157 | .markdown_body pre,
158 | .markdown_body pre > code.hljs {
159 | color: var(--code-color-1);
160 | background: var(--code-bg-1);
161 | }
162 | pre code.hljs {
163 | display: block;
164 | overflow-x: auto;
165 | padding: 1em;
166 | }
167 | code.hljs {
168 | padding: 3px 5px;
169 | }
170 | .hljs {
171 | color: var(--code-color-1);
172 | background: var(--code-bg-1);
173 | }
174 | .hljs ::selection,
175 | .hljs::selection {
176 | background-color: var(---code-bg-3);
177 | color: var(--code-color-4);
178 | }
179 | .hljs-comment,
180 | .hljs-quote {
181 | color: var(--code-color-2);
182 | font-style: italic;
183 | }
184 | .hljs-keyword,
185 | .hljs-selector-tag,
186 | .hljs-subst {
187 | color: var(--code-color-1);
188 | font-weight: 700;
189 | }
190 | .hljs-literal,
191 | .hljs-number,
192 | .hljs-tag .hljs-attr,
193 | .hljs-template-variable,
194 | .hljs-variable {
195 | color: var(--code-color-3);
196 | }
197 | .hljs-doctag,
198 | .hljs-string {
199 | color: var(--code-color-4);
200 | }
201 | .hljs-section,
202 | .hljs-selector-id,
203 | .hljs-title {
204 | color: var(--code-color-5);
205 | font-weight: 700;
206 | }
207 | .hljs-subst {
208 | font-weight: 400;
209 | }
210 | .hljs-class .hljs-title,
211 | .hljs-type {
212 | color: var(--code-color-6);
213 | font-weight: 700;
214 | }
215 | .hljs-attribute,
216 | .hljs-name,
217 | .hljs-tag {
218 | color: var(--code-color-7);
219 | font-weight: 400;
220 | }
221 | .hljs-link,
222 | .hljs-regexp {
223 | color: var(--code-color-8);
224 | }
225 | .hljs-bullet,
226 | .hljs-symbol {
227 | color: var(--code-color-9);
228 | }
229 | .hljs-built_in,
230 | .hljs-builtin-name {
231 | color: var(--code-color-10);
232 | }
233 | .hljs-meta {
234 | color: var(--code-color-11);
235 | font-weight: 700;
236 | }
237 | .hljs-deletion {
238 | background: var(--code-bg-2);
239 | }
240 | .hljs-addition {
241 | background: var(--code-bg-3);
242 | }
243 | .hljs-emphasis {
244 | font-style: italic;
245 | }
246 | .hljs-strong {
247 | font-weight: 700;
248 | }
249 |
250 | /* 超链接 */
251 | .markdown_body a {
252 | text-decoration: none;
253 | color: var(--code-color-5);
254 | font-weight: 700;
255 | border-bottom: 1px solid var(--code-color-5);
256 | }
257 |
258 | /* 导航样式 */
259 | .toc-item {
260 | color: var(--b-alpha-90);
261 | font-weight: 500;
262 | font-size: 14px;
263 | cursor: pointer;
264 | margin: 8px 0;
265 | transition: 0.4s;
266 | }
267 |
268 | .toc-item span{
269 | overflow: hidden;
270 | text-overflow: ellipsis;
271 | white-space: nowrap;
272 | display: inline-block;
273 | width: 100%;
274 | }
275 |
276 | .toc-item:hover {
277 | color: var(--code-color-5);
278 | }
279 |
280 | .toc-item0 {
281 | padding-left: 4px;
282 | }
283 |
284 | .toc-item1 {
285 | padding-left: 16px;
286 | }
287 |
288 | .toc-item2 {
289 | padding-left: 28px;
290 | }
291 |
292 | .toc-item3 {
293 | padding-left: 40px;
294 | }
295 |
296 | .toc-item4 {
297 | padding-left: 52px;
298 | }
299 |
300 | .toc-item5 {
301 | padding-left: 64px;
302 | }
303 |
304 | /* markdown table */
305 | .markdown_body table {
306 | width: 100%;
307 | border-collapse: collapse;
308 | border-spacing: 0;
309 | }
310 |
311 | .markdown_body th, .markdown_body td {
312 | padding: 8px;
313 | border: 1px solid #ddd;
314 | }
315 |
316 | .markdown_body th {
317 | background-color: #f2f2f2;
318 | font-weight: bold;
319 | }
320 |
321 |
--------------------------------------------------------------------------------
/app/styles/reset.css:
--------------------------------------------------------------------------------
1 | /* 分页器 开始 */
2 | .ant-pagination {
3 | transition: 0.4s;
4 | }
5 |
6 | .ant-pagination .ant-pagination-item a,
7 | .ant-pagination .ant-pagination-next .ant-pagination-item-link,
8 | .ant-pagination .ant-pagination-prev .ant-pagination-item-link,
9 | .ant-pagination .ant-pagination-item-container .ant-pagination-item-ellipsis {
10 | color: var(--b-alpha-80) !important;
11 | }
12 |
13 | .ant-pagination .ant-pagination-item,
14 | .ant-pagination .ant-pagination-next,
15 | .ant-pagination .ant-pagination-prev {
16 | border-radius: 50% !important;
17 | overflow: hidden;
18 | }
19 |
20 | .ant-pagination .ant-pagination-item:hover,
21 | .ant-pagination .ant-pagination-next:hover,
22 | .ant-pagination .ant-pagination-prev:hover {
23 | background-color: var(--purple-text-hover) !important;
24 | color: var(--txt-w-pure);
25 | font-weight: bold;
26 | border-radius: 50% !important;
27 | overflow: hidden;
28 | border: none;
29 | }
30 |
31 | .ant-pagination .ant-pagination-item:hover a {
32 | color: var(--txt-w-pure);
33 | }
34 |
35 | .ant-pagination .ant-pagination-item-active {
36 | background-color: var(--purple-text-hover);
37 | color: var(--txt-w-pure);
38 | font-weight: bold;
39 | border-radius: 50% !important;
40 | border: none;
41 | }
42 |
43 | .ant-pagination .ant-pagination-item-active:hover {
44 | color: var(--txt-w-pure) !important;
45 | }
46 |
47 | .ant-pagination .ant-pagination-disabled .ant-pagination-item-link,
48 | .ant-pagination .ant-pagination-disabled:hover {
49 | color: var(--b-alpha-40) !important;
50 | background-color: transparent !important;
51 | }
52 |
53 |
54 | /* 分页器 结束 */
55 |
56 | /* 输入框 开始 */
57 | .ant-input::placeholder {
58 | color: var(--b-alpha-60) !important;
59 | }
60 | /* 输入框 结束 */
61 |
62 | /* 图片 开始 */
63 | :is(.ant-image-preview-root .ant-image-preview-mask){
64 | background-color: rgba(0,0,0,0.8);
65 | backdrop-filter: blur(5px);
66 | }
67 | /* 图片 结束 */
68 |
69 | /* 评论 开始 */
70 | .el-input-group__prepend,
71 | .el-input__inner {
72 | color: var(--b-alpha-80) !important;
73 | }
74 |
75 | .el-textarea__inner,
76 | .el-input__count,
77 | .tk-submit-action-icon {
78 | color: var(--b-alpha-70) !important;
79 | }
80 |
81 | .tk-preview{
82 | color: var(--b-alpha-60) !important;
83 | }
84 |
85 | .tk-comments-container{
86 | color: var(--b-alpha-60) !important;
87 | }
88 |
89 | .tk-ruser{
90 | color: var(--purple-text);
91 | }
92 |
93 | .tk-content p{
94 | color: var(--b-alpha-90) !important;
95 | }
96 | /* 评论 结束 */
97 |
--------------------------------------------------------------------------------
/app/styles/themeColor.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --ct-body-font-family: "PingFang SC", "Hiragino Sans GB",
3 | "Microsoft Yahei", "Microsoft Jhenghei", sans-serif;
4 | --ct-theme: linear-gradient(rgb(57, 89, 138), rgb(121, 215, 237));
5 | --ct-theme-border: 2px solid rgb(255, 255, 255);
6 | --w-alpha-0: rgba(255, 255, 255, 0);
7 | --w-alpha-5: rgba(255, 255, 255, 0.05);
8 | --w-alpha-10: rgba(255, 255, 255, 0.1);
9 | --w-alpha-15: rgba(255, 255, 255, 0.15);
10 | --w-alpha-20: rgba(255, 255, 255, 0.2);
11 | --w-alpha-30: rgba(255, 255, 255, 0.3);
12 | --w-alpha-40: rgba(255, 255, 255, 0.4);
13 | --w-alpha-50: rgba(255, 255, 255, 0.5);
14 | --w-alpha-60: rgba(255, 255, 255, 0.6);
15 | --w-alpha-70: rgba(255, 255, 255, 0.7);
16 | --w-alpha-75: rgba(255, 255, 255, 0.75);
17 | --w-alpha-80: rgba(255, 255, 255, 0.8);
18 | --w-alpha-90: rgba(255, 255, 255, 0.9);
19 | --b-alpha-0: rgba(0, 0, 0, 0);
20 | --b-alpha-5: rgba(0, 0, 0, 0.05);
21 | --b-alpha-10: rgba(0, 0, 0, 0.1);
22 | --b-alpha-15: rgba(0, 0, 0, 0.15);
23 | --b-alpha-20: rgba(0, 0, 0, 0.2);
24 | --b-alpha-25: rgba(0, 0, 0, 0.25);
25 | --b-alpha-30: rgba(0, 0, 0, 0.3);
26 | --b-alpha-35: rgba(0, 0, 0, 0.35);
27 | --b-alpha-40: rgba(0, 0, 0, 0.4);
28 | --b-alpha-50: rgba(0, 0, 0, 0.5);
29 | --b-alpha-60: rgba(0, 0, 0, 0.6);
30 | --b-alpha-70: rgba(0, 0, 0, 0.7);
31 | --b-alpha-75: rgba(0, 0, 0, 0.75);
32 | --b-alpha-80: rgba(0, 0, 0, 0.8);
33 | --b-alpha-90: rgba(0, 0, 0, 0.9);
34 | --w-alpha: rgb(255, 255, 255);
35 | --b-alpha: rgb(0, 0, 0);
36 | --txt-w-pure: white;
37 | --txt-b-pure: black;
38 | --bg-w-pure: white;
39 | --bg-w-245: rgb(245, 245, 245);
40 | --bg-w-235: rgb(235, 235, 235);
41 | --bg-w-225: rgb(225, 225, 225);
42 | --purple-primary: #727cf5;
43 | --purple-primary-active: #575dcf;
44 | --purple-primary-hover: #a1acff;
45 | --purple-text: #727cf5;
46 | --purple-text-active: #575dfc;
47 | --purple-text-hover: #a1acff;
48 | --purple-border: #f0f3ff;
49 | --purple-border-hover: #c9d1ff;
50 | --purple-bg: #f0f4ff;
51 | --purple-bg-hover: #f0f3ff;
52 | --image-preview-mask: rgb(255,255,255);
53 | --color-blue: #1677ff;
54 | --color-blue-hover: #4096ff;
55 | --color-blue-bg: #e6f4ff;
56 | --color-blue-bg-hover: #bae0ff;
57 | --color-blue-border: #91caff;
58 | --color-blue-border-hover: #69b1ff;
59 | }
60 |
61 | .dark {
62 | --ct-theme: linear-gradient(rgb(9, 18, 54), rgb(30, 33, 93));
63 | --ct-theme-border: 2px solid rgb(107, 128, 150);
64 | --w-alpha-0: rgba(0, 0, 0, 0);
65 | --w-alpha-5: rgba(0, 0, 0, 0.05);
66 | --w-alpha-10: rgba(0, 0, 0, 0.1);
67 | --w-alpha-15: rgba(0, 0, 0, 0.15);
68 | --w-alpha-20: rgba(0, 0, 0, 0.2);
69 | --w-alpha-30: rgba(0, 0, 0, 0.3);
70 | --w-alpha-40: rgba(0, 0, 0, 0.4);
71 | --w-alpha-50: rgba(0, 0, 0, 0.5);
72 | --w-alpha-60: rgba(0, 0, 0, 0.6);
73 | --w-alpha-70: rgba(0, 0, 0, 0.7);
74 | --w-alpha-75: rgba(0, 0, 0, 0.75);
75 | --w-alpha-80: rgba(0, 0, 0, 0.8);
76 | --w-alpha-90: rgba(30, 30, 30, 0.9);
77 | --b-alpha-0: rgba(255, 255, 255, 0);
78 | --b-alpha-5: rgba(255, 255, 255, 0.05);
79 | --b-alpha-10: rgba(255, 255, 255, 0.1);
80 | --b-alpha-15: rgba(255, 255, 255, 0.15);
81 | --b-alpha-20: rgba(255, 255, 255, 0.2);
82 | --b-alpha-30: rgba(255, 255, 255, 0.3);
83 | --b-alpha-35: rgba(255, 255, 255, 0.35);
84 | --b-alpha-40: rgba(255, 255, 255, 0.4);
85 | --b-alpha-50: rgba(255, 255, 255, 0.5);
86 | --b-alpha-60: rgba(255, 255, 255, 0.6);
87 | --b-alpha-70: rgba(255, 255, 255, 0.7);
88 | --b-alpha-75: rgba(255, 255, 255, 0.75);
89 | --b-alpha-80: rgba(255, 255, 255, 0.8);
90 | --b-alpha-90: rgba(255, 255, 255, 0.9);
91 | --w-alpha: rgb(0, 0, 0);
92 | --b-alpha: rgb(255, 255, 255);
93 | --bg-w-pure: rgb(30, 30, 30);
94 | --bg-w-245: rgb(40, 40, 40);
95 | --purple-primary: rgb(114, 124, 245);
96 | --purple-primary-active: #5158a6;
97 | --purple-primary-hover: #939de8;
98 | --purple-text: #646cd3;
99 | --purple-text-active: #5158a6;
100 | --purple-text-hover: #939de8;
101 | --purple-border: #303358;
102 | --purple-border-hover: #3e4379;
103 | --purple-bg: #1a1b2a;
104 | --purple-bg-hover: #252643;
105 | --image-preview-mask: rgb(50,50,50);
106 | --color-blue: #1677ff;
107 | --color-blue-hover: #3c89e8;
108 | --color-blue-bg: #111a2c;
109 | --color-blue-bg-hover: #112545;
110 | --color-blue-border: #15325b;
111 | --color-blue-border-hover: #15417e;
112 | }
113 |
114 | :root{
115 | --code-color-1: #333;
116 | --code-color-2: #998;
117 | --code-color-3: teal;
118 | --code-color-4: #d14;
119 | --code-color-5: #900;
120 | --code-color-6: #458;
121 | --code-color-7: navy;
122 | --code-color-8: #009926;
123 | --code-color-9: #990073;
124 | --code-color-10: #0086b3;
125 | --code-color-11: #999;
126 | --code-bg-1: #f8f8f8;
127 | --code-bg-2: #fdd;
128 | --code-bg-3: #dfd;
129 | }
130 |
131 | .dark{
132 | --code-color-1: #ccc;
133 | --code-color-2: #667;
134 | --code-color-3: #ff7f7f;
135 | --code-color-4: #2eb;
136 | --code-color-5: #6ff;
137 | --code-color-6: #ba7;
138 | --code-color-7: #ffff7f;
139 | --code-color-8: #ff66d9;
140 | --code-color-9: #66ff8c;
141 | --code-color-10: #ff794c;
142 | --code-color-11: #666;
143 | --code-bg-1: #070707;
144 | --code-bg-2: #022;
145 | --code-bg-3: #202;
146 | }
--------------------------------------------------------------------------------
/app/tree-hole/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useEffect, useRef, useState } from "react";
3 | import { useGetState } from "ahooks";
4 | import {
5 | addNavItemStyle,
6 | bindHandleScroll,
7 | layoutContent,
8 | removeNavItemStyle,
9 | removeScroll,
10 | } from "@utils/elementUtils";
11 | import getDataApi from "@/utils/httpClient/request";
12 | import { changeTreeData, distinctObjectMap } from "@utils/dataUtils";
13 | import VirtuallyItem from "@components/VirtuallyItem";
14 | import withLoading from "@components/WithLoading";
15 | import useChangeLoading from "@components/WithLoading/useChangeLoading";
16 | import style from "./treehole.module.css";
17 |
18 | type DateItem = {
19 | [key: string]: boolean | number | string | any;
20 | };
21 |
22 | const TreeHole = (props) => {
23 | // 树洞列表
24 | const [data, setData] = useState([]);
25 | // 当前页
26 | const [page, setPage, getPage] = useGetState(1);
27 | // 每页条数
28 | const [page_size, setPageSize] = useState(20);
29 | const [loading, setLoading] = useGetState(false);
30 | const [totalPages, setTotalPages, getTotalPages] = useGetState(0);
31 | // 添加初始化状态
32 | const [initialized, setInitialized] = useState(false);
33 |
34 | const content = useRef(null);
35 |
36 | const getDate = async () => {
37 | if (getTotalPages() !== 0 && getTotalPages() < getPage()) return;
38 | setLoading(true);
39 | const posts = await getDataApi({
40 | type: "all_secret_List",
41 | params: { page: getPage(), page_size: page_size },
42 | });
43 | setData((a) => distinctObjectMap([...a, ...posts.data], "id"));
44 | getPage() === 1 && setTotalPages(posts.meta.total_pages);
45 | setLoading(false);
46 | // 第一次数据加载完成后设置初始化状态
47 | if (!initialized) {
48 | // 使用 requestAnimationFrame 确保 DOM 更新后再设置状态
49 | requestAnimationFrame(() => {
50 | setInitialized(true);
51 | });
52 | }
53 | };
54 |
55 | // 滚动事件
56 | const scrollFun = () => {
57 | // 如果还未初始化完成,不处理滚动
58 | if (!initialized) return;
59 |
60 | // 滚动盒子
61 | const scrollBox = layoutContent();
62 | const scrollConBox = content.current;
63 |
64 | if (!scrollConBox) return;
65 | const flag = page < totalPages;
66 |
67 | if (
68 | scrollConBox.offsetHeight -
69 | scrollBox.offsetHeight +
70 | 40 -
71 | scrollBox.scrollTop <=
72 | 500 &&
73 | !loading &&
74 | flag
75 | ) {
76 | setPage((data) => data + 1);
77 | }
78 | };
79 |
80 | // 获取列表数据
81 | useEffect(() => {
82 | getDate();
83 | // eslint-disable-next-line react-hooks/exhaustive-deps
84 | }, [page]);
85 |
86 | useEffect(() => {
87 | const scrollBox = layoutContent();
88 | scrollBox && scrollBox.addEventListener("scroll", scrollFun);
89 | return () => {
90 | scrollBox && scrollBox.removeEventListener("scroll", scrollFun);
91 | };
92 | // eslint-disable-next-line react-hooks/exhaustive-deps
93 | }, [page_size, page, totalPages, loading, layoutContent]);
94 |
95 | useEffect(() => {
96 | addNavItemStyle();
97 | bindHandleScroll();
98 |
99 | return () => {
100 | removeNavItemStyle();
101 | removeScroll();
102 | };
103 | }, []);
104 |
105 | useChangeLoading({ ...props, name: "tree_hole" });
106 |
107 | return (
108 |
114 |
115 | {changeTreeData(data)?.map((v, ind) => (
116 |
117 |
{v?.year}
118 | {v?.children?.map((item) => (
119 |
120 |
121 |
122 |
123 |
124 | {item?.date_str}
125 |
126 | {item?.isTop === 1 && (
127 |
置 顶
128 | )}
129 |
130 |
134 |
135 |
{item?.type}
136 |
137 |
138 | ))}
139 |
140 | ))}
141 |
142 |
143 | );
144 | };
145 |
146 | export default withLoading(TreeHole);
147 |
--------------------------------------------------------------------------------
/app/tree-hole/treehole.module.css:
--------------------------------------------------------------------------------
1 | .tree_hole {
2 | width: 100%;
3 | flex: 1;
4 | padding-top: 70px;
5 | background-color: var(--bg-w-pure);
6 | display: flex;
7 | flex-direction: column;
8 | opacity: 0;
9 | transition: opacity 0.3s ease-in-out;
10 | }
11 |
12 | .tree_hole.opacity_100 {
13 | opacity: 1;
14 | }
15 |
16 | .content {
17 | width: 60%;
18 | flex: 1;
19 | max-width: 800px;
20 | margin: 0 auto;
21 | display: flex;
22 | flex-direction: column;
23 | align-items: center;
24 | padding-bottom: 24px;
25 | }
26 |
27 | .item {
28 | width: 100%;
29 | display: flex;
30 | flex-direction: column;
31 | }
32 |
33 | .year {
34 | width: 100%;
35 | line-height: 34px;
36 | margin: 24px 0;
37 | position: relative;
38 | padding-left: 30px;
39 | display: flex;
40 | align-items: center;
41 | font-size: 28px;
42 | font-weight: 400;
43 | color: var(--b-alpha-80);
44 | }
45 |
46 | .tree_item {
47 | width: 100%;
48 | display: flex;
49 | flex-direction: column;
50 | padding: 0 30px;
51 | margin-bottom: 18px;
52 | transition: 0.4s;
53 | }
54 |
55 | .tree_item_top {
56 | width: 100%;
57 | display: flex;
58 | padding-bottom: 8px;
59 | }
60 |
61 | .tree_item_left{
62 | width: 86px;
63 | flex-shrink: 0;
64 | display: flex;
65 | flex-direction: column;
66 | }
67 |
68 | .tree_item_time {
69 | width: 100%;
70 | color: var(--b-alpha-40);
71 | letter-spacing: 0;
72 | }
73 |
74 | .tree_item_status {
75 | margin-top: 8px;
76 | width: 42px;
77 | height: 20px;
78 | display: flex;
79 | align-items: center;
80 | justify-content: center;
81 | color: var(--b-alpha-60);
82 | border: 1px solid var(--b-alpha-60);
83 | border-radius: 10px;
84 | letter-spacing: 0;
85 | font-size: 10px;
86 | font-weight: bold;
87 |
88 | }
89 |
90 | .tree_item_type {
91 | width: 100%;
92 | display: flex;
93 | align-items: center;
94 | justify-content: flex-end;
95 | font-style: oblique;
96 | font-size: 14px;
97 | color: var(--b-alpha-70);
98 | }
99 |
100 | .tree_item_content {
101 | color: var(--b-alpha-80);
102 | white-space: pre-wrap;
103 | }
104 |
105 | .tree_item:hover {
106 | transform: translateX(8px);
107 | }
108 |
109 | .tree_item:hover .tree_item_time,
110 | .tree_item:hover .tree_item_content,
111 | .tree_item:hover .tree_item_type {
112 | color: var(--purple-primary);
113 | }
114 |
115 | @media screen and (max-width: 1000px) {
116 | .content {
117 | width: 85%;
118 | }
119 | }
120 |
121 | @media screen and (max-width: 800px) {
122 | .content {
123 | width: 100%;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/app/utils/CustomHooks/usePageSize.ts:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 |
3 | const usePageSize = (props: { id: string }) => {
4 | const { id } = props;
5 | const [pageWidth, setPageWidth] = useState(0);
6 | const [loading, setLoading] = useState(true);
7 | const pageSize = () => {
8 | setPageWidth(document.getElementById(id)?.offsetWidth || 0);
9 | setLoading(false);
10 | };
11 |
12 | useEffect(() => {
13 | window.addEventListener("resize", pageSize);
14 | pageSize();
15 |
16 | return () => {
17 | window.removeEventListener("resize", pageSize);
18 | };
19 | }, []);
20 |
21 | return {
22 | pageWidth: pageWidth - 3,
23 | pageSizeLoading: loading,
24 | };
25 | };
26 |
27 | export default usePageSize;
28 |
--------------------------------------------------------------------------------
/app/utils/CustomHooks/useWindow.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Descripttion:
3 | * @version:
4 | * @Author: WangPeng
5 | * @Date: 2023-05-25 15:08:05
6 | * @LastEditors: WangPeng
7 | * @LastEditTime: 2023-12-20 10:29:45
8 | */
9 | import { useEffect } from 'react';
10 |
11 | function useWindow(props: { callback: Function; }) {
12 | const { callback } = props;
13 | useEffect(() => {
14 | // 客户端环境下才执行
15 | if (typeof window !== 'undefined') {
16 | callback && callback()
17 | }
18 | }, []);
19 |
20 | return {};
21 | }
22 |
23 | export default useWindow;
--------------------------------------------------------------------------------
/app/utils/classicUtils.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Descripttion: 此utils存放经典的utils方法函数
3 | * @version:
4 | * @Author: WangPeng
5 | * @Date: 2022-01-13 11:29:46
6 | * @LastEditors: WangPeng
7 | * @LastEditTime: 2022-01-13 14:06:05
8 | */
9 |
10 | /**
11 | * @desc 用于进行三次缓动。
12 | * @param {number} t 此参数保存动画开始的指定时间。例如,如果t的值为0,则表示动画刚刚开始。
13 | * @param {number} b 该参数保存对象在x轴上的指定起始位置。例如,如果b的值为10,则表示对象在x坐标上的起始位置为10。
14 | * @param {number} c 此参数保存对象的指定值更改。例如,如果c的值为30,则意味着对象必须向右移动30,以40结尾。
15 | * @param {number} d 此参数保留整个过程的指定持续时间。例如,如果d的值为2,则表示对象有2秒的时间来执行从10到40的运动。
16 | * @returns 此方法返回对象的缓和位置,即对象在特定时间的位置。
17 | */
18 | export const easeInOutCubic = (t: number, b: number, c: number, d: number) => {
19 | const cc = c - b;
20 | t /= d / 2;
21 | if (t < 1) {
22 | return (cc / 2) * t * t * t + b;
23 | }
24 | // eslint-disable-next-line no-return-assign
25 | return (cc / 2) * ((t -= 2) * t * t + 2) + b;
26 | };
27 |
--------------------------------------------------------------------------------
/app/utils/cloneUtils/throttleByAnimationFrame.ts:
--------------------------------------------------------------------------------
1 | import raf from 'rc-util/lib/raf';
2 |
3 | /**
4 | * 使用动画帧进行节流处理
5 | *
6 | * @param fn 需要进行节流处理的函数
7 | * @returns 返回节流处理后的函数
8 | */
9 | export function throttleByAnimationFrame(fn: (...args: any[]) => void) {
10 | let requestId: number | null;
11 |
12 | const later = (args: any[]) => () => {
13 | requestId = null;
14 | fn(...args);
15 | };
16 |
17 | const throttled = (...args: any[]) => {
18 | if (requestId == null) {
19 | requestId = raf(later(args));
20 | }
21 | };
22 |
23 | (throttled as any).cancel = () => raf.cancel(requestId!);
24 |
25 | return throttled;
26 | }
27 |
28 | /**
29 | * 使用 requestAnimationFrame 节流函数的装饰器
30 | *
31 | * @returns 返回一个节流函数
32 | */
33 | export function throttleByAnimationFrameDecorator() {
34 | return function throttle(target: any, key: string, descriptor: any) {
35 | const fn = descriptor.value;
36 | let definingProperty = false;
37 | return {
38 | configurable: true,
39 | get() {
40 | // In IE11 calling Object.defineProperty has a side-effect of evaluating the
41 | // getter for the property which is being replaced. This causes infinite
42 | // recursion and an "Out of stack space" error.
43 | // eslint-disable-next-line no-prototype-builtins
44 | if (
45 | definingProperty ||
46 | this === target.prototype ||
47 | this.hasOwnProperty(key)
48 | ) {
49 | /* istanbul ignore next */
50 | return fn;
51 | }
52 |
53 | const boundFn = throttleByAnimationFrame(fn.bind(this));
54 | definingProperty = true;
55 | Object.defineProperty(this, key, {
56 | value: boundFn,
57 | configurable: true,
58 | writable: true,
59 | });
60 | definingProperty = false;
61 | return boundFn;
62 | },
63 | };
64 | };
65 | }
66 |
--------------------------------------------------------------------------------
/app/utils/element.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Descripttion: 此utils存放元素通用的utils方法
3 | * @version:
4 | * @Author: WangPeng
5 | * @Date: 2022-01-13 11:42:16
6 | * @LastEditors: WangPeng
7 | * @LastEditTime: 2023-05-25 10:39:59
8 | */
9 |
10 | import raf from 'rc-util/lib/raf';
11 | import { easeInOutCubic } from './classicUtils';
12 |
13 | interface ScrollToOptions {
14 | /** Scroll container, default as window */
15 | getContainer?: () => HTMLElement | Window | Document;
16 | /** Scroll end callback */
17 | callback?: () => any;
18 | /** Animation duration, default as 450 */
19 | duration?: number;
20 | }
21 |
22 | /**
23 | * 判断是否为window对象
24 | * @param {any} obj
25 | * @returns {boolean}
26 | */
27 | export const isWindow = (obj: any) => {
28 | return obj !== null && obj !== undefined && obj === obj.window;
29 | };
30 |
31 | /**
32 | * 获取滚动元素距离顶部/左侧的距离
33 | * @param {HTMLElement | Window | Document | null} target 当前滚动的元素
34 | * @param {boolean} top 是否为纵向滚动,否则为横向滚动
35 | * @returns {number} 返回距离顶部/左侧的距离
36 | */
37 | export const getScroll = (
38 | target: HTMLElement | Window | Document | null,
39 | top: boolean,
40 | ): number => {
41 | if (typeof window === 'undefined') {
42 | return 0;
43 | }
44 | const method = top ? 'scrollTop' : 'scrollLeft';
45 | let result = 0;
46 | if (isWindow(target)) {
47 | result = (target as Window)[top ? 'pageYOffset' : 'pageXOffset'];
48 | } else if (target instanceof Document) {
49 | result = target.documentElement[method];
50 | } else if (target) {
51 | result = (target as HTMLElement)[method];
52 | }
53 | if (target && !isWindow(target) && typeof result !== 'number') {
54 | result = ((target as HTMLElement).ownerDocument || (target as Document))
55 | .documentElement?.[method];
56 | }
57 | return result;
58 | };
59 |
60 | /**
61 | * 回到顶部事件 还可以滚动到指定位置
62 | * @param {number} y
63 | * @param options
64 | */
65 | export const scrollTo = (y: number, options: ScrollToOptions = {}) => {
66 | const { getContainer = () => window, callback, duration = 450 } = options;
67 | const container = getContainer();
68 | const scrollTop = getScroll(container, true);
69 | const startTime = Date.now();
70 |
71 | const frameFunc = () => {
72 | const timestamp = Date.now();
73 | const time = timestamp - startTime;
74 | const nextScrollTop = easeInOutCubic(
75 | time > duration ? duration : time,
76 | scrollTop,
77 | y,
78 | duration,
79 | );
80 | if (isWindow(container)) {
81 | (container as Window).scrollTo(window.pageXOffset, nextScrollTop);
82 | } else if (
83 | container instanceof HTMLDocument ||
84 | container.constructor.name === 'HTMLDocument'
85 | ) {
86 | (container as HTMLDocument).documentElement.scrollTop = nextScrollTop;
87 | } else {
88 | (container as HTMLElement).scrollTop = nextScrollTop;
89 | }
90 | if (time < duration) {
91 | raf(frameFunc);
92 | } else if (typeof callback === 'function') {
93 | callback();
94 | }
95 | };
96 | raf(frameFunc);
97 | };
98 |
--------------------------------------------------------------------------------
/app/utils/elementUtils.ts:
--------------------------------------------------------------------------------
1 | // 全局滚动的盒子
2 | export const layoutContent: any = () => {
3 | // return document.getElementById('__next');
4 | return document.body;
5 | };
6 | // 全局导航盒子
7 | export const layoutNav: any = () => {
8 | return document.getElementById('layout_nav');
9 | };
10 | // 记录上一次滚动的位置
11 | let lastScrollPos = 0;
12 | let timerId;
13 |
14 | // 取消全局导航的样式
15 | export const removeLayoutNavStyle = () => {
16 | if (!layoutNav()) return;
17 | layoutNav()?.classList.remove('nav_active');
18 | };
19 |
20 | // 设置全局导航样式
21 | export const addLayoutNavStyle = () => {
22 | if (!layoutNav()) return;
23 | layoutNav()?.classList.add('nav_active');
24 | };
25 |
26 | // 设置全局导航样式
27 | export const addNavItemStyle = () => {
28 | const domList = Array.from(document.querySelectorAll('.nav_item_text'));
29 | domList.forEach((v: any) => v?.classList.add('nav_item_block'));
30 | if (!layoutNav()) return;
31 | layoutNav()?.classList.add('nav_active_border');
32 | };
33 |
34 | // 取消全局导航样式
35 | export const removeNavItemStyle = () => {
36 | const domList = Array.from(document.querySelectorAll('.nav_item_text'));
37 | domList.forEach((v: any) => v?.classList.remove('nav_item_block'));
38 | if (!layoutNav()) return;
39 | layoutNav()?.classList.remove('nav_active_border');
40 | };
41 |
42 | // 页面滚动事件
43 | export const pageScroll = () => {
44 | if (!layoutNav() || !layoutContent()) return;
45 | timerId && clearTimeout(timerId);
46 | if (layoutContent()?.scrollTop && layoutContent().scrollTop > 50) {
47 | layoutNav()?.classList.add('nav_active');
48 | if (lastScrollPos - layoutContent()?.scrollTop > 30) {
49 | lastScrollPos = layoutContent()?.scrollTop
50 | layoutNav()?.classList.remove('nav_none');
51 | }
52 | if (layoutContent()?.scrollTop > 30 && layoutContent()?.scrollTop - lastScrollPos > 30) {
53 | lastScrollPos = layoutContent()?.scrollTop
54 | layoutNav()?.classList.add('nav_none');
55 | }
56 | } else {
57 | layoutNav()?.classList.remove('nav_active');
58 | }
59 | timerId = setTimeout(() => {
60 | layoutNav()?.classList.remove('nav_none');
61 | }, 100);
62 | };
63 |
64 | // 设置页面滚动事件
65 | export const bindHandleScroll = (callback?) => {
66 | if (!layoutContent()) return;
67 | layoutContent().addEventListener('scroll', callback || pageScroll, false);
68 | };
69 |
70 | // 卸载页面滚动事件
71 | export const removeScroll = (callback?) => {
72 | if (!layoutContent()) return;
73 | layoutContent().removeEventListener('scroll', callback || pageScroll, false);
74 | lastScrollPos = 0;
75 | };
76 |
77 | // 回到顶部
78 | export const routeChangeComplete = () => {
79 | (document.body || window).scrollTo(0, 0);
80 | };
81 |
--------------------------------------------------------------------------------
/app/utils/httpClient/apis/blog.ts:
--------------------------------------------------------------------------------
1 | const blogApi = {
2 | // 文章列表
3 | all_blog_List: "/getClassifyList",
4 | // 文章页码列表
5 | all_blog_PageList: "/getClassifyListPage",
6 | // 文章分类数量
7 | all_blog_ClassifyNum: "/getClassifyNum",
8 | // 获取文章详情
9 | all_blog_ClassifyDetail: "/getClassifyDetails",
10 | // 获取上一篇和下一篇
11 | all_blog_NextOrPrev: "/getClassifyDetailsFooter",
12 | // 关键字获取列表
13 | all_blog_KeyworkList: "/getSearchClassifyList",
14 | // 获取文归档列表
15 | all_blog_archive_list: "/getArchive",
16 | };
17 |
18 | export default blogApi;
19 |
--------------------------------------------------------------------------------
/app/utils/httpClient/apis/index.ts:
--------------------------------------------------------------------------------
1 | import blogApi from "./blog";
2 | import secretApi from "./secret";
3 | import userApi from "./user";
4 | import photographyApi from "./photography";
5 | import wallpaperApi from "./wallpaper";
6 | import newsApi from "./news";
7 | import videoApi from "./video";
8 |
9 | export default {
10 | ...blogApi,
11 | ...secretApi,
12 | ...userApi,
13 | ...photographyApi,
14 | ...wallpaperApi,
15 | ...newsApi,
16 | ...videoApi,
17 | };
18 |
--------------------------------------------------------------------------------
/app/utils/httpClient/apis/news.ts:
--------------------------------------------------------------------------------
1 | const newsApi = {
2 | // 百度热搜
3 | apiRender_news_baidu: "/news/baidu",
4 | // 微博热搜
5 | apiRender_news_weibo: "/news/weibo",
6 | // 今日头条热搜
7 | apiRender_news_toutiao: "/news/toutiao",
8 | // 知乎热搜
9 | apiRender_news_zhihu: "/news/zhihu",
10 | // 掘金热搜
11 | apiRender_news_juejin: "/news/juejin",
12 | // 澎湃热搜
13 | apiRender_news_thepaper: "/news/thepaper",
14 | // 36kr热搜
15 | apiRender_news_36kr: "/news/36kr",
16 | // 网易热搜
17 | apiRender_news_netease: "/news/netease",
18 | // 腾讯热搜
19 | apiRender_news_tencent: "/news/tencent",
20 | // 少数派热搜
21 | apiRender_news_sspai: "/news/sspai",
22 | // 抖音热搜
23 | apiRender_news_douyin: "/news/douyin",
24 | // 百度贴吧热搜
25 | apiRender_news_tieba: "/news/tieba",
26 | // it之家热搜
27 | apiRender_news_ithome: "/news/ithome",
28 | };
29 |
30 | export const newsApiType = {
31 | apiRender_news_douyin: "抖音热搜",
32 | apiRender_news_baidu: "百度热搜",
33 | apiRender_news_weibo: "微博热搜",
34 | apiRender_news_toutiao: "今日头条热搜",
35 | apiRender_news_juejin: "掘金热搜",
36 | apiRender_news_zhihu: "知乎热搜",
37 | apiRender_news_thepaper: "澎湃热搜",
38 | apiRender_news_36kr: "36kr热搜",
39 | apiRender_news_netease: "网易热搜",
40 | apiRender_news_tencent: "腾讯热搜",
41 | apiRender_news_sspai: "少数派热搜",
42 | apiRender_news_tieba: "百度贴吧热搜",
43 | apiRender_news_ithome: "it之家热搜",
44 | };
45 |
46 | export default newsApi;
47 |
--------------------------------------------------------------------------------
/app/utils/httpClient/apis/photography.ts:
--------------------------------------------------------------------------------
1 | const photographyApi = {
2 | // 获取摄影列表
3 | all_photography_List: "/getPhotographyList",
4 | };
5 |
6 | export default photographyApi;
7 |
--------------------------------------------------------------------------------
/app/utils/httpClient/apis/secret.ts:
--------------------------------------------------------------------------------
1 | const secretApi = {
2 | // 获取树洞列表
3 | all_secret_List: "/getSecretList",
4 | };
5 |
6 | export default secretApi;
7 |
--------------------------------------------------------------------------------
/app/utils/httpClient/apis/user.ts:
--------------------------------------------------------------------------------
1 | const userApi = {
2 | // 获取访客列表
3 | all_user_visitor_List: "/getVisitorList",
4 | // 获取友情链接
5 | all_user_friendly_Links: "/getFriendlyLinks",
6 | };
7 | export default userApi;
8 |
--------------------------------------------------------------------------------
/app/utils/httpClient/apis/video.ts:
--------------------------------------------------------------------------------
1 | const videoApi = {
2 | // 获取pexels视频列表
3 | apiRender_video_List: "/movies/pexels-popular",
4 | // 搜索pexels视频列表
5 | apiRender_video_search_List: "/movies/pexels-search",
6 | };
7 | export default videoApi;
8 |
--------------------------------------------------------------------------------
/app/utils/httpClient/apis/wallpaper.ts:
--------------------------------------------------------------------------------
1 | const wallpaperApi = {
2 | // 获取360壁纸分类
3 | apiRender_picture_type360: "/picture/type-list-360",
4 | // 获取360壁纸列表
5 | apiRender_picture_list360: "/picture/list-360",
6 | };
7 |
8 | export default wallpaperApi;
9 |
--------------------------------------------------------------------------------
/app/utils/httpClient/request.ts:
--------------------------------------------------------------------------------
1 | import { objectToQueryString } from "@utils/dataUtils";
2 | import apis from "./apis";
3 |
4 | const baseObj = {
5 | all: "https://shimmer.wp-boke.work/api",
6 | apiRender: "https://api-render.wp-boke.work",
7 | };
8 |
9 | type ApiKey = keyof typeof apis;
10 |
11 | /**
12 | * 请求数据
13 | */
14 | const getData = async ({
15 | type,
16 | params = null,
17 | config = { next: { revalidate: 3600 } },
18 | }: // config = { cache: "force-cache" },
19 | {
20 | type: ApiKey;
21 | params?: { [key: string]: any } | null | undefined;
22 | config?: RequestInit | undefined;
23 | }) => {
24 | try {
25 | const BASE_URL = baseObj[type.split("_")[0]];
26 | const queryString = params ? `?${objectToQueryString(params)}` : "";
27 | const url = `${BASE_URL}${apis[type]}${queryString}`;
28 | const response = await fetch(url, config);
29 | if (!response.ok) {
30 | throw new Error(`Network response was not ok: ${response.status}`);
31 | }
32 | return await response.json();
33 | } catch (error) {
34 | console.error("Error fetching data:", error);
35 | throw error; // re-throw the error to propagate it to the caller
36 | }
37 | };
38 |
39 | export default getData;
40 |
--------------------------------------------------------------------------------
/app/utils/local.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Descripttion: 本地存储方法集
3 | * @version:
4 | * @Author: WangPeng
5 | * @Date: 2022-04-06 10:45:51
6 | * @LastEditors: WangPeng
7 | * @LastEditTime: 2023-01-13 14:18:32
8 | */
9 | // 获取本地local存储
10 | export function localGet(key: string) {
11 | const value = window.localStorage.getItem(key) as any;
12 | try {
13 | return JSON.parse(value);
14 | } catch (error) {
15 | return value;
16 | }
17 | }
18 | // 设置本地local存储
19 | export function localSet(key: string, value: any) {
20 | window.localStorage.setItem(key, JSON.stringify(value));
21 | }
22 | // 删除本地local存储
23 | export function localRemove(key: string) {
24 | window.localStorage.removeItem(key);
25 | }
26 |
27 | // 获取本地session存储
28 | export function sessionGet(key: string) {
29 | const value = window.sessionStorage.getItem(key) as any;
30 |
31 | try {
32 | return JSON.parse(value);
33 | } catch (error) {
34 | return value;
35 | }
36 | }
37 | // 设置本地session存储
38 | export function sessionSet(key: string, value: any) {
39 | window.sessionStorage.setItem(key, JSON.stringify(value));
40 | }
41 | // 删除本地session存储
42 | export function sessionRemove(key: string) {
43 | window.sessionStorage.removeItem(key);
44 | }
45 |
--------------------------------------------------------------------------------
/app/visitor/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useContext, useEffect, useState } from "react";
3 | import { useDebounceEffect, useGetState, useMount } from "ahooks";
4 | import { Spin } from "antd";
5 | import { LayoutContext } from "@/store/layoutStore";
6 | import {
7 | addNavItemStyle,
8 | bindHandleScroll,
9 | removeNavItemStyle,
10 | removeScroll,
11 | routeChangeComplete,
12 | } from "@/utils/elementUtils";
13 | import { formatDate } from "@/utils/dataUtils";
14 | import { getRandomColor } from "@utils/dataUtils";
15 | import getDataApi from "@/utils/httpClient/request";
16 | import PagerComponent from "@components/PagerComponent";
17 | import withLoading from "@components/WithLoading";
18 | import useChangeLoading from "@components/WithLoading/useChangeLoading";
19 | import style from "./visitor.module.css";
20 |
21 | const Visitor = (props) => {
22 | const { theme } = useContext(LayoutContext);
23 | const [list, setList] = useState([]);
24 | const [loading, setLoading] = useState(true);
25 | const [page, setPage, getPage] = useGetState(1);
26 | const [total, setTotal] = useState(0);
27 |
28 | const getData = async () => {
29 | setLoading(true);
30 | const posts = await getDataApi({
31 | type: "all_user_visitor_List",
32 | params: { page: getPage() },
33 | });
34 |
35 | setList(posts.data);
36 | setTotal(posts.meta.total);
37 | setLoading(false);
38 | };
39 |
40 | useMount(() => {
41 | getData();
42 | });
43 |
44 | // 获取列表数据
45 | useDebounceEffect(
46 | () => {
47 | getData();
48 | },
49 | [page],
50 | {
51 | wait: 800,
52 | }
53 | );
54 |
55 | useEffect(() => {
56 | addNavItemStyle();
57 | bindHandleScroll();
58 |
59 | return () => {
60 | removeNavItemStyle();
61 | removeScroll();
62 | };
63 | }, []);
64 |
65 | useChangeLoading({ ...props, name: "visitor" });
66 |
67 | return (
68 |
69 |
访客列表
70 |
71 |
72 | {list?.map((v: any) => (
73 |
83 |
84 |
85 | {formatDate(v.last_visited_time, "yyyy.MM.dd HH:mm")}
86 |
87 |
IP:{v.visitor_ip}
88 |
89 |
90 |
来访次数:{v.visits}
91 |
92 | 首次来访时间:{formatDate(v.create_time, "yyyy.MM.dd HH:mm")}
93 |
94 |
95 | 设备类型:{v.device_type}
96 |
97 |
设备名称:{v.os_name}
98 |
99 | 设备版本:{v.os_version}
100 |
101 |
102 |
103 | ))}
104 |
105 |
106 | {Boolean(total) && (
107 |
108 |
{
113 | setPage(v);
114 | routeChangeComplete();
115 | }}
116 | />
117 |
118 | )}
119 |
120 | );
121 | };
122 |
123 | export default withLoading(Visitor);
124 |
--------------------------------------------------------------------------------
/app/visitor/visitor.module.css:
--------------------------------------------------------------------------------
1 | .visitor {
2 | width: 100%;
3 | flex: 1;
4 | padding-top: 70px;
5 | background-color: var(--bg-w-pure);
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | }
10 |
11 | .title {
12 | padding: 36px;
13 | font-size: 24px;
14 | font-weight: 700;
15 | color: var(--b-alpha-80);
16 | text-align: center;
17 | }
18 |
19 | .content {
20 | display: flex;
21 | flex-direction: column;
22 | align-items: center;
23 | max-width: 1000px;
24 | min-height: 200px;
25 | }
26 |
27 | .item {
28 | width: 100%;
29 | display: flex;
30 | flex-direction: column;
31 | padding: 24px 24px 16px;
32 | margin-bottom: 24px;
33 | border-radius: 8px;
34 | }
35 |
36 | .item:last-child{
37 | margin-bottom: 0;
38 | }
39 |
40 | .item_header{
41 | padding-bottom: 12px;
42 | border-bottom: 1px solid var(--b-alpha-10);
43 | display: flex;
44 | align-items: center;
45 | justify-content: space-between;
46 | }
47 |
48 | .item_time{
49 | font-size: 16px;
50 | font-weight: 700;
51 | color: var(--b-alpha-80);
52 | }
53 |
54 | .item_ip{
55 | font-size: 16px;
56 | font-weight: 700;
57 | color: var(--b-alpha-80);
58 | }
59 |
60 | .item_content{
61 | padding-top: 12px;
62 | display: flex;
63 | flex-wrap: wrap;
64 | }
65 |
66 | .item_con_item{
67 | padding: 4px 8px;
68 | margin: 0 8px 8px 0;
69 | background-color: var(--purple-border-hover);
70 | font-size: 14px;
71 | border-radius: 4px;
72 | color: var(--b-alpha-70);
73 | font-weight: 500;
74 | }
75 |
76 | .pagination {
77 | width: 100%;
78 | height: 80px;
79 | display: flex;
80 | align-items: center;
81 | justify-content: center;
82 | }
83 |
84 | @media screen and (min-width: 1201px) {
85 | .content {
86 | width: 1000px;
87 | }
88 | }
89 |
90 | @media screen and (max-width: 1200px) {
91 | .content {
92 | width: 700px;
93 | }
94 | }
95 |
96 | @media screen and (max-width: 1000px) {
97 | .content {
98 | width: calc(100% - 40px);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/app/wallpaper/page.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import getDataApi from "@utils/httpClient/request";
3 | import PostClient from "./post-client";
4 |
5 | // 获取360壁纸类别
6 | async function getType360() {
7 | const { data } = await getDataApi({ type: "apiRender_picture_type360" });
8 |
9 | return data;
10 | }
11 |
12 | const Wallpaper = async () => {
13 | const typeList = await getType360();
14 |
15 | return ;
16 | };
17 |
18 | export default Wallpaper;
19 |
--------------------------------------------------------------------------------
/app/wallpaper/post-client.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect, useState } from "react";
3 | import { Image as AntImage, Spin } from "antd";
4 | import {
5 | addNavItemStyle,
6 | bindHandleScroll,
7 | removeNavItemStyle,
8 | removeScroll,
9 | routeChangeComplete,
10 | } from "@utils/elementUtils";
11 | import getDataApi from "@utils/httpClient/request";
12 | import PagerComponent from "@components/PagerComponent";
13 | import withLoading from "@components/WithLoading";
14 | import useChangeLoading from "@components/WithLoading/useChangeLoading";
15 | import styles from "./wallpaper.module.css";
16 |
17 | const PostClient = (props) => {
18 | const { typeList } = props;
19 | const [pictureList, setPictureList] = useState([]);
20 | const [currentType, setCurrentType] = useState(
21 | typeList[0].id
22 | );
23 | const [page, setPage] = useState(1);
24 | const [total, setTotal] = useState(0);
25 | const [loading, setLoading] = useState(true);
26 |
27 | useEffect(() => {
28 | const getData = async () => {
29 | setLoading(true);
30 | const { data } = await getDataApi({
31 | type: "apiRender_picture_list360",
32 | params: { page_size: 30, type_id: currentType, page },
33 | });
34 | setPictureList(data.data);
35 | setTotal(data.meta.total);
36 | setLoading(false);
37 | };
38 | getData();
39 | }, [currentType, page]);
40 |
41 | useEffect(() => {
42 | addNavItemStyle();
43 | bindHandleScroll();
44 |
45 | return () => {
46 | removeNavItemStyle();
47 | removeScroll();
48 | };
49 | }, []);
50 |
51 | useChangeLoading({ ...props, name: "wallpaper" });
52 |
53 | return (
54 |
57 |
58 | {typeList?.map((item) => (
59 |
{
65 | setCurrentType(item.id);
66 | setPage(1);
67 | setTotal(0);
68 | }}
69 | >
70 | {item.title}
71 |
72 | ))}
73 |
74 |
77 | {loading ? (
78 |
79 | ) : (
80 |
81 | {pictureList?.map((item: any) => (
82 |
90 | ))}
91 |
92 | )}
93 |
94 | {Boolean(total) && (
95 |
96 |
{
101 | setPage(v);
102 | routeChangeComplete();
103 | }}
104 | />
105 |
106 | )}
107 |
108 | );
109 | };
110 |
111 | export default withLoading(PostClient);
112 |
--------------------------------------------------------------------------------
/app/wallpaper/wallpaper.module.css:
--------------------------------------------------------------------------------
1 | .wallpaper {
2 | width: 100%;
3 | flex: 1;
4 | padding-top: 70px;
5 | background-color: var(--bg-w-pure);
6 | }
7 |
8 | .type_box {
9 | width: 70%;
10 | max-width: 1000px;
11 | margin: 0 auto;
12 | padding: 24px;
13 | display: grid;
14 | gap: 15px;
15 | grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
16 | }
17 |
18 | .type_item {
19 | white-space: nowrap;
20 | display: flex;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: 12px;
24 | padding: 0 8px;
25 | height: 28px;
26 | border-radius: 13px;
27 | box-shadow: 10px 10px 5px var(--b-alpha-10);
28 | color: var(--b-alpha-80);
29 | cursor: pointer;
30 | transition: 0.25s;
31 | }
32 |
33 | .type_item:hover {
34 | background-color: var(--purple-text);
35 | box-shadow: 10px 10px 5px var(--purple-text-hover);
36 | color: var(--w-alpha);
37 | font-weight: 500;
38 | }
39 |
40 | .type_item_active {
41 | background-color: var(--purple-text-active);
42 | box-shadow: 10px 10px 5px var(--purple-text-hover);
43 | color: var(--w-alpha);
44 | font-weight: 500;
45 | }
46 |
47 | .picture_box {
48 | width: 70%;
49 | max-width: 1000px;
50 | margin: 0 auto;
51 | padding: 24px;
52 | display: grid;
53 | gap: 15px;
54 | grid-template-columns: repeat(4, 1fr);
55 | }
56 |
57 | .picture_box_loading {
58 | width: calc(70% - 48px);
59 | max-width: 1000px;
60 | margin: 0 auto;
61 | padding: 24px;
62 | background-color: var(--b-alpha-5);
63 | display: flex;
64 | align-items: center;
65 | justify-content: center;
66 | min-height: 200px;
67 | border-radius: 14px;
68 | }
69 |
70 | .picture_item {
71 | width: 100%;
72 | height: auto;
73 | border-radius: 12px;
74 | display: flex;
75 | align-items: center;
76 | justify-content: center;
77 | }
78 |
79 | .pager_box {
80 | width: 100%;
81 | margin: 0 auto;
82 | max-width: 1000px;
83 | padding: 0 24px 24px;
84 | display: flex;
85 | align-items: center;
86 | justify-content: flex-end;
87 | }
88 |
89 | @media screen and (max-width: 1600px) {
90 | .picture_box {
91 | grid-template-columns: repeat(3, 1fr);
92 | }
93 | }
94 |
95 | @media screen and (max-width: 1200px) {
96 | .type_item {
97 | font-size: 14px;
98 | }
99 |
100 | .picture_box {
101 | grid-template-columns: repeat(3, 1fr);
102 | }
103 | }
104 |
105 | @media screen and (max-width: 1000px) {
106 | .type_box,
107 | .picture_box{
108 | width: 100%;
109 | }
110 |
111 | .picture_box_loading{
112 | width: calc(100% - 48px);
113 | }
114 | .type_item {
115 | font-size: 12px;
116 | }
117 | }
118 |
119 | @media screen and (max-width: 800px) {
120 | .picture_box {
121 | grid-template-columns: repeat(2, 1fr);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 |
3 | const withMDX = require("@next/mdx")();
4 |
5 | const nextConfig = {
6 | images: {
7 | remotePatterns: [
8 | {
9 | protocol: "https",
10 | hostname: "**.myqcloud.com",
11 | },
12 | {
13 | protocol: "https",
14 | hostname: "img.foreverblog.cn",
15 | },
16 | {
17 | protocol: "http",
18 | hostname: "cdn-hw-static2.shanhutech.cn",
19 | },
20 | {
21 | protocol: "https",
22 | hostname: "api-render.wp-boke.work",
23 | },
24 | {
25 | protocol: "https",
26 | hostname: "images.pexels.com",
27 | },
28 | {
29 | protocol: "https",
30 | hostname: "github.githubassets.com",
31 | },
32 | ],
33 | },
34 | async rewrites() {
35 | return [
36 | {
37 | source: "/sitemap",
38 | destination: "/api/sitemap",
39 | },
40 | {
41 | source: "/sitemap.xml",
42 | destination: "/api/sitemap",
43 | },
44 | {
45 | source: "/dead-chain",
46 | destination: "/api/dead-chain",
47 | },
48 | {
49 | source: "/dead-chain.xml",
50 | destination: "/api/dead-chain",
51 | },
52 | {
53 | source: "/rss",
54 | destination: "/api/rss",
55 | },
56 | {
57 | source: "/rss.xml",
58 | destination: "/api/rss",
59 | },
60 | {
61 | source: "/js/:path*",
62 | destination:
63 | "https://wp-1302605407.cos.ap-beijing.myqcloud.com/js/:path*",
64 | },
65 | ];
66 | },
67 | };
68 |
69 | module.exports = withMDX(nextConfig);
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "engines": {
3 | "node": "18.19.0"
4 | },
5 | "name": "next-blog-14",
6 | "version": "0.1.0",
7 | "private": true,
8 | "scripts": {
9 | "dev": "next dev -p 6006",
10 | "build": "next build",
11 | "start": "next start -p 6006",
12 | "lint": "next lint",
13 | "server": "next build && next start -p 6006"
14 | },
15 | "dependencies": {
16 | "@ant-design/icons": "^5.2.6",
17 | "@mdx-js/loader": "^3.0.0",
18 | "@next/mdx": "^14.0.4",
19 | "@types/fingerprintjs2": "^2.0.0",
20 | "@types/mdx": "^2.0.10",
21 | "@vercel/analytics": "^1.1.1",
22 | "@vercel/speed-insights": "^1.0.2",
23 | "ahooks": "^3.7.8",
24 | "antd": "^5.12.7",
25 | "fingerprintjs2": "^2.1.4",
26 | "highlight.js": "^11.9.0",
27 | "next": "15.3.1",
28 | "next-mdx-remote": "^4.4.1",
29 | "rc-util": "^5.38.1",
30 | "react": "^18",
31 | "react-copy-to-clipboard": "^5.1.0",
32 | "react-dom": "^18",
33 | "remark-gfm": "^3.0.1",
34 | "remark-math": "^6.0.0",
35 | "rss": "^1.2.2",
36 | "sharp": "^0.33.1",
37 | "sitemap": "^7.1.1",
38 | "twikoo": "latest",
39 | "typed.js": "^2.1.0",
40 | "xgplayer": "^3.0.18"
41 | },
42 | "devDependencies": {
43 | "@types/node": "^20",
44 | "@types/react": "^18",
45 | "@types/react-copy-to-clipboard": "^5.0.7",
46 | "@types/react-dom": "^18",
47 | "autoprefixer": "^10.4.16",
48 | "eslint": "^8",
49 | "eslint-config-next": "14.0.4",
50 | "postcss": "^8",
51 | "tailwindcss": "^3.4.0",
52 | "typescript": "^5"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/dead-chain.xml:
--------------------------------------------------------------------------------
1 | https://wp-boke.work/blog-details/38https://wp-boke.work/secrethttps://wp-boke.work/blog-details/45
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/about_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/about_3.jpg
--------------------------------------------------------------------------------
/public/images/bg00001.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/bg00001.jpeg
--------------------------------------------------------------------------------
/public/images/bg00002.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/bg00002.jpeg
--------------------------------------------------------------------------------
/public/images/bg00003.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/bg00003.jpeg
--------------------------------------------------------------------------------
/public/images/bg00004.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/bg00004.jpeg
--------------------------------------------------------------------------------
/public/images/bg00005.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/bg00005.jpeg
--------------------------------------------------------------------------------
/public/images/bg00007.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/bg00007.jpeg
--------------------------------------------------------------------------------
/public/images/bg00008.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/bg00008.jpeg
--------------------------------------------------------------------------------
/public/images/bg00009.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/bg00009.jpeg
--------------------------------------------------------------------------------
/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/logo.png
--------------------------------------------------------------------------------
/public/images/logo_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/logo_black.png
--------------------------------------------------------------------------------
/public/images/logo_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/logo_white.png
--------------------------------------------------------------------------------
/public/images/travelling-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/travelling-dark.png
--------------------------------------------------------------------------------
/public/images/travelling-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp0403/blog-nextjs-frontend/b494f4dd8b7b6cf74f2589d44af6e2bc0d844be4/public/images/travelling-light.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 | Allow:
4 | Sitemap: https://wp-boke.work/sitemap.xml
--------------------------------------------------------------------------------
/public/sitemap.xml:
--------------------------------------------------------------------------------
1 | https://wp-boke.work/https://wp-boke.work/archivehttps://wp-boke.work/tree-holehttps://wp-boke.work/photographyhttps://wp-boke.work/abouthttps://wp-boke.work/resumehttps://wp-boke.work/friendly-linkshttps://wp-boke.work/disclaimershttps://wp-boke.work/copyright-noticehttps://wp-boke.work/blog-details/61https://wp-boke.work/blog-details/43https://wp-boke.work/blog-details/25https://wp-boke.work/blog-details/66https://wp-boke.work/blog-details/64https://wp-boke.work/blog-details/63https://wp-boke.work/blog-details/60https://wp-boke.work/blog-details/59https://wp-boke.work/blog-details/57https://wp-boke.work/blog-details/56https://wp-boke.work/blog-details/55https://wp-boke.work/blog-details/54https://wp-boke.work/blog-details/53https://wp-boke.work/blog-details/50https://wp-boke.work/blog-details/49https://wp-boke.work/blog-details/48https://wp-boke.work/blog-details/46https://wp-boke.work/blog-details/41https://wp-boke.work/blog-details/39https://wp-boke.work/blog-details/15https://wp-boke.work/blog-details/13https://wp-boke.work/blog-details/11https://wp-boke.work/blog-details/10https://wp-boke.work/blog-details/52https://wp-boke.work/blog-details/51
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from 'tailwindcss'
2 |
3 | const config: Config = {
4 | content: [
5 | './pages/**/*.{js,ts,jsx,tsx,mdx}',
6 | './components/**/*.{js,ts,jsx,tsx,mdx}',
7 | './app/**/*.{js,ts,jsx,tsx,mdx}',
8 | ],
9 | theme: {
10 | extend: {
11 | backgroundImage: {
12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
13 | 'gradient-conic':
14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
15 | },
16 | },
17 | },
18 | plugins: [],
19 | }
20 | export default config
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noImplicitAny": false, // 不需要显式的声明变量的类型any
4 | "target": "ESNext",
5 | "lib": ["dom", "dom.iterable", "esnext"],
6 | "allowJs": true,
7 | "skipLibCheck": true,
8 | "strict": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "bundler",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ],
22 | "baseUrl": "./app",
23 | "paths": {
24 | "@/*": ["./*"],
25 | "@utils/*": ["./utils/*"],
26 | "@components/*": ["./components/*"],
27 | "@styles/*": ["./styles/*"],
28 | "@store/*": ["./store/*"],
29 | "@public/*": ["../public/*"],
30 | }
31 | },
32 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
33 | "exclude": ["node_modules"]
34 | }
35 |
--------------------------------------------------------------------------------