diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..5ae3e1a --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,23 @@ +/** + * App 根组件 + */ + +import React from 'react' +import { RouterProvider } from 'react-router-dom' +import { ConfigProvider, App as AntApp } from 'antd' +import zhCN from 'antd/locale/zh_CN' +import router from './router' +import { heritageTheme } from '@theme/index' +import '@styles/global.css' + +const App: React.FC = () => { + return ( + + + + + + ) +} + +export default App diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..83294a8 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,13 @@ +/** + * 应用入口文件 + */ + +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +) diff --git a/src/router/index.tsx b/src/router/index.tsx new file mode 100644 index 0000000..740c464 --- /dev/null +++ b/src/router/index.tsx @@ -0,0 +1,96 @@ +/** + * 路由配置 + */ + +import React from 'react' +import { createBrowserRouter, Navigate } from 'react-router-dom' +import MainLayout from '@layout/MainLayout' +import Home from '@pages/Home' +import HeritageList from '@pages/Heritage/List' +import HeritageDetail from '@pages/Heritage/Detail' +import InheritorsList from '@pages/Inheritors/List' +import InheritorDetail from '@pages/Inheritor/Detail' +import NewsPage from '@pages/News' +import NewsDetail from '@pages/News/Detail' +import EventDetail from '@pages/News/EventDetail' +import Login from '@pages/User/Login' +import Register from '@pages/User/Register' +import UserCenter from '@pages/User/Center' +import SearchPage from '@pages/Search' +import DataVisualization from '@pages/Data' +import About from '@pages/About' + +const router = createBrowserRouter([ + { + path: '/', + element: , + children: [ + { + index: true, + element: , + }, + { + path: 'heritage', + element: , + }, + { + path: 'heritage/categories/:category', + element: , + }, + { + path: 'heritage/:id', + element: , + }, + { + path: 'inheritors', + element: , + }, + { + path: 'inheritor/:id', + element: , + }, + { + path: 'search', + element: , + }, + { + path: 'data', + element: , + }, + { + path: 'user/center', + element: , + }, + { + path: 'about', + element: , + }, + { + path: 'news', + element: , + }, + { + path: 'news/:id', + element: , + }, + { + path: 'events/:id', + element: , + }, + { + path: '*', + element: , + }, + ], + }, + { + path: '/login', + element: , + }, + { + path: '/register', + element: , + }, +]) + +export default router diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..108e55f --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,547 @@ +/** + * 非遗文化传承网站 - 全局样式 + * 包含渐变背景、动画效果、响应式断点、传统纹样装饰等 + */ + +/* ===== 全局重置与基础样式 ===== */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + font-size: 16px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji', 'Noto Sans SC', + 'Microsoft YaHei'; + font-size: 14px; + line-height: 1.5715; + color: #2c2c2c; + background-color: #fafaf8; + overflow-x: hidden; +} + +#root { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* ===== 滚动条美化 ===== */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: #f5f0e8; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: #d4a574; + border-radius: 4px; + transition: background 0.3s ease; +} + +::-webkit-scrollbar-thumb:hover { + background: #c8363d; +} + +/* ===== 选中文本样式 ===== */ +::selection { + background-color: rgba(200, 54, 61, 0.2); + color: #2c2c2c; +} + +::-moz-selection { + background-color: rgba(200, 54, 61, 0.2); + color: #2c2c2c; +} + +/* ===== 渐变背景工具类 ===== */ +.gradient-bg-primary { + background: linear-gradient(135deg, #c8363d 0%, #8b252b 100%); +} + +.gradient-bg-warm { + background: linear-gradient(135deg, #fff9f0 0%, #f5f0e8 100%); +} + +.gradient-bg-elegant { + background: linear-gradient(135deg, #fafaf8 0%, #ffffff 50%, #f5f0e8 100%); +} + +.gradient-text-primary { + background: linear-gradient(135deg, #c8363d 0%, #d4a574 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* ===== 传统纹样装饰 ===== */ +.pattern-cloud { + position: relative; +} + +.pattern-cloud::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 80px; + background-image: url("data:image/svg+xml,%3Csvg width='100' height='80' viewBox='0 0 100 80' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 40 Q 20 20, 30 40 T 50 40 T 70 40 T 90 40' stroke='%23E8E3DB' fill='none' stroke-width='1' opacity='0.3'/%3E%3C/svg%3E"); + background-repeat: repeat-x; + background-size: 100px 80px; + pointer-events: none; +} + +.pattern-wave { + position: relative; +} + +.pattern-wave::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 60px; + background-image: url("data:image/svg+xml,%3Csvg width='100%25' height='60' viewBox='0 0 1200 60' preserveAspectRatio='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0,30 Q300,0 600,30 T1200,30 L1200,60 L0,60 Z' fill='%23F5F0E8'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-size: cover; + pointer-events: none; +} + +/* ===== 卡片阴影层级 ===== */ +.shadow-sm { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); +} + +.shadow-md { + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); +} + +.shadow-lg { + box-shadow: 0 6px 24px rgba(0, 0, 0, 0.16); +} + +.shadow-xl { + box-shadow: 0 12px 48px rgba(0, 0, 0, 0.2); +} + +/* 纸张层叠效果 */ +.shadow-paper { + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08), 0 4px 8px rgba(0, 0, 0, 0.06), + 0 1px 2px rgba(0, 0, 0, 0.04); +} + +/* ===== 玻璃态效果 ===== */ +.glass-effect { + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(232, 227, 219, 0.3); +} + +.glass-effect-dark { + background: rgba(44, 44, 44, 0.8); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +/* ===== 动画效果 ===== */ + +/* 渐显动画 */ +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* 缩放动画 */ +@keyframes scaleIn { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +} + +/* 滑入动画 */ +@keyframes slideInLeft { + from { + opacity: 0; + transform: translateX(-50px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(50px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* 脉冲动画 */ +@keyframes pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +/* 浮动动画 */ +@keyframes float { + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-10px); + } +} + +/* 动画工具类 */ +.animate-fade-in { + animation: fadeIn 0.6s ease-out; +} + +.animate-fade-in-up { + animation: fadeInUp 0.8s ease-out; +} + +.animate-fade-in-down { + animation: fadeInDown 0.8s ease-out; +} + +.animate-scale-in { + animation: scaleIn 0.5s ease-out; +} + +.animate-slide-in-left { + animation: slideInLeft 0.8s ease-out; +} + +.animate-slide-in-right { + animation: slideInRight 0.8s ease-out; +} + +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +.animate-float { + animation: float 3s ease-in-out infinite; +} + +/* ===== 过渡效果 ===== */ +.transition-smooth { + transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); +} + +.transition-fast { + transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); +} + +.transition-slow { + transition: all 0.5s cubic-bezier(0.645, 0.045, 0.355, 1); +} + +/* ===== 悬停效果 ===== */ +.hover-lift { + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.hover-lift:hover { + transform: translateY(-4px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); +} + +.hover-glow { + transition: box-shadow 0.3s ease; +} + +.hover-glow:hover { + box-shadow: 0 0 20px rgba(200, 54, 61, 0.3); +} + +.hover-scale { + transition: transform 0.3s ease; +} + +.hover-scale:hover { + transform: scale(1.05); +} + +/* ===== 文本样式 ===== */ +.text-serif { + font-family: 'Noto Serif SC', 'Songti SC', Georgia, serif; +} + +.text-gradient-primary { + background: linear-gradient(135deg, #c8363d 0%, #d4a574 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + font-weight: 600; +} + +.text-shadow-sm { + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.text-shadow-md { + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); +} + +/* ===== 容器与布局 ===== */ +.container-full { + width: 100%; + padding-left: 24px; + padding-right: 24px; + margin: 0 auto; +} + +.container { + width: 100%; + max-width: 1200px; + padding-left: 24px; + padding-right: 24px; + margin: 0 auto; +} + +.container-wide { + width: 100%; + max-width: 1440px; + padding-left: 24px; + padding-right: 24px; + margin: 0 auto; +} + +.section-spacing { + padding: 80px 0; +} + +.section-spacing-lg { + padding: 120px 0; +} + +.section-spacing-sm { + padding: 60px 0; +} + +/* ===== 响应式断点 ===== */ + +/* 平板 */ +@media (max-width: 992px) { + .container, + .container-wide, + .container-full { + padding-left: 20px; + padding-right: 20px; + } + + .section-spacing { + padding: 60px 0; + } + + .section-spacing-lg { + padding: 80px 0; + } + + .section-spacing-sm { + padding: 40px 0; + } +} + +/* 移动设备 */ +@media (max-width: 768px) { + html { + font-size: 14px; + } + + .container, + .container-wide, + .container-full { + padding-left: 16px; + padding-right: 16px; + } + + .section-spacing { + padding: 40px 0; + } + + .section-spacing-lg { + padding: 60px 0; + } + + .section-spacing-sm { + padding: 32px 0; + } +} + +/* 小屏移动设备 */ +@media (max-width: 576px) { + .section-spacing { + padding: 32px 0; + } + + .section-spacing-lg { + padding: 48px 0; + } + + .section-spacing-sm { + padding: 24px 0; + } +} + +/* ===== 工具类 ===== */ +.text-center { + text-align: center; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.flex { + display: flex; +} + +.flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +.flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.relative { + position: relative; +} + +.absolute { + position: absolute; +} + +.overflow-hidden { + overflow: hidden; +} + +.cursor-pointer { + cursor: pointer; +} + +/* ===== 图片优化 ===== */ +img { + max-width: 100%; + height: auto; + display: block; +} + +.img-cover { + width: 100%; + height: 100%; + object-fit: cover; +} + +.img-contain { + width: 100%; + height: 100%; + object-fit: contain; +} + +/* ===== 骨架屏优化 ===== */ +.skeleton-loading { + background: linear-gradient( + 90deg, + #f5f0e8 25%, + rgba(245, 240, 232, 0.5) 50%, + #f5f0e8 75% + ); + background-size: 200% 100%; + animation: skeleton-loading 1.5s ease-in-out infinite; +} + +@keyframes skeleton-loading { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} + +/* ===== 打印样式 ===== */ +@media print { + body { + background: white; + } + + .no-print { + display: none !important; + } +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +///