随机城市生成6.0edit icon

作者:
邓朝元
Fork(复制)
下载
嵌入
BUG反馈
index.html
现在支持上传本地图片了!
index.html
            
            <!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>🏙️ 终极2D城市模拟器 - 100项信息</title>
  <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet"/>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/>
  <!-- PDF 导出 -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
  <!-- 图表支持 -->
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      font-family: 'Noto Sans SC', 'Roboto', sans-serif;
      background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
      color: #fff;
      min-height: 100vh;
      padding: 20px;
      background-attachment: fixed;
    }
    .container {
      max-width: 1600px;
      margin: 0 auto;
    }
    header {
      text-align: center;
      margin-bottom: 25px;
    }
    h1 {
      font-size: 2.8em;
      margin-bottom: 8px;
      background: linear-gradient(90deg, #ffd166, #06d6a0);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      text-shadow: 0 2px 10px rgba(0,0,0,0.2);
    }
    .subtitle {
      font-size: 1.2em;
      opacity: 0.9;
      margin-bottom: 15px;
    }
    .controls {
      display: flex;
      justify-content: center;
      gap: 15px;
      flex-wrap: wrap;
      margin: 20px auto;
    }
    button {
      padding: 12px 24px;
      font-size: 1.05em;
      border: none;
      border-radius: 50px;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 8px;
      transition: all 0.3s ease;
      box-shadow: 0 4px 15px rgba(0,0,0,0.2);
    }
    .btn-generate { background: linear-gradient(45deg, #ff6b6b, #ff8e53); color: white; }
    .btn-print { background: linear-gradient(45deg, #4ecdc4, #1a936f); color: white; }
    .btn-pdf { background: linear-gradient(45deg, #45b7d1, #2980b9); color: white; }
    .btn-data { background: linear-gradient(45deg, #96ceb4, #6ab04c); color: white; }
    .btn-reset { background: linear-gradient(45deg, #fd79a8, #e84393); color: white; }
    button:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 20px rgba(0,0,0,0.3);
    }
    .loading {
      text-align: center;
      font-size: 1.2em;
      margin: 20px 0;
      color: #ffd166;
      display: none;
    }
    .city-container {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
      gap: 25px;
      margin-top: 20px;
    }
    @media (max-width: 768px) {
      .city-container { grid-template-columns: 1fr; }
      .controls { flex-direction: column; align-items: center; }
    }
    .info-panel {
      background: rgba(255, 255, 255, 0.08);
      backdrop-filter: blur(12px);
      border-radius: 18px;
      padding: 25px;
      box-shadow: 0 10px 30px rgba(0,0,0,0.25);
      border: 1px solid rgba(255,255,255,0.1);
      transition: transform 0.3s ease;
    }
    .info-panel:hover {
      transform: translateY(-5px);
    }
    .info-panel h2 {
      font-size: 1.6em;
      margin-bottom: 20px;
      color: #ffd166;
      border-bottom: 2px solid #ffd166;
      padding-bottom: 8px;
      display: flex;
      align-items: center;
      gap: 10px;
    }
    .info-item {
      margin: 12px 0;
      line-height: 1.6;
      font-size: 1.02em;
    }
    .info-item i {
      width: 30px;
      color: #a29bfe;
    }
    .map-container {
      grid-column: 1 / -1;
      text-align: center;
      margin: 30px 0;
    }
    .map-container h2 {
      margin-bottom: 15px;
      color: #06d6a0;
    }
    #real-map {
      width: 100%;
      height: 600px;
      border: 2px solid rgba(255,255,255,0.2);
      border-radius: 12px;
      background: #1a1a2e;
      overflow: hidden;
      position: relative;
    }
    .chart-container {
      width: 100%;
      height: 300px;
      margin-top: 20px;
    }
    .timeline {
      margin-top: 25px;
      position: relative;
      padding-left: 30px;
    }
    .timeline::before {
      content: '';
      position: absolute;
      left: 15px;
      top: 0;
      bottom: 0;
      width: 4px;
      background: #ffd166;
      border-radius: 2px;
    }
    .timeline-item {
      margin-bottom: 20px;
      position: relative;
    }
    .timeline-item::before {
      content: '';
      position: absolute;
      left: -27px;
      top: 8px;
      width: 16px;
      height: 16px;
      border-radius: 50%;
      background: #4ecdc4;
      border: 3px solid #1a1a2e;
    }
    .timeline-year {
      font-weight: bold;
      color: #ffd166;
    }
    .stats-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      gap: 15px;
      margin-top: 20px;
    }
    .stat-card {
      background: rgba(255,255,255,0.1);
      padding: 15px;
      border-radius: 12px;
      text-align: center;
    }
    .stat-value {
      font-size: 1.8em;
      font-weight: bold;
      color: #ffd166;
      margin: 10px 0;
    }
    .stat-label {
      font-size: 0.9em;
      opacity: 0.8;
    }
    footer {
      text-align: center;
      margin-top: 50px;
      opacity: 0.75;
      font-size: 0.95em;
    }
    @media print {
      .controls, .loading, footer, header button { display: none !important; }
      body { background: white; color: black; }
      .info-panel { background: white !important; color: black !important; }
    }
  </style>
</head>
<body>
  <!-- 音效 -->
  <audio id="bgMusic" loop>
    <source src="https://www.soundjay.com/misc/sounds/bell-ringing-05.mp3" type="audio/mpeg">
  </audio>
  <audio id="clickSound">
    <source src="https://www.soundjay.com/buttons/sounds/button-1.mp3" type="audio/mpeg">
  </audio>
  <audio id="mapClickSound">
    <source src="https://www.soundjay.com/buttons/sounds/button-09.mp3" type="audio/mpeg">
  </audio>
  <audio id="generateSound">
    <source src="https://www.soundjay.com/buttons/sounds/button-09.mp3" type="audio/mpeg">
  </audio>

  <div class="container">
    <header>
      <h1><i class="fas fa-city"></i> 终极2D城市模拟器</h1>
      <p class="subtitle">100项城市信息 + 真实地理地图 + 历史时间轴 + 经济生态系统</p>
      
      <div class="controls">
        <button id="generateBtn" class="btn-generate">
          <i class="fas fa-magic"></i> 生成城市
        </button>
        <button id="printBtn" class="btn-print">
          <i class="fas fa-print"></i> 打印报告
        </button>
        <button id="exportPdfBtn" class="btn-pdf">
          <i class="fas fa-file-pdf"></i> 导出 PDF
        </button>
        <button id="exportDataBtn" class="btn-data">
          <i class="fas fa-file-csv"></i> 导出数据
        </button>
        <button id="resetBtn" class="btn-reset">
          <i class="fas fa-redo"></i> 重置
        </button>
      </div>
      <div class="loading" id="loading">🌍 正在生成100项城市数据...</div>
    </header>

    <div id="output" style="display: none;">
      <!-- 统计卡片 -->
      <div class="stats-grid">
        <div class="stat-card">
          <div class="stat-label">人口</div>
          <div class="stat-value" id="statPopulation">0</div>
          <div class="stat-label">人</div>
        </div>
        <div class="stat-card">
          <div class="stat-label">GDP</div>
          <div class="stat-value" id="statGDP">0</div>
          <div class="stat-label">亿美元</div>
        </div>
        <div class="stat-card">
          <div class="stat-label">幸福指数</div>
          <div class="stat-value" id="statHappiness">0</div>
          <div class="stat-label">/100</div>
        </div>
        <div class="stat-card">
          <div class="stat-label">安全指数</div>
          <div class="stat-value" id="statSafety">0</div>
          <div class="stat-label">/100</div>
        </div>
      </div>

      <div class="city-container">
        <!-- 基础信息 -->
        <div class="info-panel">
          <h2><i class="fas fa-city"></i> 城市基础信息</h2>
          <div class="info-item"><i class="fas fa-tag"></i> <strong>城市名称:</strong> <span id="cityName"></span></div>
          <div class="info-item"><i class="fas fa-globe-americas"></i> <strong>国家:</strong> <span id="country"></span></div>
          <div class="info-item"><i class="fas fa-map-marker-alt"></i> <strong>地理位置:</strong> <span id="location"></span></div>
          <div class="info-item"><i class="fas fa-mountain"></i> <strong>地形:</strong> <span id="terrain"></span></div>
          <div class="info-item"><i class="fas fa-thermometer-half"></i> <strong>气候类型:</strong> <span id="climate"></span></div>
          <div class="info-item"><i class="fas fa-sun"></i> <strong>年均温:</strong> <span id="avgTemp"></span></div>
          <div class="info-item"><i class="fas fa-cloud"></i> <strong>当前天气:</strong> <span id="weather"></span></div>
          <div class="info-item"><i class="fas fa-water"></i> <strong>主要河流:</strong> <span id="river"></span></div>
          <div class="info-item"><i class="fas fa-mountain"></i> <strong>主要山脉:</strong> <span id="mountain"></span></div>
          <div class="info-item"><i class="fas fa-ruler-vertical"></i> <strong>海拔:</strong> <span id="altitude"></span> 米</div>
        </div>

        <!-- 人口经济 -->
        <div class="info-panel">
          <h2><i class="fas fa-chart-line"></i> 人口与经济</h2>
          <div class="info-item"><i class="fas fa-users"></i> <strong>人口:</strong> <span id="population"></span></div>
          <div class="info-item"><i class="fas fa-ruler-combined"></i> <strong>面积:</strong> <span id="area"></span> km²</div>
          <div class="info-item"><i class="fas fa-weight-hanging"></i> <strong>人口密度:</strong> <span id="density"></span> 人/km²</div>
          <div class="info-item"><i class="fas fa-industry"></i> <strong>支柱产业:</strong> <span id="industry"></span></div>
          <div class="info-item"><i class="fas fa-dollar-sign"></i> <strong>GDP总量:</strong> <span id="gdp"></span> 亿美元</div>
          <div class="info-item"><i class="fas fa-hand-holding-usd"></i> <strong>人均GDP:</strong> <span id="perCapitaGDP"></span> 美元</div>
          <div class="info-item"><i class="fas fa-percentage"></i> <strong>失业率:</strong> <span id="unemployment"></span></div>
          <div class="info-item"><i class="fas fa-balance-scale"></i> <strong>税收率:</strong> <span id="taxRate"></span></div>
          <div class="info-item"><i class="fas fa-tree"></i> <strong>绿化率:</strong> <span id="greenRate"></span></div>
        </div>

        <!-- 政府官员 -->
        <div class="info-panel">
          <h2><i class="fas fa-user-tie"></i> 政府官员</h2>
          <div class="info-item"><i class="fas fa-user"></i> <strong>市长:</strong> <span id="mayor"></span></div>
          <div class="info-item"><i class="fas fa-user-friends"></i> <strong>副市长:</strong> <span id="viceMayor"></span></div>
          <div class="info-item"><i class="fas fa-users-cog"></i> <strong>政党:</strong> <span id="party"></span></div>
          <div class="info-item"><i class="fas fa-calendar-check"></i> <strong>任期:</strong> <span id="term"></span></div>
          <div class="info-item"><i class="fas fa-shield-alt"></i> <strong>警察局长:</strong> <span id="policeChief"></span></div>
          <div class="info-item"><i class="fas fa-money-bill-wave"></i> <strong>财政局长:</strong> <span id="financeChief"></span></div>
          <div class="info-item"><i class="fas fa-drafting-compass"></i> <strong>规划局长:</strong> <span id="planningChief"></span></div>
          <div class="info-item"><i class="fas fa-graduation-cap"></i> <strong>教育局长:</strong> <span id="educationChief"></span></div>
          <div class="info-item"><i class="fas fa-heartbeat"></i> <strong>卫生局长:</strong> <span id="healthChief"></span></div>
        </div>

        <!-- 人口结构 -->
        <div class="info-panel">
          <h2><i class="fas fa-democrat"></i> 人口结构</h2>
          <div class="info-item"><i class="fas fa-venus-mars"></i> <strong>性别比:</strong> <span id="genderRatio"></span></div>
          <div class="info-item"><i class="fas fa-baby"></i> <strong>出生率:</strong> <span id="birthRate"></span></div>
          <div class="info-item"><i class="fas fa-skull"></i> <strong>死亡率:</strong> <span id="deathRate"></span></div>
          <div class="info-item"><i class="fas fa-heartbeat"></i> <strong>平均寿命:</strong> <span id="lifeExpectancy"></span></div>
          <div class="info-item"><i class="fas fa-plane-arrival"></i> <strong>移民率:</strong> <span id="migration"></span></div>
          <div class="info-item"><i class="fas fa-user-graduate"></i> <strong>教育水平:</strong> <span id="education"></span></div>
          <div class="info-item"><i class="fas fa-layer-group"></i> <strong>年龄分布:</strong> <span id="ageDist"></span></div>
        </div>

        <!-- 企业生态 -->
        <div class="info-panel">
          <h2><i class="fas fa-building"></i> 企业生态系统</h2>
          <div class="info-item"><i class="fas fa-building"></i> <strong>上市公司:</strong> <span id="listedCompanies"></span> 家</div>
          <div class="info-item"><i class="fas fa-lightbulb"></i> <strong>创业公司:</strong> <span id="startups"></span> 家</div>
          <div class="info-item"><i class="fas fa-headquarters"></i> <strong>总部数量:</strong> <span id="headquarters"></span> 家</div>
          <div class="info-item"><i class="fas fa-users"></i> <strong>员工总数:</strong> <span id="totalEmployees"></span> 人</div>
          <div class="info-item"><i class="fas fa-chart-line"></i> <strong>股市指数:</strong> <span id="stockIndex"></span></div>
          <div class="info-item"><i class="fas fa-home"></i> <strong>房价指数:</strong> <span id="housingIndex"></span></div>
          <div class="info-item"><i class="fas fa-briefcase"></i> <strong>就业率:</strong> <span id="employmentRate"></span></div>
        </div>

        <!-- 基础设施 -->
        <div class="info-panel">
          <h2><i class="fas fa-road"></i> 基础设施</h2>
          <div class="info-item"><i class="fas fa-subway"></i> <strong>地铁线路:</strong> <span id="subwayLines"></span> 条</div>
          <div class="info-item"><i class="fas fa-bus"></i> <strong>公交站点:</strong> <span id="busStops"></span> 个</div>
          <div class="info-item"><i class="fas fa-road"></i> <strong>道路总长:</strong> <span id="roadLength"></span> 公里</div>
          <div class="info-item"><i class="fas fa-bridge"></i> <strong>桥梁数量:</strong> <span id="bridges"></span> 座</div>
          <div class="info-item"><i class="fas fa-plane"></i> <strong>机场:</strong> <span id="airport"></span></div>
          <div class="info-item"><i class="fas fa-train"></i> <strong>火车站:</strong> <span id="trainStation"></span></div>
          <div class="info-item"><i class="fas fa-ship"></i> <strong>港口:</strong> <span id="port"></span></div>
        </div>

        <!-- 文化教育 -->
        <div class="info-panel">
          <h2><i class="fas fa-graduation-cap"></i> 文化与教育</h2>
          <div class="info-item"><i class="fas fa-university"></i> <strong>大学:</strong> <span id="universities"></span> 所</div>
          <div class="info-item"><i class="fas fa-school"></i> <strong>学校总数:</strong> <span id="schools"></span> 所</div>
          <div class="info-item"><i class="fas fa-book"></i> <strong>图书馆:</strong> <span id="libraries"></span> 个</div>
          <div class="info-item"><i class="fas fa-landmark"></i> <strong>文化地标:</strong> <span id="landmarks"></span></div>
          <div class="info-item"><i class="fas fa-museum"></i> <strong>博物馆:</strong> <span id="museums"></span> 个</div>
          <div class="info-item"><i class="fas fa-theater-masks"></i> <strong>剧院:</strong> <span id="theaters"></span> 个</div>
          <div class="info-item"><i class="fas fa-football-ball"></i> <strong>体育馆:</strong> <span id="stadiums"></span> 个</div>
        </div>

        <!-- 环境能源 -->
        <div class="info-panel">
          <h2><i class="fas fa-leaf"></i> 环境与能源</h2>
          <div class="info-item"><i class="fas fa-bolt"></i> <strong>电力供应:</strong> <span id="electricity"></span> 亿千瓦时</div>
          <div class="info-item"><i class="fas fa-tint"></i> <strong>水资源:</strong> <span id="water"></span> 万吨</div>
          <div class="info-item"><i class="fas fa-trash"></i> <strong>垃圾处理:</strong> <span id="waste"></span> 吨/日</div>
          <div class="info-item"><i class="fas fa-wifi"></i> <strong>网络覆盖:</strong> <span id="internet"></span>%</div>
          <div class="info-item"><i class="fas fa-5g"></i> <strong>5G覆盖率:</strong> <span id="fiveG"></span>%</div>
          <div class="info-item"><i class="fas fa-recycle"></i> <strong>环保等级:</strong> <span id="environment"></span></div>
          <div class="info-item"><i class="fas fa-wind"></i> <strong>可再生能源:</strong> <span id="renewable"></span>%</div>
        </div>

        <!-- 旅游娱乐 -->
        <div class="info-panel">
          <h2><i class="fas fa-tree"></i> 旅游与娱乐</h2>
          <div class="info-item"><i class="fas fa-tree"></i> <strong>公园数量:</strong> <span id="parks"></span> 个</div>
          <div class="info-item"><i class="fas fa-hotel"></i> <strong>酒店数量:</strong> <span id="hotels"></span> 家</div>
          <div class="info-item"><i class="fas fa-utensils"></i> <strong>餐厅数量:</strong> <span id="restaurants"></span> 家</div>
          <div class="info-item"><i class="fas fa-shopping-cart"></i> <strong>购物中心:</strong> <span id="malls"></span> 个</div>
          <div class="info-item"><i class="fas fa-gamepad"></i> <strong>娱乐场所:</strong> <span id="entertainment"></span> 个</div>
          <div class="info-item"><i class="fas fa-hospital"></i> <strong>医院数量:</strong> <span id="hospitals"></span> 家</div>
        </div>

        <!-- 综合指数 -->
        <div class="info-panel">
          <h2><i class="fas fa-star"></i> 综合指数</h2>
          <div class="info-item"><i class="fas fa-smile"></i> <strong>幸福指数:</strong> <span id="happinessIndex"></span>/100</div>
          <div class="info-item"><i class="fas fa-shield-alt"></i> <strong>安全指数:</strong> <span id="safetyIndex"></span>/100</div>
          <div class="info-item"><i class="fas fa-leaf"></i> <strong>环境指数:</strong> <span id="environmentIndex"></span>/100</div>
          <div class="info-item"><i class="fas fa-car"></i> <strong>交通指数:</strong> <span id="transportIndex"></span>/100</div>
          <div class="info-item"><i class="fas fa-graduation-cap"></i> <strong>教育指数:</strong> <span id="educationIndex"></span>/100</div>
          <div class="info-item"><i class="fas fa-heartbeat"></i> <strong>医疗指数:</strong> <span id="healthIndex"></span>/100</div>
          <div class="info-item"><i class="fas fa-chart-line"></i> <strong>经济指数:</strong> <span id="economyIndex"></span>/100</div>
          <div class="info-item"><i class="fas fa-trophy"></i> <strong>综合评分:</strong> <span id="overallScore"></span>/100</div>
        </div>
      </div>

      <!-- 历史时间轴 -->
      <div class="info-panel">
        <h2><i class="fas fa-book"></i> 历史时间轴 (20个事件)</h2>
        <div class="timeline" id="historyTimeline"></div>
      </div>

      <!-- 图表 -->
      <div class="info-panel">
        <h2><i class="fas fa-chart-pie"></i> 人口年龄结构</h2>
        <div class="chart-container">
          <canvas id="ageChart"></canvas>
        </div>
      </div>

      <!-- 真实地图 -->
      <div class="info-panel map-container">
        <h2><i class="fas fa-map"></i> 城市地理地图</h2>
        <div id="real-map"></div>
        <p><small>📌 点击地图查看区域详情</small></p>
      </div>
    </div>

    <footer>
      终极2D城市模拟器 v6.0 | 100项城市信息 | 数据完全虚构 | 支持打印、导出、图表 | 使用 HTML/CSS/JS 构建
    </footer>
  </div>

  <script>
    // ======================
    // 音效控制
    // ======================
    const bgMusic = document.getElementById('bgMusic');
    const clickSound = document.getElementById('clickSound');
    const mapClickSound = document.getElementById('mapClickSound');
    const generateSound = document.getElementById('generateSound');

    function playSound(sound) {
      if (sound) {
        sound.currentTime = 0;
        sound.play().catch(() => {});
      }
    }

    // ======================
    // 数据词库
    // ======================
    const CITY_PREFIXES = ["新", "港", "湖", "北", "南", "东", "西", "堡", "大", "春", " mills", "岩", "山", "阳", "湾", "谷", "岛", "滩", "岭", "峰"];
    const CITY_SUFFIXES = ["市", "城", "堡", "港", "镇", "区", "谷", "岛", "滩", "岭", "峰", "湾", "洲", "府", "都"];
    const COUNTRIES = ["美国", "加拿大", "英国", "德国", "法国", "澳大利亚", "瑞典", "挪威", "奥地利", "瑞士", "日本", "韩国", "新加坡", "新西兰", "荷兰"];
    const TERRAINS = ["沿海平原", "内陆丘陵", "山区", "河谷", "岛屿", "沙漠边缘", "森林地带", "火山地带", "冰川地区", "峡谷地貌", "盆地地形", "高原地区"];
    const CLIMATES = [
      { name: "温带季风气候", temp: "8-15°C" },
      { name: "地中海气候", temp: "12-20°C" },
      { name: "大陆性气候", temp: "1-25°C" },
      { name: "干旱气候", temp: "18-35°C" },
      { name: "亚热带湿润气候", temp: "15-28°C" },
      { name: "海洋性气候", temp: "10-18°C" },
      { name: "热带雨林气候", temp: "25-30°C" },
      { name: "高山气候", temp: "-5-15°C" }
    ];
    const WEATHERS = ["晴天", "多云", "雨天", "雪天", "雾天", "暴风雨", "雷阵雨", "冰雹", "台风", "沙尘暴"];
    const INDUSTRIES = ["科技", "制造业", "旅游业", "教育", "医疗", "农业", "金融", "新能源", "娱乐", "物流", "生物医药", "人工智能", "航空航天", "文化创意"];
    const MOTTOES = ["团结进步", "创新与传统", "自然与城市交融", "为明天而建", "安全、强大、可持续", "智慧之城", "绿色家园", "未来已来"];
    const NICKNAMES = ["翡翠之城", "河畔明珠", "北方硅谷", "山谷之心", "双湖之城", "科技之都", "文化名城", "宜居天堂", "创新高地", "梦想之城"];
    const NAMES_MALE = ["詹姆斯", "约翰", "罗伯特", "迈克尔", "威廉", "大卫", "理查德", "约瑟夫", "托马斯", "查尔斯", "克里斯", "安德鲁", "丹尼尔", "马修", "杰森"];
    const NAMES_FEMALE = ["玛丽", "帕特里夏", "詹妮弗", "琳达", "伊丽莎白", "芭芭拉", "苏珊", "杰西卡", "莎拉", "凯伦", "艾米", "丽莎", "海伦", "南希", "贝蒂"];
    const SURNAMES = ["史密斯", "约翰逊", "威廉姆斯", "布朗", "琼斯", "加西亚", "米勒", "戴维斯", "罗德里格斯", "马丁内斯", "赫尔南德斯", "洛佩兹", "冈萨雷斯", "威尔逊", "安德森"];
    const PARTIES = ["进步党", "保守党", "自由党", "绿党", "独立党", "团结党", "民主党", "共和党", "社会党", "工党"];
    const HISTORICAL_EVENTS = [
      "{year}年发生大火,市中心被毁,后以装饰艺术风格重建。",
      "{year}年由欧洲移民建立为贸易港口。",
      "{year}年举办全国运动会,旅游业大发展。",
      "{year}年代科技产业兴起,经济转型。",
      "{year}年遭遇大洪水,建设新防洪系统。",
      "著名作家{name}于{year}年在此出生。",
      "{year}年宣布碳中和城市目标。",
      "{year}年建成地标建筑{landmark}。",
      "{year}年开通地铁系统,交通改善。",
      "{year}年发现地下温泉,发展康养产业。",
      "{year}年建立大学,成为教育中心。",
      "{year}年举办国际博览会,提升国际知名度。",
      "{year}年建设新机场,航空枢纽地位确立。",
      "{year}年发现矿产资源,工业兴起。",
      "{year}年建设跨海大桥,连接岛屿。",
      "{year}年遭遇地震,重建抗震建筑。",
      "{year}年建立自由贸易区,经济腾飞。",
      "{year}年建设核电站,能源自给。",
      "{year}年举办奥运会,城市国际化。",
      "{year}年建设智慧城市系统,科技领先。"
    ];
    const LANDMARKS = ["中央塔", "遗产大桥", "观景台", "老教堂", "科技中心", "城市公园", "文化广场", "历史博物馆", "歌剧院", "体育中心"];
    const RIVERS = ["清水河", "金沙江", "碧波河", "银溪", "蓝湾河", "翡翠川", "阳光河", "彩虹河", "明珠河", "凤凰河"];
    const MOUNTAINS = ["青山", "白云山", "金山", "银山", "翠峰", "玉龙山", "天山", "昆仑山", "泰山", "华山"];
    const COMPANIES = [
      { name: "新星科技", industry: "人工智能", employees: 15000 },
      { name: "蓝海集团", industry: "新能源", employees: 8500 },
      { name: "云端数据", industry: "云计算", employees: 12000 },
      { name: "绿源生物", industry: "生物科技", employees: 6200 },
      { name: "智联制造", industry: "智能制造", employees: 22000 },
      { name: "未来金融", industry: "金融科技", employees: 9800 },
      { name: "星际航空", industry: "航空航天", employees: 11000 },
      { name: "创意文化", industry: "文化创意", employees: 4500 }
    ];
    const UNIVERSITIES = ["国立大学", "理工大学", "师范大学", "医科大学", "财经大学", "农业大学", "艺术学院", "科技大学"];
    const MUSEUMS = ["历史博物馆", "科技馆", "艺术博物馆", "自然博物馆", "军事博物馆", "民俗博物馆"];
    const THEATERS = ["大剧院", "音乐厅", "话剧团", "电影院", "文化中心"];
    const STADIUMS = ["体育场", "体育馆", "游泳馆", "网球中心", "高尔夫球场"];

    // ======================
    // 工具函数
    // ======================
    function rand(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
    function randName() { return (Math.random() > 0.5 ? rand(NAMES_MALE) : rand(NAMES_FEMALE)) + " " + rand(SURNAMES); }
    function randFloat(min, max) { return parseFloat((Math.random() * (max - min) + min).toFixed(2)); }
    function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
    function weightedRand(weights) {
      let sum = weights.reduce((a, b) => a + b, 0);
      let rand = Math.random() * sum;
      for (let i = 0; i < weights.length; i++) {
        rand -= weights[i];
        if (rand <= 0) return i;
      }
      return weights.length - 1;
    }

    // ======================
    // 生成城市
    // ======================
    function generateCity() {
      const city = {};
      
      // 基础信息
      city.name = rand(CITY_PREFIXES) + rand(CITY_SUFFIXES);
      city.country = rand(COUNTRIES);
      city.latitude = randFloat(20, 50).toFixed(4);
      city.longitude = randFloat(-120, -70).toFixed(4);
      city.location = `${city.latitude}°N, ${city.longitude}°E`;
      city.terrain = rand(TERRAINS);
      city.climateObj = rand(CLIMATES);
      city.climate = city.climateObj.name;
      city.avgTemp = city.climateObj.temp;
      city.weather = rand(WEATHERS);
      city.river = rand(RIVERS);
      city.mountain = rand(MOUNTAINS);
      city.altitude = randInt(0, 3000);
      
      // 人口经济
      city.population = randInt(50000, 15000000);
      city.area = randFloat(200, 5000);
      city.density = (city.population / city.area).toFixed(1);
      city.industry = rand(INDUSTRIES);
      city.gdpTotal = (city.population * randFloat(40000, 120000) / 1e8).toFixed(1);
      city.perCapitaGDP = randInt(40000, 150000);
      city.unemployment = randFloat(2, 12).toFixed(1) + "%";
      city.taxRate = randFloat(8, 30).toFixed(1) + "%";
      city.greenRate = randFloat(20, 70).toFixed(1) + "%";
      
      // 政府官员
      city.mayor = randName();
      city.viceMayor = randName();
      city.party = rand(PARTIES);
      city.term = `${randInt(2020, 2024)} - ${randInt(2024, 2028)}`;
      city.policeChief = randName();
      city.financeChief = randName();
      city.planningChief = randName();
      city.educationChief = randName();
      city.healthChief = randName();
      
      // 人口结构
      city.genderRatio = randInt(92, 108) + ":100";
      city.birthRate = randFloat(6, 18).toFixed(1) + "‰";
      city.deathRate = randFloat(5, 14).toFixed(1) + "‰";
      city.lifeExpectancy = randInt(72, 88) + " 岁";
      city.migration = ["净流入", "净流出", "平衡"][randInt(0, 2)];
      city.education = ["高", "中等", "发展中"][randInt(0, 2)];
      const a = randInt(12, 22), b = randInt(8, 18), c = randInt(30, 50), d = randInt(6, 15), e = 100 - a - b - c - d;
      city.ageDist = `0-14: ${a}%, 15-24: ${b}%, 25-54: ${c}%, 55-64: ${d}%, 65+: ${e}%`;
      
      // 企业生态
      city.listedCompanies = randInt(5, 50);
      city.startups = randInt(20, 200);
      city.headquarters = randInt(3, 30);
      city.totalEmployees = randInt(50000, 2000000);
      city.stockIndex = randInt(8000, 35000);
      city.housingIndex = randInt(100, 500);
      city.employmentRate = (100 - parseFloat(city.unemployment)).toFixed(1) + "%";
      
      // 基础设施
      city.subwayLines = randInt(1, 15);
      city.busStops = randInt(100, 2000);
      city.roadLength = randInt(500, 10000);
      city.bridges = randInt(5, 50);
      city.airport = city.name + "国际机场";
      city.trainStation = city.name + "火车站";
      city.port = city.terrain.includes("沿海") ? city.name + "港" : "无";
      
      // 文化教育
      city.universities = randInt(1, 10);
      city.schools = randInt(50, 500);
      city.libraries = randInt(5, 50);
      city.landmarks = rand(LANDMARKS);
      city.museums = randInt(1, 10);
      city.theaters = randInt(2, 15);
      city.stadiums = randInt(1, 8);
      
      // 环境能源
      city.electricity = randInt(100, 5000);
      city.water = randInt(1000, 50000);
      city.waste = randInt(100, 5000);
      city.internet = randInt(80, 100);
      city.fiveG = randInt(60, 100);
      city.environment = ["优秀", "良好", "一般", "较差"][randInt(0, 3)];
      city.renewable = randInt(10, 80);
      
      // 旅游娱乐
      city.parks = randInt(10, 100);
      city.hotels = randInt(20, 200);
      city.restaurants = randInt(100, 1000);
      city.malls = randInt(5, 30);
      city.entertainment = randInt(20, 100);
      city.hospitals = randInt(5, 50);
      
      // 综合指数
      city.happinessIndex = randInt(60, 95);
      city.safetyIndex = randInt(70, 98);
      city.environmentIndex = randInt(55, 90);
      city.transportIndex = randInt(65, 92);
      city.educationIndex = randInt(60, 90);
      city.healthIndex = randInt(70, 95);
      city.economyIndex = randInt(65, 95);
      city.overallScore = Math.round((
        city.happinessIndex + city.safetyIndex + city.environmentIndex + 
        city.transportIndex + city.educationIndex + city.healthIndex + 
        city.economyIndex
      ) / 7);
      
      // 历史事件
      city.history = [];
      const usedYears = new Set();
      for (let i = 0; i < 20; i++) {
        let event = rand(HISTORICAL_EVENTS);
        let year = randInt(1600, 2023);
        while (usedYears.has(year)) year = randInt(1600, 2023);
        usedYears.add(year);
        event = event.replace("{year}", year);
        event = event.replace("{name}", randName());
        event = event.replace("{landmark}", rand(LANDMARKS));
        city.history.push({ year, event });
      }
      city.history.sort((a, b) => a.year - b.year);
      
      return city;
    }

    // ======================
    // 渲染城市
    // ======================
    function renderCity(city) {
      // 基础信息
      document.getElementById('cityName').textContent = city.name;
      document.getElementById('country').textContent = city.country;
      document.getElementById('location').textContent = city.location;
      document.getElementById('terrain').textContent = city.terrain;
      document.getElementById('climate').textContent = city.climate;
      document.getElementById('avgTemp').textContent = city.avgTemp;
      document.getElementById('weather').textContent = city.weather;
      document.getElementById('river').textContent = city.river;
      document.getElementById('mountain').textContent = city.mountain;
      document.getElementById('altitude').textContent = city.altitude;
      
      // 人口经济
      document.getElementById('population').textContent = city.population.toLocaleString();
      document.getElementById('area').textContent = city.area.toFixed(1);
      document.getElementById('density').textContent = city.density;
      document.getElementById('industry').textContent = city.industry;
      document.getElementById('gdp').textContent = city.gdpTotal;
      document.getElementById('perCapitaGDP').textContent = city.perCapitaGDP.toLocaleString();
      document.getElementById('unemployment').textContent = city.unemployment;
      document.getElementById('taxRate').textContent = city.taxRate;
      document.getElementById('greenRate').textContent = city.greenRate;
      
      // 政府官员
      document.getElementById('mayor').textContent = city.mayor;
      document.getElementById('viceMayor').textContent = city.viceMayor;
      document.getElementById('party').textContent = city.party;
      document.getElementById('term').textContent = city.term;
      document.getElementById('policeChief').textContent = city.policeChief;
      document.getElementById('financeChief').textContent = city.financeChief;
      document.getElementById('planningChief').textContent = city.planningChief;
      document.getElementById('educationChief').textContent = city.educationChief;
      document.getElementById('healthChief').textContent = city.healthChief;
      
      // 人口结构
      document.getElementById('genderRatio').textContent = city.genderRatio;
      document.getElementById('birthRate').textContent = city.birthRate;
      document.getElementById('deathRate').textContent = city.deathRate;
      document.getElementById('lifeExpectancy').textContent = city.lifeExpectancy;
      document.getElementById('migration').textContent = city.migration;
      document.getElementById('education').textContent = city.education;
      document.getElementById('ageDist').textContent = city.ageDist;
      
      // 企业生态
      document.getElementById('listedCompanies').textContent = city.listedCompanies;
      document.getElementById('startups').textContent = city.startups;
      document.getElementById('headquarters').textContent = city.headquarters;
      document.getElementById('totalEmployees').textContent = city.totalEmployees.toLocaleString();
      document.getElementById('stockIndex').textContent = city.stockIndex.toLocaleString();
      document.getElementById('housingIndex').textContent = city.housingIndex;
      document.getElementById('employmentRate').textContent = city.employmentRate;
      
      // 基础设施
      document.getElementById('subwayLines').textContent = city.subwayLines;
      document.getElementById('busStops').textContent = city.busStops;
      document.getElementById('roadLength').textContent = city.roadLength.toLocaleString();
      document.getElementById('bridges').textContent = city.bridges;
      document.getElementById('airport').textContent = city.airport;
      document.getElementById('trainStation').textContent = city.trainStation;
      document.getElementById('port').textContent = city.port;
      
      // 文化教育
      document.getElementById('universities').textContent = city.universities;
      document.getElementById('schools').textContent = city.schools;
      document.getElementById('libraries').textContent = city.libraries;
      document.getElementById('landmarks').textContent = city.landmarks;
      document.getElementById('museums').textContent = city.museums;
      document.getElementById('theaters').textContent = city.theaters;
      document.getElementById('stadiums').textContent = city.stadiums;
      
      // 环境能源
      document.getElementById('electricity').textContent = city.electricity.toLocaleString();
      document.getElementById('water').textContent = city.water.toLocaleString();
      document.getElementById('waste').textContent = city.waste.toLocaleString();
      document.getElementById('internet').textContent = city.internet;
      document.getElementById('fiveG').textContent = city.fiveG;
      document.getElementById('environment').textContent = city.environment;
      document.getElementById('renewable').textContent = city.renewable + "%";
      
      // 旅游娱乐
      document.getElementById('parks').textContent = city.parks;
      document.getElementById('hotels').textContent = city.hotels;
      document.getElementById('restaurants').textContent = city.restaurants;
      document.getElementById('malls').textContent = city.malls;
      document.getElementById('entertainment').textContent = city.entertainment;
      document.getElementById('hospitals').textContent = city.hospitals;
      
      // 综合指数
      document.getElementById('happinessIndex').textContent = city.happinessIndex;
      document.getElementById('safetyIndex').textContent = city.safetyIndex;
      document.getElementById('environmentIndex').textContent = city.environmentIndex;
      document.getElementById('transportIndex').textContent = city.transportIndex;
      document.getElementById('educationIndex').textContent = city.educationIndex;
      document.getElementById('healthIndex').textContent = city.healthIndex;
      document.getElementById('economyIndex').textContent = city.economyIndex;
      document.getElementById('overallScore').textContent = city.overallScore;
      
      // 统计卡片
      document.getElementById('statPopulation').textContent = (city.population / 10000).toFixed(1) + "万";
      document.getElementById('statGDP').textContent = city.gdpTotal;
      document.getElementById('statHappiness').textContent = city.happinessIndex;
      document.getElementById('statSafety').textContent = city.safetyIndex;
      
      // 历史时间轴
      const timeline = document.getElementById('historyTimeline');
      timeline.innerHTML = '';
      city.history.forEach(item => {
        const div = document.createElement('div');
        div.className = 'timeline-item';
        div.innerHTML = `<div class="timeline-year">${item.year}年</div><div>${item.event}</div>`;
        timeline.appendChild(div);
      });
      
      // 图表
      const ctx = document.getElementById('ageChart').getContext('2d');
      if (window.ageChartInstance) window.ageChartInstance.destroy();
      const ageData = city.ageDist.match(/(\d+)%/g).map(x => parseInt(x));
      window.ageChartInstance = new Chart(ctx, {
        type: 'pie',
        data: {
          labels: ['0-14岁', '15-24岁', '25-54岁', '55-64岁', '65+岁'],
          datasets: [{
            data: ageData,
            backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF']
          }]
        },
        options: {
          responsive: true,
          plugins: {
            legend: { position: 'right' }
          }
        }
      });
      
      // 真实地图
      renderMap(city);
      
      window.generatedCity = city;
    }

    // ======================
    // 渲染真实地图(SVG)
    // ======================
    function renderMap(city) {
      const mapContainer = document.getElementById('real-map');
      mapContainer.innerHTML = '';
      
      const svgNS = "http://www.w3.org/2000/svg";
      const svg = document.createElementNS(svgNS, "svg");
      svg.setAttribute("width", "100%");
      svg.setAttribute("height", "100%");
      svg.setAttribute("viewBox", "0 0 800 600");
      
      // 背景地形
      const terrain = city.terrain;
      let bgColor = "#4a9c6d"; // 默认绿色
      if (terrain.includes("沙漠")) bgColor = "#d4b483";
      else if (terrain.includes("山区") || terrain.includes("火山")) bgColor = "#8d6e63";
      else if (terrain.includes("沿海")) bgColor = "#4db6ac";
      else if (terrain.includes("岛屿")) bgColor = "#aed581";
      else if (terrain.includes("冰川")) bgColor = "#bbdefb";
      else if (terrain.includes("森林")) bgColor = "#388e3c";
      
      const bg = document.createElementNS(svgNS, "rect");
      bg.setAttribute("width", "100%");
      bg.setAttribute("height", "100%");
      bg.setAttribute("fill", bgColor);
      svg.appendChild(bg);
      
      // 河流
      if (Math.random() > 0.3) {
        const river = document.createElementNS(svgNS, "path");
        river.setAttribute("d", "M 100 300 Q 400 250 700 300");
        river.setAttribute("stroke", "#2196f3");
        river.setAttribute("stroke-width", "8");
        river.setAttribute("fill", "none");
        river.setAttribute("stroke-linecap", "round");
        svg.appendChild(river);
      }
      
      // 山脉
      if (terrain.includes("山区") || terrain.includes("火山") || terrain.includes("峡谷")) {
        const mountains = document.createElementNS(svgNS, "path");
        mountains.setAttribute("d", "M 50 200 L 150 100 L 250 200 L 350 120 L 450 200 L 550 150 L 650 200 L 750 180");
        mountains.setAttribute("stroke", "#795548");
        mountains.setAttribute("stroke-width", "6");
        mountains.setAttribute("fill", "none");
        svg.appendChild(mountains);
      }
      
      // 城市边界
      const boundary = document.createElementNS(svgNS, "path");
      boundary.setAttribute("d", "M 200 150 L 600 150 L 600 450 L 200 450 Z");
      boundary.setAttribute("fill", "none");
      boundary.setAttribute("stroke", "#ffffff");
      boundary.setAttribute("stroke-width", "3");
      boundary.setAttribute("stroke-dasharray", "10,5");
      svg.appendChild(boundary);
      
      // 行政区划
      const districts = [
        { name: "市中心", x: 350, y: 250, color: "#ff9800" },
        { name: "住宅区", x: 250, y: 200, color: "#9c27b0" },
        { name: "工业区", x: 500, y: 350, color: "#f44336" },
        { name: "商业区", x: 400, y: 300, color: "#00bcd4" },
        { name: "教育区", x: 300, y: 350, color: "#4caf50" },
        { name: "医疗区", x: 450, y: 200, color: "#e91e63" },
        { name: "文化区", x: 350, y: 400, color: "#ffc107" }
      ];
      
      districts.forEach(district => {
        const rect = document.createElementNS(svgNS, "rect");
        rect.setAttribute("x", district.x);
        rect.setAttribute("y", district.y);
        rect.setAttribute("width", "80");
        rect.setAttribute("height", "60");
        rect.setAttribute("fill", district.color);
        rect.setAttribute("stroke", "#ffffff");
        rect.setAttribute("stroke-width", "2");
        rect.setAttribute("rx", "8");
        rect.setAttribute("ry", "8");
        rect.style.cursor = "pointer";
        rect.addEventListener('click', () => {
          playSound(mapClickSound);
          alert(`区域:${district.name}\n详细信息待开发`);
        });
        svg.appendChild(rect);
        
        const text = document.createElementNS(svgNS, "text");
        text.setAttribute("x", district.x + 40);
        text.setAttribute("y", district.y + 35);
        text.setAttribute("text-anchor", "middle");
        text.setAttribute("fill", "white");
        text.setAttribute("font-size", "12");
        text.setAttribute("font-family", "'Noto Sans SC', sans-serif");
        text.textContent = district.name;
        svg.appendChild(text);
      });
      
      // 城市标记
      const cityMarker = document.createElementNS(svgNS, "circle");
      cityMarker.setAttribute("cx", "400");
      cityMarker.setAttribute("cy", "300");
      cityMarker.setAttribute("r", "12");
      cityMarker.setAttribute("fill", "#ffeb3b");
      cityMarker.setAttribute("stroke", "#000");
      cityMarker.setAttribute("stroke-width", "2");
      svg.appendChild(cityMarker);
      
      const cityName = document.createElementNS(svgNS, "text");
      cityName.setAttribute("x", "400");
      cityName.setAttribute("y", "280");
      cityName.setAttribute("text-anchor", "middle");
      cityName.setAttribute("fill", "white");
      cityName.setAttribute("font-size", "16");
      cityName.setAttribute("font-family", "'Noto Sans SC', sans-serif");
      cityName.setAttribute("font-weight", "bold");
      cityName.textContent = city.name;
      svg.appendChild(cityName);
      
      mapContainer.appendChild(svg);
    }

    // ======================
    // 导出功能
    // ======================
    document.getElementById('printBtn').addEventListener('click', () => {
      playSound(clickSound);
      window.print();
    });

    document.getElementById('exportPdfBtn').addEventListener('click', async () => {
      playSound(clickSound);
      const { jsPDF } = window.jspdf;
      const doc = new jsPDF('p', 'mm', 'a4');
      const element = document.body;
      doc.setFont('Helvetica');
      doc.setFontSize(12);
      doc.text("城市报告 - " + (window.generatedCity?.name || "未知城市"), 20, 20);
      await html2canvas(element, { scale: 1.5, useCORS: true }).then(canvas => {
        const imgData = canvas.toDataURL('image/png');
        const imgWidth = 190;
        const imgHeight = canvas.height * imgWidth / canvas.width;
        doc.addImage(imgData, 'PNG', 10, 30, imgWidth, imgHeight);
        doc.save(`城市_${window.generatedCity.name}.pdf`);
      });
    });

    document.getElementById('exportDataBtn').addEventListener('click', () => {
      playSound(clickSound);
      const city = window.generatedCity;
      const csv = [
        ["数据项", "数值"],
        ["城市名称", city.name],
        ["国家", city.country],
        ["人口", city.population],
        ["面积_km2", city.area],
        ["人口密度", city.density],
        ["GDP_亿美元", city.gdpTotal],
        ["人均GDP", city.perCapitaGDP],
        ["失业率", city.unemployment],
        ["出生率_‰", city.birthRate.replace('‰','')],
        ["死亡率_‰", city.deathRate.replace('‰','')],
        ["平均寿命_岁", city.lifeExpectancy.replace(' 岁','')],
        ["性别比", city.genderRatio],
        ["移民率", city.migration],
        ["教育水平", city.education],
        ["0-14岁比例", city.ageDist.match(/0-14: (\d+)%/)?.[1] || 0],
        ["15-24岁比例", city.ageDist.match(/15-24: (\d+)%/)?.[1] || 0],
        ["25-54岁比例", city.ageDist.match(/25-54: (\d+)%/)?.[1] || 0],
        ["55-64岁比例", city.ageDist.match(/55-64: (\d+)%/)?.[1] || 0],
        ["65+岁比例", city.ageDist.match(/65\+: (\d+)%/)?.[1] || 0],
        ["支柱产业", city.industry],
        ["绿化率", city.greenRate],
        ["地铁线路", city.subwayLines],
        ["公交站点", city.busStops],
        ["大学数量", city.universities],
        ["医院数量", city.hospitals],
        ["幸福指数", city.happinessIndex],
        ["安全指数", city.safetyIndex],
        ["综合评分", city.overallScore]
      ].map(row => row.join(',')).join('\n');
      
      const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
      const link = document.createElement("a");
      const url = URL.createObjectURL(blob);
      link.setAttribute("href", url);
      link.setAttribute("download", `城市数据_${city.name}.csv`);
      link.style.visibility = 'hidden';
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    });

    // ======================
    // 生成事件
    // ======================
    document.getElementById('generateBtn').addEventListener('click', function() {
      playSound(generateSound);
      const loading = document.getElementById('loading');
      const output = document.getElementById('output');
      loading.style.display = 'block';
      output.style.display = 'none';
      
      setTimeout(() => {
        const city = generateCity();
        renderCity(city);
        loading.style.display = 'none';
        output.style.display = 'block';
      }, 1000);
    });

    document.getElementById('resetBtn').addEventListener('click', function() {
      playSound(clickSound);
      document.getElementById('output').style.display = 'none';
      document.getElementById('generateBtn').click();
    });

    window.onload = () => {
      document.getElementById('generateBtn').click();
    };
  </script>
</body>
</html>
        
编辑器加载中
预览
控制台