From e92f8dacdc36c805ddda1c3490f0ce5527366693 Mon Sep 17 00:00:00 2001 From: master1lan <278457198@qq.com> Date: Sun, 8 Jan 2023 17:34:10 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=99nav=E6=B7=BB=E5=8A=A0=E6=8B=96?= =?UTF-8?q?=E6=8B=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 + pnpm-lock.yaml | 30 +++ src/components/image/image.module.less | 2 + src/index.less | 23 +-- src/routers/layout/nav.tsx | 256 ++++++++++++++++--------- src/routers/video/index.tsx | 6 +- src/routers/video/masonry.tsx | 101 ++++++---- src/routers/video/masonryItem.tsx | 36 ++-- src/routers/video/video.module.less | 5 +- src/utils/fetch/index.ts | 6 +- 10 files changed, 306 insertions(+), 162 deletions(-) diff --git a/package.json b/package.json index 60300e7..b4ba934 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,9 @@ }, "dependencies": { "@dnd-kit/core": "^6.0.7", + "@dnd-kit/modifiers": "^6.0.1", + "@dnd-kit/sortable": "^7.0.2", + "@dnd-kit/utilities": "^3.2.1", "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@mui/icons-material": "^5.11.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 502e6ca..b152e8a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,6 +3,9 @@ lockfileVersion: 5.4 specifiers: '@babel/core': '>=7.0.0 <8.0.0' '@dnd-kit/core': ^6.0.7 + '@dnd-kit/modifiers': ^6.0.1 + '@dnd-kit/sortable': ^7.0.2 + '@dnd-kit/utilities': ^3.2.1 '@emotion/react': ^11.10.5 '@emotion/styled': ^11.10.5 '@faker-js/faker': ^7.6.0 @@ -35,6 +38,9 @@ specifiers: dependencies: '@dnd-kit/core': 6.0.7_biqbaboplfbrettd7655fr4n2y + '@dnd-kit/modifiers': 6.0.1_pmudlfv2z3i7vvlookxjkeidxe + '@dnd-kit/sortable': 7.0.2_pmudlfv2z3i7vvlookxjkeidxe + '@dnd-kit/utilities': 3.2.1_react@18.2.0 '@emotion/react': 11.10.5_3grbeiqrb6djg67fiejiqngkdi '@emotion/styled': 11.10.5_pwtpayi7py7ecifctvjbac33ee '@mui/icons-material': 5.11.0_oiuuhmk4wjjpe4qb2sby7usney @@ -294,6 +300,30 @@ packages: tslib: 2.4.1 dev: false + /@dnd-kit/modifiers/6.0.1_pmudlfv2z3i7vvlookxjkeidxe: + resolution: {integrity: sha512-rbxcsg3HhzlcMHVHWDuh9LCjpOVAgqbV78wLGI8tziXY3+qcMQ61qVXIvNKQFuhj75dSfD+o+PYZQ/NUk2A23A==} + peerDependencies: + '@dnd-kit/core': ^6.0.6 + react: '>=16.8.0' + dependencies: + '@dnd-kit/core': 6.0.7_biqbaboplfbrettd7655fr4n2y + '@dnd-kit/utilities': 3.2.1_react@18.2.0 + react: 18.2.0 + tslib: 2.4.1 + dev: false + + /@dnd-kit/sortable/7.0.2_pmudlfv2z3i7vvlookxjkeidxe: + resolution: {integrity: sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==} + peerDependencies: + '@dnd-kit/core': ^6.0.7 + react: '>=16.8.0' + dependencies: + '@dnd-kit/core': 6.0.7_biqbaboplfbrettd7655fr4n2y + '@dnd-kit/utilities': 3.2.1_react@18.2.0 + react: 18.2.0 + tslib: 2.4.1 + dev: false + /@dnd-kit/utilities/3.2.1_react@18.2.0: resolution: {integrity: sha512-OOXqISfvBw/1REtkSK2N3Fi2EQiLMlWUlqnOK/UpOISqBZPWpE6TqL+jcPtMOkE8TqYGiURvRdPSI9hltNUjEA==} peerDependencies: diff --git a/src/components/image/image.module.less b/src/components/image/image.module.less index 82d5a72..75afa6f 100644 --- a/src/components/image/image.module.less +++ b/src/components/image/image.module.less @@ -3,9 +3,11 @@ overflow: hidden; border-radius: 8px; outline: 0.5px solid rgba(0, 0, 0, .05); + height: 100%; &>img { width: 100%; + height: 100%; display: inline-block; object-fit: cover; transition: opacity .5s linear; diff --git a/src/index.less b/src/index.less index 2c7d2f3..6fac532 100644 --- a/src/index.less +++ b/src/index.less @@ -4,17 +4,14 @@ margin: 0; } -// .feedContainer { -// margin: 0 auto; -// // max-width: 1200px; -// display: flex; -// align-items: flex-start; -// position: relative; -// width: 100%; +.feedContainer { + margin: 0 auto; + padding: 0 8px; + max-width: 1200px; -// .element-item { -// min-width: 200px; -// box-sizing: border-box; -// padding: 0 5px; -// } -// } \ No newline at end of file + // .element-item { + // min-width: 200px; + // box-sizing: border-box; + // padding: 0 5px; + // } +} \ No newline at end of file diff --git a/src/routers/layout/nav.tsx b/src/routers/layout/nav.tsx index 1ace33e..7964b08 100644 --- a/src/routers/layout/nav.tsx +++ b/src/routers/layout/nav.tsx @@ -2,35 +2,181 @@ import { Chip, Stack } from "@mui/material"; import { useInView } from "react-intersection-observer"; import SegmentIcon from "@mui/icons-material/Segment"; import CloseFullscreenIcon from "@mui/icons-material/CloseFullscreen"; +import { NavLink } from "react-router-dom"; import { - Outlet, - NavLink, - useLoaderData, - Form, - redirect, - useLocation, - useSubmit, -} from "react-router-dom"; + DndContext, + closestCenter, + MouseSensor, + TouchSensor, + useSensor, + useSensors, + DragEndEvent, +} from "@dnd-kit/core"; +import { arrayMove, SortableContext, useSortable } from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; +import { + restrictToHorizontalAxis, + restrictToParentElement, +} from "@dnd-kit/modifiers"; import { nanoid } from "nanoid"; import styles from "./layout.module.less"; -import { FC, forwardRef, useState, useEffect, useMemo } from "react"; +import { FC, useState, useEffect, useMemo, memo } from "react"; +import { Pick } from "@utils/index"; + +export default function Header_Nav() { + //todo 这里在loacalstorage中查找 + const [navLists, setLists] = useState(nav_tag_list); + //最后一个span是否可见 + const { ref, inView } = useInView({ initialInView: true }); + //tag区是否展开 + const [showed, setShow] = useState(false); + //拖拽事件绑定 + const sensors = useSensors( + // 鼠标点击 + useSensor(MouseSensor, { + activationConstraint: { + delay: 100, + tolerance: 5, + }, + }), + // 触摸屏幕 + useSensor(TouchSensor, { + activationConstraint: { + delay: 100, + tolerance: 5, + }, + }) + ); + //拖拽事件完成事件 + function handleDragEnd(event: DragEndEvent) { + const { active, over } = event; + if (over && active.id !== over.id) { + setLists((items) => { + const oldIndex = items.findIndex((item) => item.id === active.id); + const newIndex = items.findIndex((item) => item.id === over.id); + return arrayMove(items, oldIndex, newIndex); + }); + } + } + const ComRes = useMemo( + () => ( + <> + {navLists.map((item) => ( + + ))} + + ), + [navLists] + ); + return ( +
+ + + {ComRes} + +
setShow((showed) => !showed)} + style={{ + display: inView ? "none" : "flex", + }} + > + +
+
+
+
+ ); +} + +const NavItem: FC = (props) => { + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ id: props.id }); + const style = { + transform: CSS.Transform.toString(transform), + transition, + }; + return ( +
+ {props.type === "router" ? ( + + ) : ( + + )} +
+ ); +}; + +const NavRouterChipItem: FC> = + memo((props) => ( + + `${styles["navlink"]} ${isActive ? styles["navlink-active"] : ""}` + } + > + + + )); + +const NavTagChipItem: FC> = memo((props) => { + const [clicked, setClick] = useState(false); + return ( + setClick((clicked) => !clicked)} + /> + ); +}); +//todo:修复展示更多栏的bug -const router_lists = [ +type NavRouterItemType = { + id: string; + type: "router"; + pathname: string; + name: string; +}; +type NavQueryItemType = { id: string; type: "query"; query: string }; +type NavListItemType = NavQueryItemType | NavRouterItemType; + +const nav_tag_list = [ { - id: nanoid(2), type: "router", pathname: "photo", name: "图片", }, { - id: nanoid(2), type: "router", pathname: "video", name: "视频", }, -]; - -const nav_lists = [ { type: "query", query: "所有二创", @@ -79,82 +225,4 @@ const nav_lists = [ type: "query", query: "其他分区", }, -].map((item) => ({ ...item, id: nanoid(3) })); - -export default function Header_Nav() { - //todo 这里存放到context中间去 - const { ref, inView } = useInView({ initialInView: true }); - const [showed, setShow] = useState(false); - console.log({ inView }); - const ComRes = useMemo( - () => ( - <> - {router_lists.map((router) => ( - - `${styles["navlink"]} ${isActive ? styles["navlink-active"] : ""}` - } - > - - - ))} - {nav_lists.map((nav) => ( - - ))} - - ), - [] - ); - return ( -
- - {ComRes} - -
setShow((showed) => !showed)} - style={{ - display: inView ? "none" : "flex", - }} - > - -
-
-
- ); -} - -const ClickChip: FC<{ label: string }> = (props) => { - const [clicked, setClick] = useState(false); - return ( - //@ts-ignore - setClick((clicked) => !clicked)} - /> - ); -}; -//todo:修复展示更多栏的bug -//todo:改造nav栏,通过拖拽修改排序 +].map((item) => ({ ...item, id: nanoid(3) })) as NavListItemType[]; diff --git a/src/routers/video/index.tsx b/src/routers/video/index.tsx index 62b2716..eb06d97 100644 --- a/src/routers/video/index.tsx +++ b/src/routers/video/index.tsx @@ -1,4 +1,8 @@ import VideoMasonry from "./masonry"; export default function VideoPage() { - return <>{/* */}; + return ( + <> + + + ); } diff --git a/src/routers/video/masonry.tsx b/src/routers/video/masonry.tsx index 44ab401..c7368fe 100644 --- a/src/routers/video/masonry.tsx +++ b/src/routers/video/masonry.tsx @@ -1,22 +1,18 @@ import { fetchVideos } from "@utils/fetch"; import { concurrencyRequest, Pick } from "@utils/index"; -import { useState, useEffect } from "react"; +import { useState, useEffect, FC } from "react"; import { VideoRouterImageCardType, VideoRouterMasonryType } from "./videotype"; -import { - getImageSize, - getResizeHeight, - ImageSize, -} from "@components/image/tool"; -import { Masonry as Masonic_masonry } from "masonic"; +import { getImageSize } from "@components/image/tool"; import Grid from "@mui/material/Unstable_Grid2"; import ImageShouldResizeProview from "@components/proview/imageSize"; import { VideoRouterImageCard } from "./masonryItem"; +import { Skeleton } from "@mui/material"; /** * @description 该组件负责渲染视频图片的瀑布流 */ export default function VideoMasonry(props: any) { const [lists, setLists] = useState([]); - const order_width = 200; + const [isLoading, setLoading] = useState(true); useEffect(() => { // 在内部定义fetchHandler,保证拿到的是同步的 const fetchHandler = async () => { @@ -30,39 +26,30 @@ export default function VideoMasonry(props: any) { setLists((lists) => [ ...lists, ...data.map((item, index) => { - const imageSize: ImageSize = imageSizelists[index], - itemRes: VideoRouterImageCardType = Pick( - item, - "title", - "bvid", - "name", - "tname", - "copyright", - "pic", - "tag", - "view", - "coin", - "share", - "like", - "updated_at", - "danmaku", - "duration", - "favorite", - "face" - ); - - // if (imageSize.success === true) { - // return { - // ...itemRes, - // width: order_width, - // height: Math.floor((order_width * 3) / 4), - // }; - // } + const itemRes: VideoRouterImageCardType = Pick( + item, + "title", + "bvid", + "name", + "tname", + "copyright", + "pic", + "tag", + "view", + "coin", + "share", + "like", + "updated_at", + "danmaku", + "duration", + "favorite", + "face" + ); return itemRes; }), ]); }; - fetchHandler(); + fetchHandler().then(() => setLoading(false)); }, [props]); return (
@@ -82,10 +69,50 @@ export default function VideoMasonry(props: any) { ))} + {isLoading && }
); } +const LoadingSkeleton: FC<{ num: number }> = ({ num = 0 }) => { + return ( + <> + {...Array.from({ length: num }, (v, key) => ( + + + + +
+ +
+ + +
+
+
+ ))} + + ); +}; + //todo:改成feed流 diff --git a/src/routers/video/masonryItem.tsx b/src/routers/video/masonryItem.tsx index 7c0b753..af38589 100644 --- a/src/routers/video/masonryItem.tsx +++ b/src/routers/video/masonryItem.tsx @@ -5,6 +5,8 @@ import SubjectSharpIcon from "@mui/icons-material/SubjectSharp"; import ThumbUpSharpIcon from "@mui/icons-material/ThumbUpSharp"; import FavoriteSharpIcon from "@mui/icons-material/FavoriteSharp"; import { Link, Avatar } from "@mui/material"; +import useMediaQuery from "@mui/material/useMediaQuery"; +import { useTheme } from "@mui/material"; import { VideoRouterImageCardType } from "./videotype"; import getrealtiveTime, { getVideoTime } from "@utils/time"; import styles from "./video.module.less"; @@ -95,6 +97,8 @@ const VideoInfo: FC< > > = (props) => { const { title, name, bvid, updated_at, coin, like, favorite, face } = props; + const theme = useTheme(); + const matchesPhone = useMediaQuery(theme.breakpoints.up("sm")); return (

