Skip to content

Commit 1f65c0c

Browse files
committed
frontend: add loading
1 parent 17a7603 commit 1f65c0c

File tree

5 files changed

+175
-5
lines changed

5 files changed

+175
-5
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { useEffect, useRef, useState } from "react";
2+
3+
const TopLoadingBar = () => {
4+
const [isVisible, setIsVisible] = useState(false);
5+
const [progress, setProgress] = useState(0);
6+
const intervalRef = useRef(null);
7+
8+
useEffect(() => {
9+
// 监听全局事件
10+
const handleShow = () => {
11+
setIsVisible(true);
12+
setProgress(0);
13+
14+
// 清除可能存在的旧interval
15+
if (intervalRef.current) {
16+
clearInterval(intervalRef.current);
17+
}
18+
19+
// 模拟进度
20+
let currentProgress = 0;
21+
intervalRef.current = setInterval(() => {
22+
currentProgress += Math.random() * 10;
23+
if (currentProgress >= 90) {
24+
clearInterval(intervalRef.current);
25+
}
26+
setProgress(currentProgress);
27+
}, 200);
28+
};
29+
30+
const handleHide = () => {
31+
// 清除进度interval
32+
if (intervalRef.current) {
33+
clearInterval(intervalRef.current);
34+
intervalRef.current = null;
35+
}
36+
setProgress(100);
37+
setTimeout(() => {
38+
setIsVisible(false);
39+
setProgress(0);
40+
}, 300);
41+
};
42+
43+
// 添加全局事件监听器
44+
window.addEventListener("loading:show", handleShow);
45+
window.addEventListener("loading:hide", handleHide);
46+
47+
return () => {
48+
// 组件卸载时清理
49+
if (intervalRef.current) {
50+
clearInterval(intervalRef.current);
51+
}
52+
window.removeEventListener("loading:show", handleShow);
53+
window.removeEventListener("loading:hide", handleHide);
54+
};
55+
}, []);
56+
57+
useEffect(() => {
58+
console.log(progress);
59+
}, [progress]);
60+
61+
if (!isVisible) return null;
62+
63+
return (
64+
<div className="top-loading-bar">
65+
<div
66+
className="loading-bar-progress"
67+
style={{ width: `${progress}%` }}
68+
></div>
69+
</div>
70+
);
71+
};
72+
73+
export default TopLoadingBar;

frontend/src/hooks/useFetchData.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// 首页数据获取
22
import { useState } from "react";
33
import { useDebouncedEffect } from "./useDebouncedEffect";
4+
import Loading from "@/utils/loading";
45

56
export default function useFetchData<T>(
67
fetchFunc: (params?: any) => Promise<any>,
@@ -52,6 +53,7 @@ export default function useFetchData<T>(
5253

5354
async function fetchData() {
5455
const { keywords, filter, current, pageSize } = searchParams;
56+
Loading.show();
5557
const { data } = await fetchFunc({
5658
...filter,
5759
keywords,
@@ -67,6 +69,7 @@ export default function useFetchData<T>(
6769
}));
6870
const result = data?.results ?? [];
6971
setTableData(result.map(mapDataFunc));
72+
Loading.hide();
7073

7174
console.log(data, result.map(mapDataFunc));
7275
}

frontend/src/index.css

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,39 @@
1-
@import "tailwindcss"
1+
@import "tailwindcss";
22

3+
/* components/TopLoadingBar.css */
4+
.top-loading-bar {
5+
position: fixed;
6+
top: 0;
7+
left: 0;
8+
width: 100%;
9+
height: 3px;
10+
background-color: transparent;
11+
z-index: 9999;
12+
overflow: hidden;
13+
}
14+
15+
.loading-bar-progress {
16+
height: 100%;
17+
background: linear-gradient(90deg, #3498db, #2ecc71, #3498db);
18+
background-size: 200% 100%;
19+
animation: gradient-animation 2s linear infinite, width-animation 0.3s ease;
20+
transition: width 0.3s ease;
21+
}
22+
23+
@keyframes gradient-animation {
24+
0% {
25+
background-position: 200% 0;
26+
}
27+
100% {
28+
background-position: -200% 0;
29+
}
30+
}
31+
32+
@keyframes width-animation {
33+
from {
34+
transform: translateX(-100%);
35+
}
36+
to {
37+
transform: translateX(0);
38+
}
39+
}

frontend/src/main.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import { RouterProvider } from "react-router";
44
import router from "./routes/routes";
55
import { App as AntdApp, Spin } from "antd";
66
import "./index.css";
7+
import TopLoadingBar from "./components/TopLoadingBar";
78

89
createRoot(document.getElementById("root")!).render(
910
<StrictMode>
10-
<Suspense fallback={<Spin />}>
11-
<AntdApp>
11+
<AntdApp>
12+
<Suspense fallback={<Spin />}>
13+
<TopLoadingBar />
1214
<RouterProvider router={router} />
13-
</AntdApp>
14-
</Suspense>
15+
</Suspense>
16+
</AntdApp>
1517
</StrictMode>
1618
);

frontend/src/utils/loading.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
class LoadingManager {
2+
constructor() {
3+
this.isShowing = false;
4+
this.queue = 0; // 支持多个并发请求
5+
}
6+
7+
show() {
8+
this.queue++;
9+
this.isShowing = true;
10+
11+
// 触发全局事件
12+
const event = new Event("loading:show");
13+
window.dispatchEvent(event);
14+
}
15+
16+
hide() {
17+
this.queue = Math.max(0, this.queue - 1);
18+
19+
if (this.queue === 0) {
20+
this.isShowing = false;
21+
// 触发全局事件
22+
const event = new Event("loading:hide");
23+
window.dispatchEvent(event);
24+
}
25+
}
26+
27+
// 强制隐藏所有加载
28+
hideAll() {
29+
this.queue = 0;
30+
this.isShowing = false;
31+
const event = new Event("loading:hide");
32+
window.dispatchEvent(event);
33+
}
34+
35+
// 获取当前状态
36+
getStatus() {
37+
return {
38+
isShowing: this.isShowing,
39+
queueCount: this.queue,
40+
};
41+
}
42+
}
43+
44+
// 创建单例实例
45+
const loadingManager = new LoadingManager();
46+
47+
// 导出常用方法
48+
export const Loading = {
49+
show: () => loadingManager.show(),
50+
hide: () => loadingManager.hide(),
51+
hideAll: () => loadingManager.hideAll(),
52+
getStatus: () => loadingManager.getStatus(),
53+
};
54+
55+
export default Loading;

0 commit comments

Comments
 (0)