diff --git a/app/context/CrateContext.tsx b/app/context/CrateContext.tsx new file mode 100644 index 0000000..52becc4 --- /dev/null +++ b/app/context/CrateContext.tsx @@ -0,0 +1,64 @@ +// context/HeaderContext.tsx +"use client"; +import React, { createContext, useContext, useState } from 'react'; + +// 定义 cratesInfo 接口 +export interface CratesInfo { + crate_name: string; + description: string; + dependencies: { + direct: number; + indirect: number; + }; + dependents: { + direct: number; + indirect: number; + }; + cves: { + cve_id: string; + url: string; + description: string; + }[]; + versions: string[]; +} + +// 定义 HeaderContext 的类型 +interface CrateData { + crateName: string | undefined; + crateVersion: string | string[] | undefined; + results: CratesInfo | null; // 存储 cratesInfo 数据 +} + +interface HeaderContextType { + crateData: CrateData; + setCrateData: React.Dispatch>; +} + +// 创建上下文,并设置默认值为 undefined +const HeaderContext = createContext(undefined); + +// 创建提供者组件 +export const HeaderProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [crateData, setCrateData] = useState({ + crateName: undefined, + crateVersion: undefined, + results: null, + }); + + // 这里可以添加一些副作用,例如在 crateData 更新时执行某些操作 + + return ( + + {children} + + ); +}; + +// 创建一个自定义 Hook 用于使用上下文 +export const useHeaderContext = (): HeaderContextType => { + const context = useContext(HeaderContext); + if (context === undefined) { + throw new Error('useHeaderContext must be used within a HeaderProvider'); + } + return context; +}; \ No newline at end of file diff --git a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx index cdbfc35..fc966f1 100644 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx +++ b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx @@ -1,21 +1,22 @@ +//Dependencies页面 "use client"; import React, { useEffect, useState } from 'react'; import { useParams } from 'next/navigation' -import Link from 'next/link'; + import DependencyTable from '../../../../../../../components/DependencyTable'; import { dependenciesInfo } from '@/app/lib/all_interface'; + const CratePage = () => { const params = useParams(); const [results, setResults] = useState(null); - const [searchQuery, setSearchQuery] = useState(''); + const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const crateName = params.name; // 从 URL 中获取 crate_name 参数 - const version = params.version; // 从 URL 中获取 version 参数 + useEffect(() => { const fetchCrateData = async () => { @@ -45,100 +46,12 @@ const CratePage = () => { if (error) return
Error: {error}
; // console.log('dependencyyyyyyyyyyyyyyy', results?.data); return ( -
+ <> {/* Existing header and search */} -
-
-
- -
- open - / - source - / - insights -
- -
- {crateName} -
- - {/* 这里可以添加版本选择的下拉菜单 */} -
-
-
-
- setSearchQuery(e.target.value)} // 更新搜索内容 - /> - - - -
-
- - {/* 导航栏 */} - -
+ -
); }; diff --git a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx index ab776fb..84d0386 100644 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx +++ b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx @@ -1,11 +1,13 @@ +//Dependents页面 "use client"; import React, { useEffect, useState } from 'react'; -import Link from 'next/link'; + import DependentTable from '../../../../../../../components/DependentTable'; import { dependentsInfo } from '@/app/lib/all_interface'; import { useParams } from 'next/navigation'; + const CratePage = () => { const [results, setResults] = useState(null); const [loading, setLoading] = useState(true); @@ -13,8 +15,7 @@ const CratePage = () => { const params = useParams(); - const crateName = params.name; // 从 URL 中获取 crate_name 参数 - const version = params.version; // 从 URL 中获取 version 参数 + useEffect(() => { const fetchCrateData = async () => { @@ -43,95 +44,10 @@ const CratePage = () => { if (loading) return
Loading...
; if (error) return
Error: {error}
; - //console.log('dependencyyyyyyyyyyyyyyy', results?.data); + return (
- {/* Existing header and search */} -
-
-
- -
- open - / - source - / - insights -
- -
- {crateName} -
- - {/* 这里可以添加版本选择的下拉菜单 */} -
-
-
-
- - -
-
- - {/* 导航栏 */} - - -
- diff --git a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx index e3d2c56..c9f49d2 100644 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx +++ b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx @@ -1,28 +1,30 @@ +//Overview页面 "use client"; import React, { useEffect, useState } from 'react'; import Link from 'next/link'; //import { useSearchParams } from 'next/navigation'; import { cratesInfo } from '@/app/lib/all_interface'; -import { useParams } from 'next/navigation' +import { useParams } from 'next/navigation'; + + + const CratePage = () => { - const [isOpen, setIsOpen] = useState(false); // 状态管理下拉菜单的显示 + const params = useParams(); - //console.log("params:", params); - const [searchQuery, setSearchQuery] = useState(''); + + + + const [results, setResults] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - // const searchParams = useSearchParams(); - // const crateName = searchParams.get('crate_name'); // 从 URL 中获取 crate_name 参数 - // const crateVersion = searchParams.get('version'); // 从 URL 中获取 version 参数 - const crateName = params.name; // 从 URL 中获取 crate_name 参数 - const crateVersion = params.version; // 从 URL 中获取 version 参数 + useEffect(() => { const fetchCrateData = async () => { try { @@ -49,15 +51,6 @@ const CratePage = () => { fetchCrateData(); // 调用函数来获取数据 }, [params.name, params.version, params.nsfront, params.nsbehind]); // 依赖项数组,确保在 name 或 version 改变时重新获取数据 - - const toggleDropdown = () => { - setIsOpen(prev => !prev); - }; - - const closeDropdown = () => { - setIsOpen(false); - }; - // 渲染部分 if (loading) return

Loading...

; if (error) return

Error: {error}

; @@ -67,121 +60,6 @@ const CratePage = () => { return (
- {/* Existing header and search */} -
-
-
- -
- open - / - source - / - insights -
- -
- {crateName} - {/*版本列表*/} -
- - {isOpen && ( -
- {/* 遮罩层 */} -
-
-
    - {results?.versions.map((version, index) => ( - -
  • - {version} -
  • - - ))} -
-
-
- )} -
- - -
-
-
- setSearchQuery(e.target.value)} // 更新搜索内容 - /> - - - -
-
- - {/* 导航栏 */} - -
- {/* cve */}
@@ -286,7 +164,7 @@ const CratePage = () => {
Pinned-Dependencies - 0/10 + 9/10
diff --git a/app/homepage/[nsfront]/[nsbehind]/layout.tsx b/app/homepage/[nsfront]/[nsbehind]/layout.tsx new file mode 100644 index 0000000..4b5052a --- /dev/null +++ b/app/homepage/[nsfront]/[nsbehind]/layout.tsx @@ -0,0 +1,24 @@ +import '@/app/ui/global.css'; +import { HeaderProvider } from '../../../context/CrateContext'; +import Header from '@/components/HeaderWithSearch'; +export const metadata = { + title: 'cratespro', + description: 'Generated by Next.js', +} + +export default function layout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + +
+ {children} + + + + ) +} diff --git a/app/homepage/layout.tsx b/app/homepage/layout.tsx index 30e72fa..d80a618 100644 --- a/app/homepage/layout.tsx +++ b/app/homepage/layout.tsx @@ -1,5 +1,5 @@ import '@/app/ui/global.css'; - +import { HeaderProvider } from '../context/CrateContext'; export const metadata = { title: 'cratespro', @@ -12,8 +12,12 @@ export default function RootLayout({ children: React.ReactNode }) { return ( - - {children} + + + + {children} + + ) } diff --git a/components/HeaderWithSearch.tsx b/components/HeaderWithSearch.tsx new file mode 100644 index 0000000..bcbe1d7 --- /dev/null +++ b/components/HeaderWithSearch.tsx @@ -0,0 +1,160 @@ +// components/Header.js +'use client'; +import React, { useEffect, useState } from 'react'; +import Link from 'next/link'; +import { useHeaderContext } from '../app/context/CrateContext'; +import { useParams } from 'next/navigation'; +// import { cratesInfo } from '@/app/lib/all_interface'; + +const Header = () => { + const params = useParams(); + + const [searchQuery, setSearchQuery] = useState(''); + const { crateData, setCrateData } = useHeaderContext(); + const [isOpen, setIsOpen] = useState(false); + //const [currentVersion, setCurrentVersion] = useState(); // 存储当前选中的版本 + + const [currentVersion, setCurrentVersion] = useState(params.version); // 存储当前选中的版本 + + const [activeTab, setActiveTab] = useState('overview'); // 默认选中 Overview + // 定义导航项数据 + const navItems = [ + { name: 'Overview', path: '' }, + { name: 'Dependencies', path: '/dependencies' }, + { name: 'Dependents', path: '/dependents' }, + ]; + + + // 使用 useEffect 从 API 获取数据 + useEffect(() => { + const fetchData = async () => { + // 如果 crateData.results 为空,说明数据还未加载 + if (!crateData.results) { + const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}`); + const data = await response.json(); + console.log('dataaaaaaaaaaaaaa:', data); + setCrateData({ + crateName: data.crate_name, + crateVersion: params.version, + results: data, + }); + + } + }; + + fetchData(); + }, [params.nsfront, params.nsbehind, params.name, params.version, setCrateData, crateData.crateVersion, crateData.results]); // 添加 crateData 作为依赖项 + + const toggleDropdown = () => { + setIsOpen((prev) => !prev); + }; + + const closeDropdown = () => { + setIsOpen(false); + + }; + + + + + + return ( +
+
+
+ +
+ open + / + source + / + insights +
+ +
+ {params.name} +
+ + {isOpen && ( +
+
+
+
    + {crateData.results?.versions.map((version, index) => ( + setCurrentVersion(version)} + href={`/homepage/${params.nsfront}/${params.nsbehind}/${crateData.results?.crate_name}/${version}`} + > +
  • + + {version} + +
  • + + ))} +
+
+
+ )} +
+
+
+
+ setSearchQuery(e.target.value)} + /> + + + +
+
+ +
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index 8ed419e..2264c4b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -3,6 +3,7 @@ // next.config.mjs export default { + async redirects() { return [ { @@ -13,3 +14,11 @@ export default { ]; }, }; +// /** @type {import('next').NextConfig} */ +// const nextConfig = { +// experimental: { +// ppr: 'incremental', +// }, +// } + +// module.exports = nextConfig \ No newline at end of file