From c03af0937c327c7be9c6262e2893ba514f71bb4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=87=AA=E7=94=B1=E7=9A=84=E4=B8=96=E7=95=8C=E4=BA=BA?= <3196812536@qq.com> Date: Wed, 1 Jan 2025 18:33:31 +0800 Subject: [PATCH 1/2] feat: add search sort filter --- src/components/layout/Layout.tsx | 4 ++-- src/pages/search/result/index.tsx | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index 6a1ceb92..3f2d6c08 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -31,10 +31,10 @@ const Layout: React.FC = ({ children }) => {
{children}
) : (
- {pathname === '/' && } + {(pathname === '/' || pathname === '/search/result') && }
{ const router = useRouter(); @@ -54,10 +65,29 @@ const Result: NextPage = () => { rootMargin: '0px 0px 100px 0px', }); + const { + tid = 'all', + sort_by = 'featured', + rank_by = 'newest', + year, + month, + }: QueryProps = router.query; + const sortBy = indexSortBy.includes(sort_by) ? sort_by : 'featured'; + const rankBy = indexRankBy.includes(rank_by) ? rank_by : 'newest'; + return ( <> +
{list.map((item) => ( From 5d70cf26f2ba9b8d0d26002f5c8443c2fa58ebef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=87=AA=E7=94=B1=E7=9A=84=E4=B8=96=E7=95=8C=E4=BA=BA?= <3196812536@qq.com> Date: Wed, 8 Jan 2025 18:10:44 +0800 Subject: [PATCH 2/2] feat: add search sort filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit token过期无法测试 --- src/components/layout/Layout.tsx | 6 +- src/components/search/SearchNavbar.tsx | 76 +++++++++++++++ src/components/search/SearchTagLink.tsx | 47 +++++++++ src/components/search/SearchTagList.tsx | 122 ++++++++++++++++++++++++ src/pages/search/result/index.tsx | 38 ++------ src/utils/util.ts | 9 ++ 6 files changed, 265 insertions(+), 33 deletions(-) create mode 100644 src/components/search/SearchNavbar.tsx create mode 100644 src/components/search/SearchTagLink.tsx create mode 100644 src/components/search/SearchTagList.tsx diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index 3f2d6c08..fe7d5ca6 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -2,6 +2,7 @@ import { useRouter } from 'next/router'; import { ReactNode, useMemo } from 'react'; import Header from '@/components/layout/Header'; +import SearchTagList from '@/components/search/SearchTagList'; import { Side } from '@/components/side/Side'; import TagList from '../side/TagList'; @@ -31,10 +32,11 @@ const Layout: React.FC = ({ children }) => {
{children}
) : (
- {(pathname === '/' || pathname === '/search/result') && } + {pathname === '/' && } + {pathname === '/search/result' && }
string; + i18n_lang: string; + tid?: string; + sortBy: string; + rankBy: string; +} + +const SearchNavbar = ({ + middleText = '', + endText = '', + i18n_lang, + tid, + sortBy, + rankBy, +}: Props) => { + const router = useRouter(); + + const goBack = () => { + if (window.history.length < 2) { + router.push('/'); + } else { + router.back(); + } + }; + + const { tagItems } = useFilterHandling(tid, sortBy, rankBy, i18n_lang); + + return ( +
+
+
+ +
+
+ {middleText} +
+ {endText ? ( +
+ {endText} +
+ ) : ( +
+ )} +
+ {/* 移动端标签 */} +
+ {tagItems.length > 0 ? ( + + ) : ( + + )} +
+
+ ); +}; + +export default SearchNavbar; diff --git a/src/components/search/SearchTagLink.tsx b/src/components/search/SearchTagLink.tsx new file mode 100644 index 00000000..77a2c751 --- /dev/null +++ b/src/components/search/SearchTagLink.tsx @@ -0,0 +1,47 @@ +import { NoPrefetchLink } from '@/components/links/CustomLink'; + +import { makeUrl } from '@/utils/util'; + +import { TagType } from '@/types/tag'; + +interface Props { + tid?: string; + sort_by: string; + items: TagType[]; + rank_by: string; +} + +export default function SearchTagLink({ tid, sort_by, items, rank_by }: Props) { + return ( +
+
    + {items.map((item: TagType) => { + return ( +
  • + + + {item.name} + + +
  • + ); + })} +
+
+ ); +} diff --git a/src/components/search/SearchTagList.tsx b/src/components/search/SearchTagList.tsx new file mode 100644 index 00000000..ba21a1ed --- /dev/null +++ b/src/components/search/SearchTagList.tsx @@ -0,0 +1,122 @@ +import classNames from 'classnames'; +import Link from 'next/link'; +import { useRouter } from 'next/router'; +import { useTranslation } from 'next-i18next'; +import { useEffect, useState } from 'react'; +import { AiOutlineAppstore, AiOutlineSetting } from 'react-icons/ai'; + +import { TagModal } from '@/components/dialog/TagModal'; + +import { getTags } from '@/services/tag'; +import { isMobile, makeUrl } from '@/utils/util'; + +import { TagListSkeleton } from '../loading/skeleton'; + +import { Tag } from '@/types/tag'; + +export default function SearchTagList() { + const { t, i18n } = useTranslation('home'); + const defaultTag: Tag = { + name: '综合', + name_en: 'All', + tid: 'all', + icon_name: 'find', + }; + + const router = useRouter(); + const { + tid = 'all', + sort_by = 'featured', + rank_by, + year, + month, + } = router.query; + const [tags, setTags] = useState([]); + const [maxHeight, setMaxHeight] = useState(444); + + const updateMaxHeight = () => { + const calculatedHeight = Math.max(window.innerHeight * 0.6, 300); + setMaxHeight(calculatedHeight); + }; + + useEffect(() => { + const initTags = async () => { + const res = await getTags(); + if (res.success) { + res.data.unshift(defaultTag); + setTags(res.data); + } + }; + + if (!isMobile()) { + initTags(); + updateMaxHeight(); + window.addEventListener('resize', updateMaxHeight); + return () => { + window.removeEventListener('resize', updateMaxHeight); + }; + } + }, []); + + const iconClassName = (iconName: string) => `iconfont icon-${iconName} mr-1`; + + const tagClassName = (itemTid: string) => + classNames( + 'flex flex-row w-[115px] items-center my-1 py-2 px-3 rounded text-[14px] cursor-pointer hover:bg-gray-100 hover:text-blue-500 dark:hover:bg-gray-700', + { + 'text-gray-500 dark:text-gray-400': tid !== itemTid, + 'bg-gray-100 dark:bg-gray-700 text-blue-500': tid === itemTid, + } + ); + + return ( +
+
+
+
+
+
+ +
{t('tag_side.title')}
+
+
+
+
+ {!tags.length && } + {tags.map((item: Tag) => ( + +
+
+
+ {i18n.language === 'zh' + ? item.name + : item.name_en || item.name} +
+
+ + ))} +
+ +
+ +
{t('tag_side.manage')}
+
+
+
+
+
+ ); +} diff --git a/src/pages/search/result/index.tsx b/src/pages/search/result/index.tsx index 36098516..a5bbc058 100644 --- a/src/pages/search/result/index.tsx +++ b/src/pages/search/result/index.tsx @@ -11,7 +11,7 @@ import RepositoryItem from '@/components/home/Item'; import ItemBottom from '@/components/home/ItemBottom'; import Loading from '@/components/loading/Loading'; import { SearchSkeleton } from '@/components/loading/skeleton'; -import Navbar from '@/components/navbar/Navbar'; +import SearchNavbar from '@/components/search/SearchNavbar'; import Seo from '@/components/Seo'; import ToTop from '@/components/toTop/ToTop'; @@ -19,17 +19,6 @@ import { fetcher } from '@/services/base'; import { makeUrl } from '@/utils/api'; import { SearchItemType, SearchResponse } from '@/types/search'; -import IndexBar from '@/components/navbar/IndexBar'; -import { indexRankBy, indexSortBy } from '@/utils/constants'; - -// TODO 需要测试 -type QueryProps = { - sort_by?: string; - tid?: string; - rank_by?: string; - year?: number; - month?: number; -}; const Result: NextPage = () => { const router = useRouter(); @@ -64,29 +53,16 @@ const Result: NextPage = () => { onLoadMore: () => setSize(pageIndex + 1), rootMargin: '0px 0px 100px 0px', }); - - const { - tid = 'all', - sort_by = 'featured', - rank_by = 'newest', - year, - month, - }: QueryProps = router.query; - const sortBy = indexSortBy.includes(sort_by) ? sort_by : 'featured'; - const rankBy = indexRankBy.includes(rank_by) ? rank_by : 'newest'; - + // TODO: SearchNavbar return ( <> - -
diff --git a/src/utils/util.ts b/src/utils/util.ts index 85b27e85..d93d4b0e 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -90,3 +90,12 @@ export const constructURL = (params: { [key: string]: any }) => { return `/?${queryString}`; }; + +export const makeUrl = (path: string, params: { [key: string]: any }) => { + const queryString = Object.entries(params) + .filter(([_, value]) => value !== undefined && value !== null) + .map(([key, value]) => `${key}=${value}`) + .join('&'); + + return `${path}?${queryString}`; +};