@@ -108,25 +112,27 @@ const VideoInfo: FC<

- + {matchesPhone && }
{name} - {getrealtiveTime(updated_at * 1000)} + {matchesPhone && {getrealtiveTime(updated_at * 1000)}} -
- - {" "} - {getFixedNumber(like)} - - - {getFixedNumber(coin)} - - - {" "} - {getFixedNumber(favorite)} - -
+ {matchesPhone && ( +
+ + {" "} + {getFixedNumber(like)} + + + {getFixedNumber(coin)} + + + {" "} + {getFixedNumber(favorite)} + +
+ )}
diff --git a/src/routers/video/video.module.less b/src/routers/video/video.module.less index d686409..29f8010 100644 --- a/src/routers/video/video.module.less +++ b/src/routers/video/video.module.less @@ -2,7 +2,10 @@ height: 100%; display: flex; flex-flow: column nowrap; - // justify-content: space-between; + + &>:first-child { + flex: 1; + } .video-data { transition: opacity .3s linear; diff --git a/src/utils/fetch/index.ts b/src/utils/fetch/index.ts index 0808f11..a18238c 100644 --- a/src/utils/fetch/index.ts +++ b/src/utils/fetch/index.ts @@ -11,7 +11,11 @@ import { IFetchVideoParams, RFetchVideoRes } from "./fetchtype"; export function fetchVideos( params: IFetchVideoParams ): Promise { - return Promise.resolve(videoJson); + return new Promise((resolve) => { + setTimeout(() => { + resolve(videoJson); + }, 1000); + }); return fetch("/v1/video-interface/advanced-search?order=pubdate&page=1", { method: "GET", headers: {