返回列表 发布新帖
查看: 5|回复: 0

建筑网站-建筑导航网站代码汇总

[复制链接] [复制链接]

86

主题

13

回帖

186

积分

管理员

积分
186
发表于 2 小时前 | 查看全部 |阅读模式
QQ20260417-135742.png
建筑网站-建筑导航网站代码汇总
项目概述
项目名称:建筑网站 - 建筑行业网址导航
技术栈: 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+精选网站
支持暗黑/浅色主题切换
响应式设计,适配多端
分类搜索功能
热门资源横向滚动展示
平滑动画效果

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

投诉/建议联系

admin@archask.com

未经授权禁止转载,复制和建立镜像,
如有违反,追究法律责任
  • 关注视频号
  • 关注抖音号
www.archask.com问筑 © 2001-2026 Discuz! Team. Powered by Discuz! W1.5 蜀ICP备20022646号
关灯 在本版发帖
扫一扫添加微信客服
返回顶部
快速回复 返回顶部 返回列表