Quellcode durchsuchen

feat: 完成图片展示瀑布流与响应式

Shellmiao vor 3 Jahren
Ursprung
Commit
e2b14cb58d

+ 16 - 2
src/App.tsx

@@ -2,18 +2,32 @@ import React from "react";
 import "./Global/Global.sass";
 import "react-photo-view/dist/react-photo-view.css";
 import HeaderComponent from "./Components/HeaderComponent";
-import ArticleList from "./Components/ArticleList";
+import ArticleList from "./Components/Article/ArticleList";
 import MenuComponent from "./Components/MenuComponent";
 import { Routes, Route } from "react-router-dom";
 import NotFoundComponent from "./Components/NotFoundComponent";
+import PicList from "./Components/Pic/PicList";
+import { useDispatch, useSelector } from "react-redux";
+import { selectWidth, setWidth } from "./Redux/WidthReducer";
 
 function App() {
+  const dispatch = useDispatch();
+
+  const width = useSelector(selectWidth);
+
+  const handleResize = (e: any) => {
+    dispatch(setWidth(e?.target?.innerWidth));
+  };
+
+  window.addEventListener("resize", handleResize);
+
   return (
-    <div className="App">
+    <div className="App" style={{ maxWidth: `${width}px` }}>
       <HeaderComponent />
       <MenuComponent />
       <Routes>
         <Route path="/" element={<ArticleList />}></Route>
+        <Route path="/pic" element={<PicList />}></Route>
         <Route path="*" element={<NotFoundComponent />} />
       </Routes>
     </div>

+ 15 - 4
src/Components/ArticleList.tsx → src/Components/Article/ArticleList.tsx

@@ -1,14 +1,14 @@
-import "./ArticleList.sass";
 import { useDispatch, useSelector } from "react-redux";
 import {
   fetchCodimd,
   selectAllCodimd,
   selectCodimdState,
-} from "../Redux/CodimdReducer";
+} from "../../Redux/CodimdReducer";
 import ArticleListUnitComponent from "./ArticleListUnitComponent";
 import { useEffect } from "react";
-import { Status } from "../Redux/CodimdReducer";
 import { AnyAction, Dispatch } from "@reduxjs/toolkit";
+import { Status } from "../../Redux/Store";
+import { selectWidth } from "../../Redux/WidthReducer";
 
 function getList(codimdStatus: Status, dispatch: Dispatch<AnyAction>): void {
   if (codimdStatus === "idle") {
@@ -17,6 +17,8 @@ function getList(codimdStatus: Status, dispatch: Dispatch<AnyAction>): void {
 }
 
 function ArticleList() {
+  const width = useSelector(selectWidth);
+
   const dispatch = useDispatch();
 
   const codimd = useSelector(selectAllCodimd);
@@ -42,7 +44,16 @@ function ArticleList() {
     content = <div>{articleList}</div>;
   }
 
-  return <div>{content}</div>;
+  return (
+    <div
+      style={{
+        maxWidth: `${width < 1000 ? 600 : width / 2}px`,
+        margin: "0 auto",
+      }}
+    >
+      {content}
+    </div>
+  );
 }
 
 export default ArticleList;

+ 1 - 1
src/Components/ArticleListUnitComponent.sass → src/Components/Article/ArticleListUnitComponent.sass

@@ -1,4 +1,4 @@
-@import "../Global/GlobalVar"
+@import "../../Global/GlobalVar"
 
 .article-unit
     padding-bottom: 32px

+ 0 - 0
src/Components/ArticleListUnitComponent.tsx → src/Components/Article/ArticleListUnitComponent.tsx


+ 0 - 0
src/Components/ArticleList.sass


+ 5 - 0
src/Components/MenuComponent.sass

@@ -20,6 +20,11 @@ $right: 24px
 $padding: 10px 6px
 $spacing: 64px
 
+#menu-div
+    position: fixed
+    right: 0
+    bottom: 0
+
 #menu-hamburger-div,#menu-pic-div,#menu-article-div
     background: $background
     position: absolute

+ 7 - 0
src/Components/Pic/PicComponent.sass

@@ -0,0 +1,7 @@
+.pic-component-div
+    margin-bottom: 20px
+
+.pic-component-img
+    object-fit: contain
+    border-radius: 10px
+    cursor: pointer

+ 29 - 0
src/Components/Pic/PicComponent.tsx

@@ -0,0 +1,29 @@
+import { PhotoView } from "react-photo-view";
+import "./PicComponent.sass";
+
+export type PicEle = {
+  id: string;
+  path: string;
+  name: string;
+  width: string;
+  height: string;
+  create: string;
+};
+
+function PicComponent(prop: { picEle: PicEle; width: number }) {
+  const url =
+    "https://pic.shellmiao.com/" + prop.picEle.path + "/" + prop.picEle.name;
+  return (
+    <div className="pic-component-div">
+      <PhotoView src={url}>
+        <img
+          src={url}
+          alt=""
+          className="pic-component-img"
+          style={{ width: `${prop.width}px` }}
+        />
+      </PhotoView>
+    </div>
+  );
+}
+export default PicComponent;

+ 6 - 0
src/Components/Pic/PicList.sass

@@ -0,0 +1,6 @@
+#left
+    float: left
+    display: inline
+#right
+    float: right
+    display: inline

+ 85 - 0
src/Components/Pic/PicList.tsx

@@ -0,0 +1,85 @@
+import { AnyAction, Dispatch } from "@reduxjs/toolkit";
+import { useEffect } from "react";
+import { PhotoProvider } from "react-photo-view";
+import { useDispatch, useSelector } from "react-redux";
+import { fetchPic, selectAllPic, selectPicState } from "../../Redux/PicReducer";
+import { Status } from "../../Redux/Store";
+import { selectWidth } from "../../Redux/WidthReducer";
+import PicComponent, { PicEle } from "./PicComponent";
+import "./PicList.sass";
+
+function getList(picStatus: Status, dispatch: Dispatch<AnyAction>): void {
+  if (picStatus === "idle") {
+    dispatch<any>(fetchPic());
+  }
+}
+
+function PicList() {
+  const width = useSelector(selectWidth);
+
+  const dispatch = useDispatch();
+
+  const picListData = useSelector(selectAllPic) as unknown as [PicEle];
+
+  const picStatus = useSelector(selectPicState);
+
+  useEffect(() => {
+    getList(picStatus, dispatch);
+  }, [picStatus, dispatch]);
+
+  let picList;
+
+  if (width <= 1400) {
+    picList = picListData.map((ele, index) => {
+      const data = { picEle: ele, width: (width * 7) / 8 };
+      return (
+        <div key={index}>
+          <PicComponent {...data} />
+        </div>
+      );
+    });
+  } else {
+    let left = 0,
+      right = 0;
+    let leftPic = [],
+      rightPic = [];
+    for (let i = 0; i < picListData.length; i++) {
+      const data = { picEle: picListData[i], width: (width * 3) / 8 };
+      const delta =
+        Number(picListData[i].height) *
+        (data.width / Number(picListData[i].width));
+      if (right < left) {
+        rightPic.push(
+          <div key={i}>
+            <PicComponent {...data} />
+          </div>
+        );
+        right += delta;
+      } else {
+        leftPic.push(
+          <div key={i}>
+            <PicComponent {...data} />
+          </div>
+        );
+        left += delta;
+      }
+    }
+    picList = (
+      <div>
+        <div id="left" style={{ marginLeft: `${width / 10}px` }}>
+          {leftPic}
+        </div>
+        <div id="right" style={{ marginRight: `${width / 10}px` }}>
+          {rightPic}
+        </div>
+      </div>
+    );
+  }
+
+  return (
+    <div>
+      <PhotoProvider>{picList}</PhotoProvider>
+    </div>
+  );
+}
+export default PicList;

+ 2 - 1
src/Global/Global.sass

@@ -24,5 +24,6 @@ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockq
     min-height: 100vh
     flex-direction: column
     padding: 0 24px
-    max-width: 600px
     margin: 0 auto
+*
+    -webkit-tap-highlight-color: transparent

+ 4 - 6
src/Redux/CodimdReducer.ts

@@ -1,9 +1,7 @@
 import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
-import { ArticleEle } from "../Components/ArticleListUnitComponent";
-import getArticleList from "../Utils/Axios";
-import { state } from "./Store";
-
-export type Status = "idle" | "loading" | "succeeded" | "failed";
+import { ArticleEle } from "../Components/Article/ArticleListUnitComponent";
+import { getCodimdList } from "../Utils/Axios";
+import { state, Status } from "./Store";
 
 export type CodimdState = {
   data: [ArticleEle];
@@ -18,7 +16,7 @@ export const codimdState = {
 };
 
 export const fetchCodimd = createAsyncThunk("posts/fetchCodimd", async () => {
-  const response = await getArticleList();
+  const response = await getCodimdList();
   return response.data;
 });
 

+ 58 - 0
src/Redux/PicReducer.ts

@@ -0,0 +1,58 @@
+import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
+import { PicEle } from "../Components/Pic/PicComponent";
+import { getPicList } from "../Utils/Axios";
+import { state, Status } from "./Store";
+
+export type PicState = {
+  data: [];
+  status: Status;
+  error: string | null;
+};
+
+export const picState = {
+  data: [] as unknown as [PicEle],
+  status: "idle",
+  error: "",
+};
+
+export const fetchPic = createAsyncThunk("posts/fetchPic", async () => {
+  const response = await getPicList();
+  return response.data;
+});
+
+export const picSlice = createSlice({
+  name: "pic",
+  initialState: picState,
+  reducers: {},
+  extraReducers(builder) {
+    builder
+      .addCase(fetchPic.pending, (state, action) => {
+        state.status = "loading";
+      })
+      .addCase(fetchPic.fulfilled, (state, action) => {
+        state.status = "succeeded";
+        (action.payload as [PicEle]).forEach((elePayload) => {
+          let flag = false;
+          for (let i = 0; i < state.data.length; i++) {
+            if (state.data[i].id === elePayload.id) {
+              flag = true;
+              break;
+            }
+          }
+          if (!flag) {
+            state.data.push(elePayload);
+          }
+        });
+      })
+      .addCase(fetchPic.rejected, (state, action) => {
+        state.status = "failed";
+        state.error = action.error.message as string;
+      });
+  },
+});
+
+export const selectAllPic = (state: state) => state.pic.data;
+
+export const selectPicState = (state: state) => state.pic.status;
+
+export default picSlice.reducer;

+ 8 - 0
src/Redux/Store.ts

@@ -1,12 +1,20 @@
 import { configureStore } from "@reduxjs/toolkit";
 import codimdReducer, { CodimdState } from "./CodimdReducer";
+import PicReducer, { PicState } from "./PicReducer";
+import WidthReducer from "./WidthReducer";
+
+export type Status = "idle" | "loading" | "succeeded" | "failed";
 
 export type state = {
   codimd: CodimdState;
+  pic: PicState;
+  width: number;
 };
 
 export default configureStore({
   reducer: {
     codimd: codimdReducer,
+    pic: PicReducer,
+    width: WidthReducer,
   },
 });

+ 18 - 0
src/Redux/WidthReducer.ts

@@ -0,0 +1,18 @@
+import { createSlice } from "@reduxjs/toolkit";
+import { state } from "./Store";
+
+export const widthSlice = createSlice({
+  name: "width",
+  initialState: document.body.clientWidth,
+  reducers: {
+    setWidth(state, action) {
+      return (state = action.payload);
+    },
+  },
+});
+
+export const selectWidth = (state: state) => state.width;
+
+export const { setWidth } = widthSlice.actions;
+
+export default widthSlice.reducer;

+ 8 - 3
src/Utils/Axios.ts

@@ -1,6 +1,7 @@
 import axios from "axios";
 
-axios.defaults.baseURL = "http://127.0.0.1:5000";
+const codimdUrl = "http://127.0.0.1:5000";
+const picUrl = "http://127.0.0.1:5001";
 
 type DefaultRes = {
   // `data` 由服务器提供的响应
@@ -20,6 +21,10 @@ type DefaultRes = {
   request: {};
 };
 
-export default async function getCodimdList(): Promise<DefaultRes> {
-  return axios.get("/get_my_notes");
+export async function getCodimdList(): Promise<DefaultRes> {
+  return axios.get(codimdUrl + "/get_my_notes");
+}
+
+export async function getPicList(): Promise<DefaultRes> {
+  return axios.get(picUrl + "/get_my_pics");
 }