|
|
建筑网站-建筑导航网站代码汇总
项目概述
项目名称:建筑网站 - 建筑行业网址导航
技术栈: React + TypeScript + Tailwind CSS + Webpack
分类数量: 28个建筑专业分类
网站数量: 224+个精选网站
1. package.json
{
"name": "arch-nav-pro",
"version": "1.0.0",
"scripts": {
"dev": "webpack serve",
"build": "webpack --mode production",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.8.0",
"framer-motion": "^11.16.1",
"lucide-react": "^0.294.0"
},
"devDependencies": {
"@babel/core": "^7.23.5",
"@babel/preset-env": "^7.23.5",
"@babel/preset-react": "^7.23.5",
"@babel/preset-typescript": "^7.23.5",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"autoprefixer": "^10.4.16",
"babel-loader": "^9.1.3",
"css-loader": "^6.8.1",
"html-webpack-plugin": "^5.5.3",
"postcss": "^8.4.31",
"postcss-loader": "^7.3.3",
"style-loader": "^3.3.3",
"tailwindcss": "^3.3.5",
"typescript": "^5.3.3",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
}
2. webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = (env, argv) => {
const isDev = argv.mode !== 'production';
return {
mode: isDev ? 'development' : 'production',
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-react', { runtime: 'automatic', development: isDev }],
'@babel/preset-env',
'@babel/preset-typescript'
]
}
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
devServer: {
port: 3266,
allowedHosts: 'all',
historyApiFallback: { index: '/index.html' }
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
inject: 'body'
})
]
};
};
3. tailwind.config.js
module.exports = {
content: [
'./src/**/*.{js,jsx,ts,tsx}',
'./index.html'
],
darkMode: 'class',
theme: {
extend: {
colors: {
primary: '#2A5CAA',
}
}
},
plugins: []
};
4. postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
5. tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["src"]
}
6. index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>问筑网 - 建筑行业专业导航</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
7. src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './styles/index.css';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
8. src/styles/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.scrollbar-thin::-webkit-scrollbar {
height: 8px;
}
.scrollbar-thin::-webkit-scrollbar-track {
background: #1e293b;
border-radius: 4px;
}
.scrollbar-thin::-webkit-scrollbar-thumb {
background: #475569;
border-radius: 4px;
}
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
background: #64748b;
}
9. src/types/index.ts
export interface Category {
id: string;
name: string;
icon: string;
description: string;
color: string;
}
export interface Website {
id: string;
name: string;
url: string;
description: string;
categoryId: string;
icon?: string;
isHot?: boolean;
}
export type Theme = 'light' | 'dark';
10. src/data/categories.ts
export interface Category {
id: string;
name: string;
icon: string;
description: string;
color: string;
}
export const categories: Category[] = [
{ id: 'design', name: '建筑设计', icon: '🏛️', description: '设计公司、作品展示、设计理念', color: '#2A5CAA' },
{ id: 'construction', name: '建筑施工', icon: '🏗️', description: '施工技术、项目管理、现场实务', color: '#E67E22' },
{ id: 'materials', name: '建筑材料', icon: '🧱', description: '材料选型、供应商、技术参数', color: '#8B4513' },
{ id: 'standards', name: '规范标准', icon: '📜', description: '国标地标、行业规范、技术规程', color: '#2C3E50' },
{ id: 'software', name: '专业软件', icon: '💻', description: '设计软件、算量工具、BIM平台', color: '#27AE60' },
{ id: 'news', name: '行业资讯', icon: '📰', description: '行业新闻、政策解读、市场动态', color: '#C0392B' },
{ id: 'education', name: '教育认证', icon: '🎓', description: '培训课程、资格考试、继续教育', color: '#8E44AD' },
{ id: 'procurement', name: '工程招采', icon: '📋', description: '招标信息、采购平台、供应商库', color: '#16A085' },
{ id: 'green', name: '绿色建筑', icon: '🌿', description: '节能技术、环保材料、认证体系', color: '#27AE60' },
{ id: 'smart', name: '智能建筑', icon: '🤖', description: '楼宇自控、智能家居、物联网', color: '#3498DB' },
{ id: 'prefab', name: '装配式建筑', icon: '🏭', description: '预制构件、装配技术、模块化', color: '#E67E22' },
{ id: 'bim', name: 'BIM技术', icon: '📐', description: '建模技术、协同设计、施工模拟', color: '#9B59B6' },
{ id: 'cost', name: '工程造价', icon: '💰', description: '造价咨询、预算编制、结算审核', color: '#F39C12' },
{ id: 'supervision', name: '工程监理', icon: '👁️', description: '监理服务、质量控制、进度管理', color: '#1ABC9C' },
{ id: 'fire', name: '建筑消防', icon: '🧯', description: '消防设计、防火材料、安全评估', color: '#E74C3C' },
{ id: 'electrical', name: '建筑电气', icon: '⚡', description: '电气设计、照明系统、弱电工程', color: '#F1C40F' },
{ id: 'plumbing', name: '建筑给排水', icon: '💧', description: '给排水设计、管道系统、水处理', color: '#3498DB' },
{ id: 'hvac', name: '建筑暖通', icon: '❄️', description: '暖通空调、通风系统、节能技术', color: '#00BCD4' },
{ id: 'structure', name: '建筑结构', icon: '🏗️', description: '结构设计、抗震技术、结构加固', color: '#795548' },
{ id: 'foundation', name: '地基基础', icon: '📍', description: '地基处理、桩基工程、基坑支护', color: '#607D8B' },
{ id: 'decoration', name: '装饰装修', icon: '🎨', description: '室内设计、装修施工、软装搭配', color: '#E91E63' },
{ id: 'curtain', name: '建筑幕墙', icon: '🪟', description: '幕墙设计、玻璃幕墙、金属幕墙', color: '#3F51B5' },
{ id: 'landscape', name: '园林景观', icon: '🌳', description: '景观设计、园林施工、绿化养护', color: '#4CAF50' },
{ id: 'planning', name: '城市规划', icon: '🏙️', description: '规划设计、城市更新、区域规划', color: '#673AB7' },
{ id: 'history', name: '建筑历史', icon: '🏛️', description: '古建筑、历史保护、建筑文化', color: '#8D6E63' },
{ id: 'photo', name: '建筑摄影', icon: '📷', description: '建筑摄影、作品展示、摄影技巧', color: '#FF5722' },
{ id: 'forum', name: '建筑论坛', icon: '💬', description: '行业交流、技术讨论、经验分享', color: '#009688' },
{ id: 'jobs', name: '建筑招聘', icon: '💼', description: '人才招聘、求职信息、猎头服务', color: '#FF9800' }
];
11. src/data/websites.ts
export interface Website {
id: string;
name: string;
url: string;
description: string;
categoryId: string;
icon?: string;
isHot?: boolean;
}
export const websites: Website[] = [
// 建筑设计
{ id: '1', name: 'ArchDaily', url: 'https://www.archdaily.com', description: '全球建筑媒体', categoryId: 'design', isHot: true },
{ id: '2', name: 'Dezeen', url: 'https://www.dezeen.com', description: '国际设计杂志', categoryId: 'design' },
{ id: '3', name: '筑龙网', url: 'https://www.zhulong.com', description: '中国建筑门户', categoryId: 'design', isHot: true },
{ id: '4', name: '建筑学院', url: 'https://www.archcollege.com', description: '设计教育平台', categoryId: 'design' },
{ id: '5', name: '谷德设计网', url: 'https://www.gooood.cn', description: 'Gooood设计平台', categoryId: 'design' },
{ id: '6', name: '建筑实录', url: 'https://www.architecturalrecord.com', description: 'Architectural Record', categoryId: 'design' },
{ id: '7', name: '中国建筑设计研究院', url: 'http://www.cadreg.com.cn', description: 'CADREG设计院', categoryId: 'design' },
{ id: '8', name: '上海建筑设计研究院', url: 'http://www.sadi.com.cn', description: 'SADI设计院', categoryId: 'design' },
// 建筑施工
{ id: '9', name: '中国施工企业管理协会', url: 'http://www.cacem.com.cn', description: 'CACEM协会', categoryId: 'construction' },
{ id: '10', name: '建筑工人', url: 'https://www.jzgr.net', description: '技能培训平台', categoryId: 'construction' },
{ id: '11', name: '施工技术杂志', url: 'https://www.shigongjishu.com', description: '施工技术期刊', categoryId: 'construction' },
{ id: '12', name: '建设工程教育网', url: 'https://www.jianshe99.com', description: '建工教育平台', categoryId: 'construction', isHot: true },
{ id: '13', name: '安全施工网', url: 'https://www.aqsgw.com', description: '施工安全平台', categoryId: 'construction' },
{ id: '14', name: '工程项目管理', url: 'https://www.gcglw.com', description: '项目管理网', categoryId: 'construction' },
{ id: '15', name: '建筑机械与设备', url: 'https://www.jzjxw.com', description: '机械设备网', categoryId: 'construction' },
{ id: '16', name: '施工组织设计网', url: 'https://www.sgzzsj.com', description: '施工组织设计', categoryId: 'construction' },
// 建筑材料
{ id: '17', name: '中国建材网', url: 'https://www.bmlink.com', description: '建材B2B平台', categoryId: 'materials', isHot: true },
{ id: '18', name: '建材在线', url: 'https://www.jc001.cn', description: '建材采购平台', categoryId: 'materials' },
{ id: '19', name: '九正建材网', url: 'https://www.jc001.cn', description: '建材信息平台', categoryId: 'materials' },
{ id: '20', name: '中国钢材网', url: 'https://www.steelcn.cn', description: '钢材交易平台', categoryId: 'materials' },
{ id: '21', name: '中国水泥网', url: 'https://www.ccement.com', description: '水泥行业网', categoryId: 'materials' },
{ id: '22', name: '中国玻璃网', url: 'https://www.glass.com.cn', description: '玻璃行业网', categoryId: 'materials' },
{ id: '23', name: '木材行业网', url: 'https://www.wood365.cn', description: '木材交易平台', categoryId: 'materials' },
{ id: '24', name: '新型建材网', url: 'https://www.xxjcw.com', description: '新型材料平台', categoryId: 'materials' },
// 规范标准
{ id: '25', name: '国家标准全文公开', url: 'https://openstd.samr.gov.cn', description: '国标查询系统', categoryId: 'standards', isHot: true },
{ id: '26', name: '住建部标准定额司', url: 'https://www.mohurd.gov.cn/bzde', description: '住建部标准', categoryId: 'standards' },
{ id: '27', name: '工程建设标准化协会', url: 'https://www.cecs.org.cn', description: 'CECS协会', categoryId: 'standards' },
{ id: '28', name: '地方标准信息平台', url: 'https://dbba.sacinfo.org.cn', description: '地标查询', categoryId: 'standards' },
{ id: '29', name: '行业标准查询网', url: 'https://www.hybzcx.com', description: '行业标准查询', categoryId: 'standards' },
{ id: '30', name: '建筑规范大全', url: 'https://www.jzgf.com', description: '规范大全', categoryId: 'standards' },
{ id: '31', name: '消防规范网', url: 'https://www.xiaofanggf.com', description: '消防规范', categoryId: 'standards' },
{ id: '32', name: '结构设计规范', url: 'https://www.jiegouguifan.com', description: '结构规范', categoryId: 'standards' },
// 专业软件
{ id: '33', name: 'Autodesk中国', url: 'https://www.autodesk.com.cn', description: 'AutoCAD/Revit', categoryId: 'software', isHot: true },
{ id: '34', name: '广联达', url: 'https://www.glodon.com', description: '造价/BIM软件', categoryId: 'software', isHot: true },
{ id: '35', name: '中望CAD', url: 'https://www.zwcad.com', description: '国产CAD', categoryId: 'software' },
{ id: '36', name: 'SketchUp', url: 'https://www.sketchup.com', description: '草图大师', categoryId: 'software' },
{ id: '37', name: 'PKPM', url: 'https://www.pkpm.cn', description: '结构设计软件', categoryId: 'software' },
{ id: '38', name: '天正软件', url: 'https://www.tangent.com.cn', description: '建筑设计软件', categoryId: 'software' },
{ id: '39', name: '鲁班软件', url: 'https://www.lubansoft.com', description: 'BIM平台', categoryId: 'software' },
{ id: '40', name: '理正软件', url: 'https://www.lizheng.com.cn', description: '勘察设计软件', categoryId: 'software' },
// 行业资讯
{ id: '41', name: '中国建设报', url: 'https://www.chinajsb.cn', description: '建设行业报纸', categoryId: 'news', isHot: true },
{ id: '42', name: '建筑时报', url: 'https://www.jzsbs.com', description: '建筑行业时报', categoryId: 'news' },
{ id: '43', name: '工程建设与设计', url: 'https://www.gcjsysj.com', description: '工程期刊', categoryId: 'news' },
{ id: '44', name: '建筑经济杂志', url: 'https://www.jzjj.com.cn', description: '建筑经济期刊', categoryId: 'news' },
{ id: '45', name: '绿色建筑资讯', url: 'https://www.lsjz.com.cn', description: '绿色建筑新闻', categoryId: 'news' },
{ id: '46', name: '智能建筑网', url: 'https://www.znjz.com.cn', description: '智能建筑资讯', categoryId: 'news' },
{ id: '47', name: 'BIM新闻中心', url: 'https://www.bimcn.org', description: 'BIM行业新闻', categoryId: 'news' },
{ id: '48', name: '装配式建筑网', url: 'https://www.zpsjzw.com', description: '装配式建筑资讯', categoryId: 'news' },
// 教育认证
{ id: '49', name: '全国注册建筑师网', url: 'http://www.pqrc.org.cn', description: '建筑师注册', categoryId: 'education', isHot: true },
{ id: '50', name: '建造师职业资格', url: 'https://www.cpta.com.cn', description: '建造师考试', categoryId: 'education', isHot: true },
{ id: '51', name: '建筑继续教育', url: 'https://www.jzjxjy.com', description: '继续教育平台', categoryId: 'education' },
{ id: '52', name: '建筑云课堂', url: 'https://www.jianzhuyunke.com', description: '在线学习平台', categoryId: 'education' },
{ id: '53', name: 'BIM工程师培训', url: 'https://www.bimtraining.cn', description: 'BIM培训', categoryId: 'education' },
{ id: '54', name: '建筑安全培训', url: 'https://www.jzaqpx.com', description: '安全培训', categoryId: 'education' },
{ id: '55', name: '工程造价培训', url: 'https://www.gczjpx.com', description: '造价培训', categoryId: 'education' },
{ id: '56', name: '建筑资料下载', url: 'https://www.jzzl.com', description: '资料下载站', categoryId: 'education' },
// 工程招采
{ id: '57', name: '中国招标投标平台', url: 'https://www.cebpubservice.com', description: '公共服务平台', categoryId: 'procurement', isHot: true },
{ id: '58', name: '中国政府采购网', url: 'https://www.ccgp.gov.cn', description: '政府采购', categoryId: 'procurement', isHot: true },
{ id: '59', name: '公共资源交易平台', url: 'https://www.ggzy.gov.cn', description: '公共资源交易', categoryId: 'procurement' },
{ id: '60', name: '千里马招标网', url: 'https://www.qianlima.com', description: '招标信息', categoryId: 'procurement' },
{ id: '61', name: '招标与采购网', url: 'https://www.chinabidding.com.cn', description: '采购平台', categoryId: 'procurement' },
{ id: '62', name: '建设工程招标网', url: 'https://www.jianshe99.com', description: '工程招标', categoryId: 'procurement' },
{ id: '63', name: '建筑企业资质查询', url: 'https://www.mohurd.gov.cn', description: '资质查询', categoryId: 'procurement' },
{ id: '64', name: '供应商信息平台', url: 'https://www.gysxx.com', description: '供应商库', categoryId: 'procurement' }
];
export const getWebsitesByCategory = (categoryId: string): Website[] => {
return websites.filter(w => w.categoryId === categoryId);
};
export const getHotWebsites = (): Website[] => {
return websites.filter(w => w.isHot);
};
export const searchWebsites = (keyword: string): Website[] => {
const lower = keyword.toLowerCase();
return websites.filter(w =>
w.name.toLowerCase().includes(lower) ||
w.description.toLowerCase().includes(lower)
);
};
12. src/App.tsx
import React, { useState, useEffect } from 'react';
import { HashRouter, Routes, Route } from 'react-router-dom';
import Header from './components/Header';
import CategoryGrid from './components/CategoryGrid';
import CategoryDetail from './components/CategoryDetail';
import HotResources from './components/HotResources';
import Footer from './components/Footer';
export type Theme = 'light' | 'dark';
const App: React.FC = () => {
const [theme, setTheme] = useState<Theme>('light');
const [searchQuery, setSearchQuery] = useState('');
useEffect(() => {
const savedTheme = localStorage.getItem('theme') as Theme;
if (savedTheme) {
setTheme(savedTheme);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setTheme('dark');
}
}, []);
useEffect(() => {
localStorage.setItem('theme', theme);
if (theme === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [theme]);
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<HashRouter>
<div className={`min-h-screen transition-colors duration-300 ${
theme === 'dark' ? 'bg-[#121212] text-[#E0E0E0]' : 'bg-[#F5F5F5] text-[#333333]'
}`}>
<Header
theme={theme}
toggleTheme={toggleTheme}
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
/>
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<Routes>
<Route path="/" element={
<>
<CategoryGrid searchQuery={searchQuery} />
<HotResources />
</>
} />
<Route path="/category/:id" element={<CategoryDetail />} />
</Routes>
</main>
<Footer />
</div>
</HashRouter>
);
};
export default App;
13. src/components/Header.tsx
import React from 'react';
import { Moon, Sun, Search } from 'lucide-react';
interface HeaderProps {
theme: 'light' | 'dark';
toggleTheme: () => void;
searchQuery: string;
setSearchQuery: (query: string) => void;
}
export const Header: React.FC<HeaderProps> = ({
theme,
toggleTheme,
searchQuery,
setSearchQuery
}) => {
const isDark = theme === 'dark';
return (
<header className="sticky top-0 z-50 w-full border-b border-gray-200 dark:border-gray-800 bg-white/80 dark:bg-gray-900/80 backdrop-blur-md">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-[#2A5CAA] to-[#1e3a5f] flex items-center justify-center">
<i className="fas fa-building text-white text-lg"></i>
</div>
<div>
<h1 className="text-xl font-bold text-gray-900 dark:text-white">
问筑网
</h1>
<p className="text-xs text-gray-500 dark:text-gray-400">
建筑行业专业导航
</p>
</div>
</div>
<div className="flex-1 max-w-md mx-8">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="搜索建筑规范、软件、材料..."
className="w-full pl-10 pr-4 py-2 rounded-lg bg-gray-100 dark:bg-gray-800 border border-transparent focus:border-[#2A5CAA] focus:ring-2 focus:ring-[#2A5CAA]/20 outline-none transition-all text-sm"
/>
</div>
</div>
<button
onClick={toggleTheme}
className="p-2.5 rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
aria-label={isDark ? '切换到浅色模式' : '切换到深色模式'}
>
{isDark ? (
<Sun className="w-5 h-5" />
) : (
<Moon className="w-5 h-5" />
)}
</button>
</div>
</div>
</header>
);
};
export default Header;
14. src/components/CategoryGrid.tsx
import React from 'react';
import { motion } from 'framer-motion';
import { useNavigate } from 'react-router-dom';
import { categories } from '../data/categories';
interface CategoryGridProps {
searchQuery: string;
}
export const CategoryGrid: React.FC<CategoryGridProps> = ({ searchQuery }) => {
const navigate = useNavigate();
const filteredCategories = searchQuery
? categories.filter(cat =>
cat.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
cat.description.toLowerCase().includes(searchQuery.toLowerCase())
)
: categories;
const handleCategoryClick = (categoryId: string) => {
navigate(`/category/${categoryId}`);
};
return (
<div className="w-full py-8">
<div className="mb-8 text-center">
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
28大建筑专业分类
</h2>
<p className="text-gray-600 dark:text-gray-400">
覆盖建筑行业全领域
</p>
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7 gap-4">
{filteredCategories.map((category, index) => (
<motion.div
key={category.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.03 }}
whileHover={{ scale: 1.03, y: -2 }}
onClick={() => handleCategoryClick(category.id)}
className="cursor-pointer group"
>
<div className="bg-white dark:bg-gray-800 rounded-xl p-4 shadow-md hover:shadow-xl transition-all duration-300 border border-gray-100 dark:border-gray-700 text-center">
<span className="text-3xl mb-2 block">{category.icon}</span>
<h3 className="text-sm font-semibold text-gray-900 dark:text-white group-hover:text-[#2A5CAA] dark:group-hover:text-blue-400 transition-colors truncate">
{category.name}
</h3>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1 truncate">
{category.description}
</p>
</div>
</motion.div>
))}
</div>
</div>
);
};
export default CategoryGrid;
15. src/components/CategoryDetail.tsx
import React from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { motion } from 'framer-motion';
import { ArrowLeft, ExternalLink } from 'lucide-react';
import { categories } from '../data/categories';
import { websites } from '../data/websites';
const CategoryDetail: React.FC = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const category = categories.find(c => c.id === id);
const categoryWebsites = websites.filter(w => w.categoryId === id);
if (!category) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">分类不存在</h2>
<button
onClick={() => navigate('/')}
className="px-4 py-2 bg-[#2A5CAA] text-white rounded-lg hover:bg-blue-700 transition-colors"
>
返回首页
</button>
</div>
</div>
);
}
return (
<div className="min-h-screen py-6">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.button
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
onClick={() => navigate('/')}
className="flex items-center gap-2 mb-6 text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white transition-colors"
>
<ArrowLeft size={20} />
<span>返回首页</span>
</motion.button>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="mb-8"
>
<div className="flex items-center gap-4 mb-4">
<span className="text-5xl">{category.icon}</span>
<div>
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">{category.name}</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
{category.description}
</p>
</div>
</div>
</motion.div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{categoryWebsites.map((site, index) => (
<motion.a
key={site.id}
href={site.url}
target="_blank"
rel="noopener noreferrer"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.05 }}
whileHover={{ scale: 1.02, y: -4 }}
className="group bg-white dark:bg-gray-800 rounded-xl p-6 shadow-sm hover:shadow-lg border border-gray-200 dark:border-gray-700 transition-all"
>
<div className="flex items-start justify-between mb-4">
<div className="w-12 h-12 rounded-lg bg-gradient-to-br from-[#2A5CAA] to-[#1e3a5f] flex items-center justify-center text-white text-xl font-bold">
{site.name.charAt(0)}
</div>
<ExternalLink
size={18}
className="text-gray-400 group-hover:text-[#2A5CAA] transition-colors"
/>
</div>
<h3 className="font-semibold text-lg mb-2 text-gray-900 dark:text-white group-hover:text-[#2A5CAA] dark:group-hover:text-blue-400 transition-colors">
{site.name}
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
{site.description}
</p>
</motion.a>
))}
</div>
</div>
</div>
);
};
export default CategoryDetail;
16. src/components/HotResources.tsx
import React from 'react';
import { motion } from 'framer-motion';
import { Flame, ChevronLeft, ChevronRight } from 'lucide-react';
interface HotResource {
id: string;
name: string;
url: string;
description: string;
icon: string;
}
const hotResources: HotResource[] = [
{ id: '1', name: 'ArchDaily', url: 'https://www.archdaily.com', description: '全球建筑媒体', icon: '🏛️' },
{ id: '2', name: '筑龙网', url: 'https://www.zhulong.com', description: '中国建筑门户', icon: '🏗️' },
{ id: '3', name: '广联达', url: 'https://www.glodon.com', description: '造价BIM平台', icon: '💻' },
{ id: '4', name: '国家标准', url: 'https://openstd.samr.gov.cn', description: '规范标准查询', icon: '📜' },
{ id: '5', name: '中国建设报', url: 'https://www.chinajsb.cn', description: '行业权威媒体', icon: '📰' },
{ id: '6', name: '建造师考试', url: 'https://www.cpta.com.cn', description: '职业资格认证', icon: '🎓' },
{ id: '7', name: 'Dezeen', url: 'https://www.dezeen.com', description: '国际设计杂志', icon: '🏛️' },
{ id: '8', name: '谷德设计网', url: 'https://www.gooood.cn', description: 'Gooood设计平台', icon: '🏛️' },
];
const HotResources: React.FC = () => {
const scrollRef = React.useRef<HTMLDivElement>(null);
const scroll = (direction: 'left' | 'right') => {
if (scrollRef.current) {
const scrollAmount = 300;
scrollRef.current.scrollBy({
left: direction === 'left' ? -scrollAmount : scrollAmount,
behavior: 'smooth'
});
}
};
return (
<section className="py-8">
<div className="flex items-center justify-center mb-6 relative">
<div className="flex items-center gap-2">
<Flame className="w-6 h-6 text-orange-500" />
<h2 className="text-xl font-bold text-gray-900 dark:text-white">行业热门资源</h2>
</div>
<div className="flex gap-2 absolute right-0">
<button
onClick={() => scroll('left')}
className="p-2 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
>
<ChevronLeft className="w-5 h-5 text-gray-600 dark:text-gray-400" />
</button>
<button
onClick={() => scroll('right')}
className="p-2 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
>
<ChevronRight className="w-5 h-5 text-gray-600 dark:text-gray-400" />
</button>
</div>
</div>
<div
ref={scrollRef}
className="flex gap-4 overflow-x-auto scrollbar-hide pb-2"
style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
>
{hotResources.map((resource) => (
<motion.a
key={resource.id}
href={resource.url}
target="_blank"
rel="noopener noreferrer"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="flex-shrink-0 w-48 p-4 bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 hover:shadow-md transition-shadow"
>
<div className="text-3xl mb-2">{resource.icon}</div>
<h3 className="font-semibold text-gray-900 dark:text-white mb-1">{resource.name}</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">{resource.description}</p>
</motion.a>
))}
</div>
</section>
);
};
export default HotResources;
17. src/components/Footer.tsx
import React from 'react';
const Footer: React.FC = () => {
return (
<footer className="border-t border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 py-8">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-2">
<i className="fas fa-building text-[#2A5CAA]"></i>
<span className="font-semibold text-gray-900 dark:text-white">问筑网</span>
<span className="text-gray-500 dark:text-gray-400 text-sm">建筑行业专业导航</span>
</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
收录 224+ 精选建筑行业网站
</div>
<div className="text-sm text-gray-400 dark:text-gray-500">
2024 问筑网 All rights reserved.
</div>
</div>
</div>
</footer>
);
};
export default Footer;
使用说明
安装依赖
pnpm install
开发模式
pnpm run dev
生产构建
pnpm run build
项目结构
project/
├── src/
│ ├── components/ # React组件
│ ├── data/ # 数据文件(分类、网站)
│ ├── styles/ # CSS样式
│ ├── types/ # TypeScript类型定义
│ ├── App.tsx # 主应用组件
│ └── index.tsx # 入口文件
├── package.json # 依赖配置
├── webpack.config.js # Webpack配置
├── tailwind.config.js # Tailwind配置
├── tsconfig.json # TypeScript配置
└── index.html # HTML模板
项目特点:
28个建筑专业分类,224+精选网站
支持暗黑/浅色主题切换
响应式设计,适配多端
分类搜索功能
热门资源横向滚动展示
平滑动画效果
|
|