diff --git a/package-lock.json b/package-lock.json index 6581800..6ebe4cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "next": "15.3.5", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "zustand": "^5.0.6" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -1222,7 +1223,7 @@ "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "dev": true, + "devOptional": true, "dependencies": { "csstype": "^3.0.2" } @@ -2234,7 +2235,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "devOptional": true }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -5758,6 +5759,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.6.tgz", + "integrity": "sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index e97bf62..c680442 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,20 @@ "lint": "next lint" }, "dependencies": { + "next": "15.3.5", "react": "^19.0.0", "react-dom": "^19.0.0", - "next": "15.3.5" + "zustand": "^5.0.6" }, "devDependencies": { - "typescript": "^5", + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4", "eslint": "^9", "eslint-config-next": "15.3.5", - "@eslint/eslintrc": "^3" + "tailwindcss": "^4", + "typescript": "^5" } } diff --git a/src/api/cms/cmsWebsite/model/index.ts b/src/api/cms/cmsWebsite/model/index.ts index 0b98583..a518ff4 100644 --- a/src/api/cms/cmsWebsite/model/index.ts +++ b/src/api/cms/cmsWebsite/model/index.ts @@ -1,5 +1,6 @@ import type { PageParam } from '@/api'; import {CmsWebsiteSetting} from "@/api/cms/cmsWebsiteSetting/model"; +import {CmsNavigation} from "@/api/cms/cmsNavigation/model"; /** * 网站信息记录表 @@ -119,6 +120,12 @@ export interface CmsWebsite { username?: string; // 网站配置 setting?: CmsWebsiteSetting; + // 导航菜单列表 + navigations?: CmsNavigation[]; + // 顶部导航菜单 + topNavs?: CmsNavigation[]; + // 底部导航菜单 + bottomNavs?: CmsNavigation[]; } /** diff --git a/src/api/layout/index.ts b/src/api/layout/index.ts index d2c1437..d7558a8 100644 --- a/src/api/layout/index.ts +++ b/src/api/layout/index.ts @@ -6,6 +6,7 @@ import {MODULES_API_URL, SERVER_API_URL} from '@/config/setting'; import { Company } from '@/api/system/company/model'; import { CmsWebsite } from '@/api/cms/cmsWebsite/model'; import {Menu} from "@/api/system/menu/model"; +import { CmsNavigation } from '@/api/cms/cmsNavigation/model'; /** * 获取当前登录的用户信息、菜单、权限、角色 @@ -33,6 +34,39 @@ export async function getSiteInfo(): Promise { return Promise.reject(new Error(res.data.message)); } +/** + * 获取网站导航菜单 + */ +export async function getNavigations(position?: number): Promise { + const res = await request.get>( + MODULES_API_URL + '/cms/cms-navigation', + { + params: { + position, // 0不限 1顶部 2底部 + status: 0 // 只获取正常状态的菜单 + } + } + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data as unknown as CmsNavigation[]; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 获取底部导航菜单 + */ +export async function getBottomNavigations(): Promise { + return getNavigations(2); // position: 2 表示底部菜单 +} + +/** + * 获取顶部导航菜单 + */ +export async function getTopNavigations(): Promise { + return getNavigations(1); // position: 1 表示顶部菜单 +} + /** * 获取当前登录的用户信息、菜单、权限、角色 */ diff --git a/src/app/page.tsx b/src/app/page.tsx index 8d9bade..aa9942b 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,27 +1,120 @@ -// import HeroSection from '@/components/sections/HeroSection'; -// import FeaturesSection from '@/components/sections/FeaturesSection'; -// import CasesSection from '@/components/sections/CasesSection'; -// import PartnersSection from '@/components/sections/PartnersSection'; -// import ContactSection from '@/components/sections/ContactSection'; +import SiteInfoDisplay from '@/components/sections/SiteInfoDisplay'; +import NavigationDisplay from '@/components/sections/NavigationDisplay'; export default function Home() { return ( -
-
Main
- {/* 英雄区域 */} - {/**/} +
+
+
+

+ Zustand 全局状态管理演示 +

+

+ 这个页面展示了如何使用 Zustand 管理全局的站点信息, + 实现数据缓存和跨组件共享。 +

+
- {/* 产品服务 */} - {/**/} +
+ {/* 站点信息展示 */} +
+

+ 站点信息展示 +

+ +
- {/* 客户案例 */} - {/**/} + {/* 导航菜单展示 */} +
+

+ 导航菜单展示 +

+ +
- {/* 合作伙伴 */} - {/**/} + {/* 功能说明 */} +
+

+ 功能特点 +

+
+
    +
  • +
    + + + +
    +
    +

    自动缓存

    +

    30分钟内不重复请求接口

    +
    +
  • - {/* 联系我们 */} - {/**/} -
+
  • +
    + + + +
    +
    +

    持久化存储

    +

    数据存储在 sessionStorage 中

    +
    +
  • + +
  • +
    + + + +
    +
    +

    全局共享

    +

    任意组件都可以访问站点信息

    +
    +
  • + +
  • +
    + + + +
    +
    +

    错误处理

    +

    自动重试和错误状态管理

    +
    +
  • + +
  • +
    + + + +
    +
    +

    导航菜单管理

    +

    支持顶部和底部导航菜单的动态加载

    +
    +
  • + +
  • +
    + + + +
    +
    +

    TypeScript 支持

    +

    完整的类型安全保障

    +
    +
  • + + + + + + ); } diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index 89e8661..4917aa4 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -1,39 +1,189 @@ +'use client'; import Link from 'next/link'; import Image from 'next/image'; +import { useSiteInfoValue } from '@/hooks/useSiteInfo'; +import { CmsNavigation } from '@/api/cms/cmsNavigation/model'; const Footer = () => { + const { siteInfo, bottomNavs, isLoaded, isNavsLoaded } = useSiteInfoValue(); const currentYear = new Date().getFullYear(); + console.log(bottomNavs,'bottomNavs') - const footerLinks = { - products: [ - { name: '网站建设', href: '/products/website' }, - { name: '移动应用', href: '/products/mobile' }, - { name: '系统开发', href: '/products/system' }, - { name: '电商平台', href: '/products/ecommerce' }, + // 按父级分组底部导航 + const groupedNavs = bottomNavs.reduce((groups: Record, nav) => { + if (nav.parentId === 0) { + // 顶级菜单作为分组标题 + groups[nav.title || 'default'] = bottomNavs.filter(child => child.parentId === nav.navigationId); + } + return groups; + }, {}); + + // 如果没有底部导航数据,显示默认的静态菜单 + const defaultFooterLinks = { + '产品服务': [ + { title: '网站建设', path: '/products/website' }, + { title: '移动应用', path: '/products/mobile' }, + { title: '系统开发', path: '/products/system' }, + { title: '电商平台', path: '/products/ecommerce' }, ], - solutions: [ - { name: '企业数字化', href: '/solutions/digital' }, - { name: '教育信息化', href: '/solutions/education' }, - { name: '智慧城市', href: '/solutions/smart-city' }, - { name: '工业互联网', href: '/solutions/industry' }, + '解决方案': [ + { title: '企业数字化', path: '/solutions/digital' }, + { title: '教育信息化', path: '/solutions/education' }, + { title: '智慧城市', path: '/solutions/smart-city' }, + { title: '工业互联网', path: '/solutions/industry' }, ], - support: [ - { name: '技术文档', href: '/docs' }, - { name: '开发指南', href: '/guide' }, - { name: 'API参考', href: '/api' }, - { name: '常见问题', href: '/faq' }, + '技术支持': [ + { title: '技术文档', path: '/docs' }, + { title: '开发指南', path: '/guide' }, + { title: 'API参考', path: '/api' }, + { title: '常见问题', path: '/faq' }, ], - company: [ - { name: '关于我们', href: '/about' }, - { name: '新闻动态', href: '/news' }, - { name: '招聘信息', href: '/careers' }, - { name: '联系我们', href: '/contact' }, + '关于我们': [ + { title: '公司介绍', path: '/about' }, + { title: '新闻动态', path: '/news' }, + { title: '招聘信息', path: '/careers' }, + { title: '联系我们', path: '/contact' }, ], }; + // 使用接口数据或默认数据 + const footerLinks = Object.keys(groupedNavs).length > 0 ? groupedNavs : defaultFooterLinks; + console.log(footerLinks,'footerLinks') + return ( -