cloudflare上免费部署随机地址生成器

cloudflare上免费部署随机地址生成器

wszx01
2025-07-15 / 0 评论 / 2 阅读

cloudflare免费部署随机地址生成器

github项目
这是一个基于 Cloudflare Workers 的随机地址生成器,可以生成全球多个国家的真实地址、姓名和电话号码。 本项目基于 Real-Address-Generator 做了一些样式和逻辑上的调整和优化。

一、 主要功能 ,爱好者可以自己修改部署
支持多个国家/地区的地址生成
根据不同国家生成符合当地特色的姓名
生成符合各国格式的电话号码
实时地图预览
地址保存和管理
一键复制信息
二、部署到 Cloudflare Workers

1、原项目代码

// 国家坐标数据
const countryCoordinates = {
    "US": [{ lat: 37.7749, lng: -122.4194 }, { lat: 34.0522, lng: -118.2437 }],
    "UK": [{ lat: 51.5074, lng: -0.1278 }, { lat: 53.4808, lng: -2.2426 }],
    "FR": [{ lat: 48.8566, lng: 2.3522 }, { lat: 45.7640, lng: 4.8357 }],
    "DE": [{ lat: 52.5200, lng: 13.4050 }, { lat: 48.1351, lng: 11.5820 }],
    "CN": [{ lat: 39.9042, lng: 116.4074 }, { lat: 31.2304, lng: 121.4737 }],
    "TW": [{ lat: 25.0330, lng: 121.5654 }, { lat: 22.6273, lng: 120.3014 }],
    "HK": [{ lat: 22.3193, lng: 114.1694 },{ lat: 22.3964, lng: 114.1095 }],
    "JP": [{ lat: 35.6895, lng: 139.6917 }, { lat: 34.6937, lng: 135.5023 }],
    "IN": [{ lat: 28.6139, lng: 77.2090 }, { lat: 19.0760, lng: 72.8777 }],
    "AU": [{ lat: -33.8688, lng: 151.2093 }, { lat: -37.8136, lng: 144.9631 }],
    "BR": [{ lat: -23.5505, lng: -46.6333 }, { lat: -22.9068, lng: -43.1729 }],
    "CA": [{ lat: 43.651070, lng: -79.347015 }, { lat: 45.501690, lng: -73.567253 }],
    "RU": [{ lat: 55.7558, lng: 37.6173 }, { lat: 59.9343, lng: 30.3351 }],
    "ZA": [{ lat: -33.9249, lng: 18.4241 }, { lat: -26.2041, lng: 28.0473 }],
    "MX": [{ lat: 19.4326, lng: -99.1332 }, { lat: 20.6597, lng: -103.3496 }],
    "KR": [{ lat: 37.5665, lng: 126.9780 }, { lat: 35.1796, lng: 129.0756 }],
    "IT": [{ lat: 41.9028, lng: 12.4964 }, { lat: 45.4642, lng: 9.1900 }],
    "ES": [{ lat: 40.4168, lng: -3.7038 }, { lat: 41.3851, lng: 2.1734 }],
    "TR": [{ lat: 41.0082, lng: 28.9784 }, { lat: 39.9334, lng: 32.8597 }],
    "SA": [{ lat: 24.7136, lng: 46.6753 }, { lat: 21.3891, lng: 39.8579 }],
    "AR": [{ lat: -34.6037, lng: -58.3816 }, { lat: -31.4201, lng: -64.1888 }],
    "EG": [{ lat: 30.0444, lng: 31.2357 }, { lat: 31.2156, lng: 29.9553 }],
    "NG": [{ lat: 6.5244, lng: 3.3792 }, { lat: 9.0579, lng: 7.4951 }],
    "ID": [{ lat: -6.2088, lng: 106.8456 }, { lat: -7.7956, lng: 110.3695 }]
};

// 姓名数据
const namesByCountry = {
    "CN": {
        first: ["Li", "Wang", "Zhang", "Liu", "Chen", "Yang", "Huang", "Zhao", "Wu", "Zhou", "Xu", "Sun", "Ma", "Zhu", "Hu", "Guo", "He", "Gao", "Lin", "Zheng"],
        last: ["Wei", "Fang", "Na", "Xiuying", "Min", "Jing", "Li", "Qiang", "Lei", "Jun", "Yang", "Yong", "Yan", "Jie", "Tao", "Ming", "Chao", "Xiulan", "Xia", "Ping"]
    },
    "JP": {
        first: ["Sato", "Suzuki", "Takahashi", "Tanaka", "Watanabe", "Ito", "Yamamoto", "Nakamura", "Kobayashi", "Kato"],
        last: ["Shota", "Ren", "Hina", "Yui", "Hiroto", "Sota", "Yota", "Misaki", "Nanami", "Yuto"]
    },
    "KR": {
        first: ["Kim", "Lee", "Park", "Choi", "Jung", "Kang", "Jo", "Yoon", "Jang", "Lim"],
        last: ["Minjun", "Seojun", "Doyun", "Jiho", "Jihun", "Seoyeon", "Seoyun", "Jiwoo", "Seohyun", "Minseo"]
    },
    "TW": {
        first: ["Chen", "Lin", "Huang", "Chang", "Lee", "Wang", "Wu", "Liu", "Tsai", "Yang"],
        last: ["Zhiming", "Jianhong", "Junjie", "Yijun", "Shufen", "Meiling", "Yating", "Jiahao", "Zhihao", "Shuhui"]
    },
    "HK": {
        first: ["Chan", "Lee", "Wong", "Cheung", "Lau", "Wang", "Ng", "Cheng", "Leung", "Ho"],
        last: ["Chiming", "Kayan", "Junjie", "Wingsze", "Kaming", "Meiling", "Kahao", "Winger", "Chihao", "Shukfan"]
    },
    "US": {
        first: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"],
        last: ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Thomas", "Christopher"]
    },
    "UK": {
        first: ["Smith", "Jones", "Williams", "Taylor", "Brown", "Davies", "Evans", "Wilson", "Thomas", "Roberts"],
        last: ["Oliver", "Jack", "Harry", "George", "Noah", "Charlie", "Jacob", "Oscar", "William", "Leo"]
    },
    "FR": {
        first: ["Martin", "Bernard", "Dubois", "Thomas", "Robert", "Richard", "Petit", "Durand", "Leroy", "Moreau"],
        last: ["Lucas", "Louis", "Gabriel", "Arthur", "Jules", "Hugo", "Leo", "Adam", "Raphael", "Paul"]
    },
    "DE": {
        first: ["Mueller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer", "Wagner", "Becker", "Schulz", "Hoffmann"],
        last: ["Ben", "Paul", "Leon", "Noah", "Luis", "Finn", "Felix", "Jonas", "Maximilian", "Henry"]
    },
    "IT": {
        first: ["Rossi", "Ferrari", "Russo", "Bianchi", "Romano", "Gallo", "Costa", "Fontana", "Conti", "Esposito"],
        last: ["Leonardo", "Francesco", "Alessandro", "Lorenzo", "Matteo", "Andrea", "Gabriele", "Marco", "Antonio", "Giuseppe"]
    },
    "ES": {
        first: ["Garcia", "Rodriguez", "Gonzalez", "Fernandez", "Lopez", "Martinez", "Sanchez", "Perez", "Martin", "Gomez"],
        last: ["Antonio", "Jose", "Manuel", "Francisco", "David", "Juan", "Miguel", "Javier", "Rafael", "Carlos"]
    },
    "BR": {
        first: ["Silva", "Santos", "Oliveira", "Souza", "Rodrigues", "Ferreira", "Alves", "Pereira", "Lima", "Gomes"],
        last: ["Miguel", "Arthur", "Heitor", "Pedro", "Davi", "Gabriel", "Bernardo", "Lucas", "Matheus", "Rafael"]
    },
    "RU": {
        first: ["Ivanov", "Smirnov", "Kuznetsov", "Popov", "Vasiliev", "Petrov", "Sokolov", "Mikhailov", "Fedorov", "Morozov"],
        last: ["Alexander", "Dmitry", "Maxim", "Ivan", "Andrey", "Mikhail", "Artem", "Daniel", "Roman", "Sergey"]
    },
    "IN": {
        first: ["Kumar", "Singh", "Sharma", "Patel", "Gupta", "Shah", "Verma", "Rao", "Reddy", "Joshi"],
        last: ["Aarav", "Vihaan", "Vivaan", "Aditya", "Arjun", "Reyansh", "Ayaan", "Sai", "Krishna", "Ishaan"]
    },
    "AU": {
        first: ["Smith", "Jones", "Williams", "Brown", "Wilson", "Taylor", "Johnson", "White", "Anderson", "Thompson"],
        last: ["Oliver", "William", "Jack", "Noah", "Thomas", "James", "Lucas", "Henry", "Ethan", "Alexander"]
    },
    "CA": {
        first: ["Smith", "Brown", "Tremblay", "Martin", "Roy", "Wilson", "MacDonald", "Taylor", "Campbell", "Anderson"],
        last: ["Liam", "Noah", "Oliver", "William", "James", "Benjamin", "Lucas", "Henry", "Theodore", "Jack"]
    },
    "MX": {
        first: ["Garcia", "Rodriguez", "Martinez", "Lopez", "Gonzalez", "Perez", "Sanchez", "Ramirez", "Torres", "Flores"],
        last: ["Santiago", "Mateo", "Sebastian", "Leonardo", "Diego", "Daniel", "Gabriel", "Adrian", "David", "Alexander"]
    },
    "TR": {
        first: ["Yilmaz", "Kaya", "Demir", "Sahin", "Celik", "Yildiz", "Erdogan", "Ozturk", "Aydin", "Ozdemir"],
        last: ["Yusuf", "Eymen", "Ömer", "Mustafa", "Ali", "Mehmet", "Ahmet", "Emir", "Hamza", "Ibrahim"]
    },
    "SA": {
        first: ["Al-Saud", "Al-Sheikh", "Al-Rashid", "Al-Qahtani", "Al-Ghamdi", "Al-Zahrani", "Al-Dossari", "Al-Shammari", "Al-Otaibi", "Al-Harbi"],
        last: ["Mohammed", "Abdullah", "Ahmed", "Ali", "Omar", "Ibrahim", "Khalid", "Hassan", "Fahad", "Abdul"]
    },
    "AR": {
        first: ["Gonzalez", "Rodriguez", "Garcia", "Fernandez", "Lopez", "Martinez", "Perez", "Romero", "Sanchez", "Diaz"],
        last: ["Mateo", "Thiago", "Benjamin", "Valentino", "Santiago", "Juan", "Lucas", "Martin", "Nicolas", "Joaquin"]
    },
    "EG": {
        first: ["Mohamed", "Ahmed", "Mahmoud", "Ibrahim", "Ali", "Hassan", "Hussein", "Mostafa", "Kamal", "Samir"],
        last: ["Omar", "Youssef", "Adam", "Malik", "Zain", "Hamza", "Kareem", "Hassan", "Ali", "Ibrahim"]
    },
    "NG": {
        first: ["Okafor", "Adebayo", "Okonkwo", "Eze", "Oluwaseun", "Adegoke", "Afolabi", "Ogunleye", "Adeniyi", "Adesina"],
        last: ["Oluwadamilare", "Oluwatobiloba", "Ayomide", "Temitope", "Oluwaseun", "Adebayo", "Chibuike", "Chisom", "Chidi", "Obinna"]
    },
    "ID": {
        first: ["Wijaya", "Kusuma", "Suryanto", "Halim", "Santoso", "Tanaka", "Wibowo", "Susanto", "Hidayat", "Putra"],
        last: ["Muhammad", "Ahmad", "Abdul", "Aditya", "Budi", "Dimas", "Eko", "Fajar", "Gading", "Hadi"]
    },
    "ZA": {
        first: ["Nkosi", "Van der Merwe", "Botha", "Mkhize", "Khumalo", "Pretorius", "Venter", "Ndlovu", "Fourie", "Nel"],
        last: ["Bandile", "Themba", "Sipho", "Thabo", "Jabu", "Mandla", "Blessing", "Gift", "Lucky", "Precious"]
    }
};

// 电话号码格式配置
const phoneFormats = {
    "US": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[201, 989]]
    },
    "CN": {
        format: "+86 1XX-XXXX-XXXX",
        mobilePrefix: ["30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "50", "51", "52", "53", "55", "56", "57", "58", "59", "66", "70", "71", "72", "73", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89"]
    },
    "JP": {
        format: "+81 XX-XXXX-XXXX",
        mobilePrefix: ["70", "80", "90"]
    },
    "KR": {
        format: "+82 10-XXXX-XXXX"
    },
    "UK": {
        format: "+44 7XXX XXXXXX",
        mobilePrefix: ["7"]
    },
    "FR": {
        format: "+33 6 XX XX XX XX",
        mobilePrefix: ["6", "7"]
    },
    "DE": {
        format: "+49 15X XXXXXXXX",
        mobilePrefix: ["15", "16", "17"]
    },
    "TW": {
        format: "+886 9XX-XXX-XXX"
    },
    "HK": {
        format: "+852 XXXX XXXX",
        mobilePrefix: ["5", "6", "9"]
    },
    "AU": {
        format: "+61 4XX XXX XXX",
        mobilePrefix: ["4"]
    },
    "CA": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[204, 989]]
    },
    "MX": {
        format: "+52 1XX XXX XXXX"
    },
    "TR": {
        format: "+90 5XX XXX XXXX",
        mobilePrefix: ["5"]
    },
    "SA": {
        format: "+966 5XX XXX XXXX",
        mobilePrefix: ["5"]
    },
    "AR": {
        format: "+54 9XX XXXX-XXXX"
    },
    "EG": {
        format: "+20 1XX XXX XXXX",
        mobilePrefix: ["1"]
    },
    "NG": {
        format: "+234 8XX XXX XXXX",
        mobilePrefix: ["7", "8", "9"]
    },
    "ID": {
        format: "+62 8XX-XXXX-XXXX",
        mobilePrefix: ["8"]
    },
    "ZA": {
        format: "+27 8X XXX XXXX",
        mobilePrefix: ["6", "7", "8"]
    }
};

// 工具函数
function getRandomLocation(country) {
    const coordsArray = countryCoordinates[country];
    const randomCity = coordsArray[Math.floor(Math.random() * coordsArray.length)];
    const lat = randomCity.lat + (Math.random() - 0.5) * 0.1;
    const lng = randomCity.lng + (Math.random() - 0.5) * 0.1;
    return { lat, lng };
}

function getRandomName(country) {
    if (!namesByCountry[country]) {
        return null;
    }
    const names = namesByCountry[country];
    const firstName = names.first[Math.floor(Math.random() * names.first.length)];
    const lastName = names.last[Math.floor(Math.random() * names.last.length)];
    return `${firstName} ${lastName}`;
}

function generateAreaCode(ranges) {
    const range = ranges[Math.floor(Math.random() * ranges.length)];
    const [min, max] = range;
    return Math.floor(min + Math.random() * (max - min + 1));
}

function getRandomPhoneNumber(country) {
    const format = phoneFormats[country] || phoneFormats["US"];
    let phone = format.format;

    if (format.areaCodeRanges) {
        const areaCode = generateAreaCode(format.areaCodeRanges);
        phone = phone.replace("XXX", areaCode);
        phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
    } else if (format.mobilePrefix) {
        const prefix = format.mobilePrefix[Math.floor(Math.random() * format.mobilePrefix.length)];
        // 先替换前缀
        if (prefix.length === 2) {
            phone = phone.replace(/XX/, prefix);
        } else {
            phone = phone.replace(/X/, prefix);
        }
        // 然后替换剩余的X
        phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
    } else {
        phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
    }
    return phone;
}

function isValidAddress(data) {
    return data && 
           data.address && 
           data.address.house_number && 
           data.address.road && 
           (data.address.city || data.address.town);
}

// 处理CORS请求的headers
const corsHeaders = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type',
};

// HTML 模板
const htmlContent = `<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>随机地址生成器</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    animation: {
                        'gradient': 'gradient 8s linear infinite',
                    },
                    keyframes: {
                        gradient: {
                            '0%, 100%': {
                                'background-size': '200% 200%',
                                'background-position': 'left center'
                            },
                            '50%': {
                                'background-size': '200% 200%',
                                'background-position': 'right center'
                            }
                        }
                    }
                }
            }
        }
    </script>
</head>
<body class="bg-gradient-to-br from-blue-50 via-white to-blue-50 text-gray-800 min-h-screen font-['Noto_Sans_SC']">
    <!-- 头部 -->
    <header class="bg-gradient-to-r from-blue-500 via-sky-500 to-blue-500 animate-gradient w-full p-6 shadow-lg">
        <div class="max-w-4xl mx-auto flex items-center justify-between">
            <div class="flex items-center gap-3">
                <img src="https://img.freepik.com/premium-vector/minimal-location-map-icon-logo-symbol-vector-design-transparent_965979-613.jpg?w=2000" alt="Logo" class="w-12 h-12 transform hover:scale-105 transition-transform">
                <h1 class="text-2xl font-bold text-white">随机地址生成器</h1>
            </div>
            <a href="https://github.com/jiangnan1224/AddressGenerator/" 
               target="_blank" 
               class="flex items-center gap-2 text-white hover:text-gray-200 transition-colors">
                <svg viewBox="0 0 16 16" class="w-6 h-6 fill-current" aria-hidden="true">
                    <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
                </svg>
                <span class="font-medium">GitHub</span>
            </a>
        </div>
    </header>

    <!-- 主要内容 -->
    <main class="container mx-auto px-4 py-8 max-w-5xl">
        <!-- 加载动画 -->
        <div id="loading" class="hidden fixed inset-0 bg-white bg-opacity-75 backdrop-blur-sm flex items-center justify-center z-50">
            <div class="bg-white rounded-2xl p-8 flex flex-col items-center shadow-2xl">
                <div class="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500 mb-4"></div>
                <div class="text-gray-800 text-lg font-medium">正在加载...</div>
            </div>
        </div>

        <div id="copied" class="hidden fixed top-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg z-40 transform transition-transform duration-300">
            已复制到剪贴板!
        </div>

        <!-- 国家选择 -->
        <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-200 mb-8">
            <div class="flex flex-col sm:flex-row items-start sm:items-center gap-4">
                <div class="w-full sm:w-auto flex flex-col sm:flex-row items-start sm:items-center gap-2 flex-grow">
                    <label for="country" class="text-blue-600 font-bold whitespace-nowrap">选择国家/地区:</label>
                    <select id="country" 
                            onchange="changeCountry(this.value)" 
                            class="w-full bg-gray-50 border border-gray-200 rounded-xl p-3 text-gray-800 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all">
                    </select>
                </div>
                <button onclick="generateNewAddress(document.getElementById('country').value)" 
                        class="w-full sm:w-auto bg-gradient-to-r from-blue-500 to-sky-500 hover:from-blue-600 hover:to-sky-600 text-white font-bold py-3 px-6 rounded-xl transition-all transform hover:scale-105 hover:shadow-lg focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
                    获取新地址
                </button>
            </div>
        </div>

        <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
            <!-- 左侧面板 -->
            <div class="space-y-6">
                <!-- 信息卡片 -->
                <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-200">
                    <h2 class="text-xl font-bold mb-6 text-blue-600">个人信息</h2>
                    <div class="space-y-4">
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">姓名:</strong><span id="name" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">性别:</strong><span id="gender" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">电话:</strong><span id="phone" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">地址:</strong><span id="address" class="ml-2"></span>
                        </div>
                    </div>
                </div>

                <!-- 保存按钮 -->
                <button onclick="saveAddress()" 
                        class="w-full bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600 hover:to-emerald-600 text-white font-bold py-3 px-6 rounded-xl transition-all transform hover:scale-105 hover:shadow-lg focus:ring-2 focus:ring-green-500 focus:ring-opacity-50">
                    保存地址
                </button>
            </div>

            <!-- 右侧面板 -->
            <div class="space-y-6">
                <!-- 地图 -->
                <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-200">
                    <h2 class="text-xl font-bold mb-6 text-blue-600">地图预览</h2>
                    <iframe id="map" class="w-full h-[400px] rounded-xl border border-gray-200"></iframe>
                </div>
            </div>
        </div>

        <!-- 已保存的地址表格 -->
        <div class="mt-8 bg-white rounded-2xl shadow-xl p-6 border border-gray-200">
            <h2 class="text-xl font-bold mb-6 text-blue-600">已保存的地址</h2>
            <div class="overflow-x-auto">
                <table class="w-full border-collapse" id="savedAddressesTable">
                    <thead>
                        <tr class="bg-gradient-to-r from-blue-500 to-sky-500 text-white">
                            <th class="p-4 text-left rounded-tl-lg">姓名</th>
                            <th class="p-4 text-left">性别</th>
                            <th class="p-4 text-left">电话号码</th>
                            <th class="p-4 text-left">地址</th>
                            <th class="p-4 text-left">备注</th>
                            <th class="p-4 text-left rounded-tr-lg">操作</th>
                        </tr>
                    </thead>
                    <tbody></tbody>
                </table>
            </div>
        </div>
    </main>

    <!-- 页脚 -->
    <footer class="text-center py-8 text-gray-600 text-sm mt-8 bg-gray-50 border-t border-gray-200">
        <p class="max-w-4xl mx-auto px-4">
            All right reserved
            <a href="https://github.com/jiangnan1224/AddressGenerator/" 
               target="_blank" 
               class="inline-flex items-center hover:text-blue-600 transition-colors">
                <img src="https://pic.imgdb.cn/item/66e7ab36d9c307b7e9cefd24.png" alt="GitHub" class="w-5 h-5 ml-1">
            </a>
        </p>
    </footer>

    <script>
        // 国家数据
        const countries = [
            { name: "美国", code: "US" },
            { name: "英国", code: "UK" },
            { name: "法国", code: "FR" },
            { name: "德国", code: "DE" },
            { name: "中国", code: "CN" },
            { name: "中国台湾", code: "TW" },
            { name: "中国香港", code: "HK" },
            { name: "日本", code: "JP" },
            { name: "印度", code: "IN" },
            { name: "澳大利亚", code: "AU" },
            { name: "巴西", code: "BR" },
            { name: "加拿大", code: "CA" },
            { name: "俄罗斯", code: "RU" },
            { name: "南非", code: "ZA" },
            { name: "墨西哥", code: "MX" },
            { name: "韩国", code: "KR" },
            { name: "意大利", code: "IT" },
            { name: "西班牙", code: "ES" },
            { name: "土耳其", code: "TR" },
            { name: "沙特阿拉伯", code: "SA" },
            { name: "阿根廷", code: "AR" },
            { name: "埃及", code: "EG" },
            { name: "尼日利亚", code: "NG" },
            { name: "印度尼西亚", code: "ID" }
        ];

        // 初始化国家选择下拉框
        function initCountrySelect() {
            const select = document.getElementById('country');
            countries.forEach(country => {
                const option = document.createElement('option');
                option.value = country.code;
                option.textContent = country.name;
                select.appendChild(option);
            });
        }

        // 复制到剪贴板
        function copyToClipboard(text) {
            navigator.clipboard.writeText(text).then(() => {
                const copied = document.getElementById('copied');
                copied.classList.remove('hidden');
                copied.classList.add('translate-y-0');
                copied.classList.remove('translate-y-[-100%]');
                
                setTimeout(() => {
                    copied.classList.add('translate-y-[-100%]');
                    copied.classList.remove('translate-y-0');
                    setTimeout(() => {
                        copied.classList.add('hidden');
                    }, 300);
                }, 2000);
            });
        }

        // 显示/隐藏加载动画
        function toggleLoading(show) {
            const loading = document.getElementById('loading');
            if (show) {
                loading.classList.remove('hidden');
            } else {
                loading.classList.add('hidden');
            }
        }

        // 更改国家
        function changeCountry(country) {
            toggleLoading(true);
            generateNewAddress(country);
        }

        // 保存地址
        function saveAddress() {
            const note = prompt('请输入备注(可以留空)') || '';
            const savedAddresses = JSON.parse(localStorage.getItem('savedAddresses') || '[]');
            const newEntry = {
                note: note,
                name: document.getElementById('name').textContent,
                gender: document.getElementById('gender').textContent,
                phone: document.getElementById('phone').textContent,
                address: document.getElementById('address').textContent
            };
            savedAddresses.push(newEntry);
            localStorage.setItem('savedAddresses', JSON.stringify(savedAddresses));
            renderSavedAddresses();
        }

        // 渲染保存的地址
        function renderSavedAddresses() {
            const savedAddresses = JSON.parse(localStorage.getItem('savedAddresses') || '[]');
            const tbody = document.querySelector('#savedAddressesTable tbody');
            tbody.innerHTML = '';
            
            savedAddresses.forEach((entry, index) => {
                const row = tbody.insertRow();
                row.className = 'border-t border-gray-200 hover:bg-gray-50 transition-colors';
                
                const cells = ['name', 'gender', 'phone', 'address', 'note', ''];
                cells.forEach((cell, i) => {
                    const td = row.insertCell();
                    td.className = 'p-4';
                    
                    if (i === cells.length - 1) {
                        const deleteBtn = document.createElement('button');
                        deleteBtn.textContent = '删除';
                        deleteBtn.className = 'bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg transition-all transform hover:scale-105 focus:ring-2 focus:ring-red-500 focus:ring-opacity-50';
                        deleteBtn.onclick = () => {
                            if (confirm('确定要删除这条记录吗?')) {
                                savedAddresses.splice(index, 1);
                                localStorage.setItem('savedAddresses', JSON.stringify(savedAddresses));
                                renderSavedAddresses();
                            }
                        };
                        td.appendChild(deleteBtn);
                    } else {
                        td.textContent = entry[cell];
                    }
                });
            });
        }

        // 生成新地址
        async function generateNewAddress(country) {
            if (!country) {
                country = document.getElementById('country').value;
            }
            toggleLoading(true);
            
            try {
                const response = await fetch(\`\${window.location.href}api?country=\${country}\`);
                const data = await response.json();
                
                if (data.error) {
                    alert(data.error);
                    return;
                }
                
                document.getElementById('name').textContent = data.name;
                document.getElementById('gender').textContent = data.gender;
                document.getElementById('phone').textContent = data.phone;
                document.getElementById('address').textContent = data.address;
                
                // 更新地图
                document.getElementById('map').src = \`https://www.google.com/maps?q=\${encodeURIComponent(data.address)}&output=embed\`;
            } catch (error) {
                console.error('Error fetching address:', error);
                alert('获取地址时发生错误,请重试');
            } finally {
                toggleLoading(false);
            }
        }

        // 页面加载时初始化
        window.onload = function() {
            initCountrySelect();
            generateNewAddress();
            renderSavedAddresses();
        };
    </script>
</body>
</html>`;

async function handleRequest(request) {
    const url = new URL(request.url);
    const path = url.pathname;

    // 处理 API 请求
    if (path === '/api') {
        // 原有的 API 处理逻辑
        return handleApiRequest(request);
    }

    // 处理根路径请求,返回 HTML 页面
    return new Response(htmlContent, {
        headers: {
            'Content-Type': 'text/html;charset=UTF-8',
            ...corsHeaders
        }
    });
}

// 将原有的处理逻辑移到单独的函数中
async function handleApiRequest(request) {
    if (request.method === 'OPTIONS') {
        return new Response(null, {
            headers: corsHeaders
        });
    }

    if (request.method !== 'GET') {
        return new Response('Method not allowed', { status: 405 });
    }

    const url = new URL(request.url);
    const country = url.searchParams.get('country') || 'US';

    if (!countryCoordinates[country]) {
        return new Response(JSON.stringify({
            error: 'Invalid country code'
        }), {
            status: 400,
            headers: {
                'Content-Type': 'application/json',
                ...corsHeaders
            }
        });
    }

    let attempts = 0;
    const maxAttempts = 20;
    
    while (attempts < maxAttempts) {
        try {
            const location = getRandomLocation(country);
            const apiUrl = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${location.lat}&lon=${location.lng}&zoom=18&addressdetails=1`;
            
            const response = await fetch(apiUrl, {
                headers: {
                    'User-Agent': 'Cloudflare Worker Random Address Generator'
                }
            });
            
            const data = await response.json();
            
            if (isValidAddress(data)) {
                let city = data.address.city || data.address.town || '';
                city = city.split(';')[0].trim();
                
                const address = `${data.address.house_number} ${data.address.road}, ${city}, ${data.address.postcode || ''}, ${country}`.replace(/\s+/g, ' ').trim();
                
                const name = getRandomName(country);
                const gender = Math.random() > 0.5 ? 'Male' : 'Female';
                const phone = getRandomPhoneNumber(country);
                
                const result = {
                    name,
                    gender,
                    phone,
                    address,
                    coordinates: {
                        lat: location.lat,
                        lng: location.lng
                    }
                };
                
                return new Response(JSON.stringify(result), {
                    headers: {
                        'Content-Type': 'application/json',
                        ...corsHeaders
                    }
                });
            }
            
            attempts++;
            if (attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 100));
            }
        } catch (error) {
            attempts++;
            if (attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 100));
            }
        }
    }
    
    return new Response(JSON.stringify({
        error: 'Failed to generate valid address after multiple attempts'
    }), {
        status: 500,
        headers: {
            'Content-Type': 'application/json',
            ...corsHeaders
        }
    });
}

// 注册事件监听器
addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request));
}); 

2、修改后的代码,各有所好,当然也可以找一下其它人修改的代码
2025年07月16日更新,界面调整,添加多个国家和地区

// 国家坐标数据
const countryCoordinates = {
    "US": [{ lat: 37.7749, lng: -122.4194 }, { lat: 34.0522, lng: -118.2437 }, { lat: 32.7767, lng: -96.7970 }, { lat: 41.8781, lng: -87.6298 }, { lat: 29.7604, lng: -95.3698 }, { lat: 33.4484, lng: -112.0740 }, { lat: 39.9526, lng: -75.1652 }, { lat: 29.4241, lng: -98.4936 }, { lat: 32.7157, lng: -117.1611 }, { lat: 47.6062, lng: -122.3321 }],
    "NY US": [{ lat: 40.7128, lng: -74.0060 }],
    "California US": [{ lat: 34.0522, lng: -118.2437 }],
    "Texas US": [{ lat: 32.7767, lng: -96.7970 }],
    "CA US": [{ lat: 37.3382, lng: -121.8863 }],
    "Illinois US": [{ lat: 41.8781, lng: -87.6298 }],

    // 添加的城市坐标:休斯敦、菲尼克斯、费城、圣安东尼奥、圣地亚哥、西雅图
    "TX US": [{ lat: 29.7604, lng: -95.3698 }],
    "Arizona US": [{ lat: 33.4484, lng: -112.0740 }],
    "Pennsylvania US": [{ lat: 39.9526, lng: -75.1652 }],
    "Tx US": [{ lat: 29.4241, lng: -98.4936 }],
    "Ca US": [{ lat: 32.7157, lng: -117.1611 }],
    "Washington US": [{ lat: 47.6062, lng: -122.3321 }],

    "UK": [{ lat: 51.5074, lng: -0.1278 }, { lat: 53.4808, lng: -2.2426 }],
    "FR": [{ lat: 48.8566, lng: 2.3522 }, { lat: 45.7640, lng: 4.8357 }, { lat: 43.2965, lng: 5.3698 }],
    "DE": [{ lat: 52.5200, lng: 13.4050 }, { lat: 48.1351, lng: 11.5820 }],
    "CN": [{ lat: 39.9042, lng: 116.4074 }, { lat: 31.2304, lng: 121.4737 }],
    "TW": [{ lat: 25.0330, lng: 121.5654 }, { lat: 22.6273, lng: 120.3014 }],
    "HK": [{ lat: 22.3193, lng: 114.1694 },{ lat: 22.3964, lng: 114.1095 }],
    "JP": [{ lat: 35.6895, lng: 139.6917 }, { lat: 34.6937, lng: 135.5023 }],
    "IN": [{ lat: 28.6139, lng: 77.2090 }, { lat: 19.0760, lng: 72.8777 }],
    "AU": [{ lat: -33.8688, lng: 151.2093 }, { lat: -37.8136, lng: 144.9631 }],
    "BR": [{ lat: -23.5505, lng: -46.6333 }, { lat: -22.9068, lng: -43.1729 }],
    "CA": [{ lat: 43.651070, lng: -79.347015 }, { lat: 45.501690, lng: -73.567253 }],
    "RU": [{ lat: 55.7558, lng: 37.6173 }, { lat: 59.9343, lng: 30.3351 }],
    "ZA": [{ lat: -33.9249, lng: 18.4241 }, { lat: -26.2041, lng: 28.0473 }],
    "MX": [{ lat: 19.4326, lng: -99.1332 }, { lat: 20.6597, lng: -103.3496 }],
    "KR": [{ lat: 37.5665, lng: 126.9780 }, { lat: 35.1796, lng: 129.0756 }],
    "IT": [{ lat: 41.9028, lng: 12.4964 }, { lat: 45.4642, lng: 9.1900 }],
    "ES": [{ lat: 40.4168, lng: -3.7038 }, { lat: 41.3851, lng: 2.1734 }],
    "TR": [{ lat: 41.0082, lng: 28.9784 }, { lat: 39.9334, lng: 32.8597 }],
    "SA": [{ lat: 24.7136, lng: 46.6753 }, { lat: 21.3891, lng: 39.8579 }],
    "AR": [{ lat: -34.6037, lng: -58.3816 }, { lat: -31.4201, lng: -64.1888 }],
    "EG": [{ lat: 30.0444, lng: 31.2357 }, { lat: 31.2156, lng: 29.9553 }],
    "NG": [{ lat: 6.5244, lng: 3.3792 }, { lat: 9.0579, lng: 7.4951 }],
    "ID": [{ lat: -6.2088, lng: 106.8456 }, { lat: -7.7956, lng: 110.3695 }],
    "VN": [{ lat: 21.0285, lng: 105.8048 }, { lat: 10.7626, lng: 106.6602 }], // 添加越南坐标数据

    // 新增城市坐标
    "Berlin DE": [{ lat: 52.5200, lng: 13.4050 }],
    "Bavaria DE": [{ lat: 48.1351, lng: 11.5820 }],
    "Île-de-France FR": [{ lat: 48.8566, lng: 2.3522 }],
    "Provence-Alpes-Côte d'Azur FR": [{ lat: 43.2965, lng: 5.3698 }],
    "London UK": [{ lat: 51.5074, lng: -0.1278 }],
    "Seoul KR": [{ lat: 37.5665, lng: 126.9780 }],
    "Ontario CA": [{ lat: 43.651070, lng: -79.347015 }],
    "Comunidad de Madrid ES": [{ lat: 40.4168, lng: -3.7038 }],
    "Cataluña ES": [{ lat: 41.3851, lng: 2.1734 }],
    "Tokyo JP": [{ lat: 35.6895, lng: 139.6917 }],
    "Osaka JP": [{ lat: 34.6937, lng: 135.5023 }],
};

// 姓名数据
const namesByCountry = {
    "US": {
        first: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"],
        last: ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Thomas", "Christopher"]
    },
    "CN": {
        first: ["李", "王", "张", "刘", "陈", "杨", "黄", "赵", "吴", "周", "孙", "苏", "马", "朱", "候", "郭", "黑", "高", "林", "钱", "冯", "卫", "蒋", "许", "吕", "施", "曹", "严", "魏", "陶", "方", "雷"],
        last: ["力", "斌", "那", "真", "杰", "京", "丽", "晴", "来", "东", "勇", "仕", "云", "洁", "山", "伟", "明", "亮", "刚", "飞", "英", "兴", "浩", "曙光", "平", "志远", "志珍", "第一", "建国", "玉仙", "现红", "瑞山", "一山", "新丽", "思思", "青凤", "秀美"]
    },
    "JP": {
        first: ["Sato", "Suzuki", "Takahashi", "Tanaka", "Watanabe", "Ito", "Yamamoto", "Nakamura", "Kobayashi", "Kato"],
        last: ["Shota", "Ren", "Hina", "Yui", "Hiroto", "Sota", "Yota", "Misaki", "Nanami", "Yuto"]
    },
    "KR": {
        first: ["Kim", "Lee", "Park", "Choi", "Jung", "Kang", "Jo", "Yoon", "Jang", "Lim"],
        last: ["Minjun", "Seojun", "Doyun", "Jiho", "Jihun", "Seoyeon", "Seoyun", "Jiwoo", "Seohyun", "Minseo"]
    },
    "TW": {
        first: ["Chen", "Lin", "Huang", "Chang", "Lee", "Wang", "Wu", "Liu", "Tsai", "Yang"],
        last: ["Zhiming", "Jianhong", "Junjie", "Yijun", "Shufen", "Meiling", "Yating", "Jiahao", "Zhihao", "Shuhui"]
    },
    "HK": {
        first: ["Chan", "Lee", "Wong", "Cheung", "Lau", "Wang", "Ng", "Cheng", "Leung", "Ho"],
        last: ["Chiming", "Kayan", "Junjie", "Wingsze", "Kaming", "Meiling", "Kahao", "Winger", "Chihao", "Shukfan"]
    },
    "UK": {
        first: ["Smith", "Jones", "Williams", "Taylor", "Brown", "Davies", "Evans", "Wilson", "Thomas", "Roberts"],
        last: ["Oliver", "Jack", "Harry", "George", "Noah", "Charlie", "Jacob", "Oscar", "William", "Leo"]
    },
    "FR": {
        first: ["Martin", "Bernard", "Dubois", "Thomas", "Robert", "Richard", "Petit", "Durand", "Leroy", "Moreau"],
        last: ["Lucas", "Louis", "Gabriel", "Arthur", "Jules", "Hugo", "Leo", "Adam", "Raphael", "Paul"]
    },
    "DE": {
        first: ["Mueller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer", "Wagner", "Becker", "Schulz", "Hoffmann"],
        last: ["Ben", "Paul", "Leon", "Noah", "Luis", "Finn", "Felix", "Jonas", "Maximilian", "Henry"]
    },
    "IT": {
        first: ["Rossi", "Ferrari", "Russo", "Bianchi", "Romano", "Gallo", "Costa", "Fontana", "Conti", "Esposito"],
        last: ["Leonardo", "Francesco", "Alessandro", "Lorenzo", "Matteo", "Andrea", "Gabriele", "Marco", "Antonio", "Giuseppe"]
    },
    "ES": {
        first: ["Garcia", "Rodriguez", "Gonzalez", "Fernandez", "Lopez", "Martinez", "Sanchez", "Perez", "Martin", "Gomez"],
        last: ["Antonio", "Jose", "Manuel", "Francisco", "David", "Juan", "Miguel", "Javier", "Rafael", "Carlos"]
    },
    "BR": {
        first: ["Silva", "Santos", "Oliveira", "Souza", "Rodrigues", "Ferreira", "Alves", "Pereira", "Lima", "Gomes"],
        last: ["Miguel", "Arthur", "Heitor", "Pedro", "Davi", "Gabriel", "Bernardo", "Lucas", "Matheus", "Rafael"]
    },
    "RU": {
        first: ["Ivanov", "Smirnov", "Kuznetsov", "Popov", "Vasiliev", "Petrov", "Sokolov", "Mikhailov", "Fedorov", "Morozov"],
        last: ["Alexander", "Dmitry", "Maxim", "Ivan", "Andrey", "Mikhail", "Artem", "Daniel", "Roman", "Sergey"]
    },
    "IN": {
        first: ["Kumar", "Singh", "Sharma", "Patel", "Gupta", "Shah", "Verma", "Rao", "Reddy", "Joshi"],
        last: ["Aarav", "Vihaan", "Vivaan", "Aditya", "Arjun", "Reyansh", "Ayaan", "Sai", "Krishna", "Ishaan"]
    },
    "AU": {
        first: ["Smith", "Jones", "Williams", "Brown", "Wilson", "Taylor", "Johnson", "White", "Anderson", "Thompson"],
        last: ["Oliver", "William", "Jack", "Noah", "Thomas", "James", "Lucas", "Henry", "Ethan", "Alexander"]
    },
    "CA": {
        first: ["Smith", "Brown", "Tremblay", "Martin", "Roy", "Wilson", "MacDonald", "Taylor", "Campbell", "Anderson"],
        last: ["Liam", "Noah", "Oliver", "William", "James", "Benjamin", "Lucas", "Henry", "Theodore", "Jack"]
    },
    "MX": {
        first: ["Garcia", "Rodriguez", "Martinez", "Lopez", "Gonzalez", "Perez", "Sanchez", "Ramirez", "Torres", "Flores"],
        last: ["Santiago", "Mateo", "Sebastian", "Leonardo", "Diego", "Daniel", "Gabriel", "Adrian", "David", "Alexander"]
    },
    "TR": {
        first: ["Yilmaz", "Kaya", "Demir", "Sahin", "Celik", "Yildiz", "Erdogan", "Ozturk", "Aydin", "Ozdemir"],
        last: ["Yusuf", "Eymen", "Ömer", "Mustafa", "Ali", "Mehmet", "Ahmet", "Emir", "Hamza", "Ibrahim"]
    },
    "SA": {
        first: ["Al-Saud", "Al-Sheikh", "Al-Rashid", "Al-Qahtani", "Al-Ghamdi", "Al-Zahrani", "Al-Dossari", "Al-Shammari", "Al-Otaibi", "Al-Harbi"],
        last: ["Mohammed", "Abdullah", "Ahmed", "Ali", "Omar", "Ibrahim", "Khalid", "Hassan", "Fahad", "Abdul"]
    },
    "AR": {
        first: ["Gonzalez", "Rodriguez", "Garcia", "Fernandez", "Lopez", "Martinez", "Perez", "Romero", "Sanchez", "Diaz"],
        last: ["Mateo", "Thiago", "Benjamin", "Valentino", "Santiago", "Juan", "Lucas", "Martin", "Nicolas", "Joaquin"]
    },
    "EG": {
        first: ["Mohamed", "Ahmed", "Mahmoud", "Ibrahim", "Ali", "Hassan", "Hussein", "Mostafa", "Kamal", "Samir"],
        last: ["Omar", "Youssef", "Adam", "Malik", "Zain", "Hamza", "Kareem", "Hassan", "Ali", "Ibrahim"]
    },
    "NG": {
        first: ["Okafor", "Adebayo", "Okonkwo", "Eze", "Oluwaseun", "Adegoke", "Afolabi", "Ogunleye", "Adeniyi", "Adesina"],
        last: ["Oluwadamilare", "Oluwatobiloba", "Ayomide", "Temitope", "Oluwaseun", "Adebayo", "Chibuike", "Chisom", "Chidi", "Obinna"]
    },
    "ID": {
        first: ["Wijaya", "Kusuma", "Suryanto", "Halim", "Santoso", "Tanaka", "Wibowo", "Susanto", "Hidayat", "Putra"],
        last: ["Muhammad", "Ahmad", "Abdul", "Aditya", "Budi", "Dimas", "Eko", "Fajar", "Gading", "Hadi"]
    },
    "ZA": {
        first: ["Nkosi", "Van der Merwe", "Botha", "Mkhize", "Khumalo", "Pretorius", "Venter", "Ndlovu", "Fourie", "Nel"],
        last: ["Bandile", "Themba", "Sipho", "Thabo", "Jabu", "Mandla", "Blessing", "Gift", "Lucky", "Precious"]
    },
    "VN": {
        first: ["Nguyen", "Tran", "Le", "Pham", "Hoang", "Vu", "Do", "Dao", "Bui", "Dang"],
        last: ["Van", "Minh", "Thanh", "Ngoc", "Huu", "Quoc", "Xuan", "Duc", "Tuan", "Khanh"]
    }
};

// 电话号码格式配置
const phoneFormats = {
    "US": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[201, 989]]
    },
    "CN": {
        format: "+86 1XX-XXXX-XXXX",
        mobilePrefix: ["30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "50", "51", "52", "53", "55", "56", "57", "58", "59", "66", "70", "71", "72", "73", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89"]
    },
    "JP": {
        format: "+81 XX-XXXX-XXXX",
        mobilePrefix: ["70", "80", "90"]
    },
    "KR": {
        format: "+82 10-XXXX-XXXX"
    },
    "UK": {
        format: "+44 7XXX XXXXXX",
        mobilePrefix: ["7"]
    },
    "FR": {
        format: "+33 6 XX XX XX XX",
        mobilePrefix: ["6", "7"]
    },
    "DE": {
        format: "+49 15X XXXXXXXX",
        mobilePrefix: ["15", "16", "17"]
    },
    "TW": {
        format: "+886 9XX-XXX-XXX"
    },
    "HK": {
        format: "+852 XXXX XXXX",
        mobilePrefix: ["5", "6", "9"]
    },
    "AU": {
        format: "+61 4XX XXX XXX",
        mobilePrefix: ["4"]
    },
    "CA": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[204, 989]]
    },
    "MX": {
        format: "+52 1XX XXX XXXX"
    },
    "TR": {
        format: "+90 5XX XXX XXXX",
        mobilePrefix: ["5"]
    },
    "SA": {
        format: "+966 5XX XXX XXXX",
        mobilePrefix: ["5"]
    },
    "AR": {
        format: "+54 9XX XXXX-XXXX"
    },
    "EG": {
        format: "+20 1XX XXX XXXX",
        mobilePrefix: ["1"]
    },
    "NG": {
        format: "+234 8XX XXX XXXX",
        mobilePrefix: ["7", "8", "9"]
    },
    "ID": {
        format: "+62 8XX-XXXX-XXXX",
        mobilePrefix: ["8"]
    },
    "ZA": {
        format: "+27 8X XXX XXXX",
        mobilePrefix: ["6", "7", "8"]
    },
    "VN": {
        format: "+84 9X XXX XXXX",
        mobilePrefix: ["9", "8"]
    }
};

// 新增加的所有美国地区别名都指向同一个 US 数据
namesByCountry["NY US"] = namesByCountry["US"];
namesByCountry["California US"] = namesByCountry["US"];
namesByCountry["Texas US"] = namesByCountry["US"];
namesByCountry["CA US"] = namesByCountry["US"];
namesByCountry["Illinois US"] = namesByCountry["US"];

namesByCountry["TX US"] = namesByCountry["US"];
namesByCountry["Arizona US"] = namesByCountry["US"];
namesByCountry["Pennsylvania US"] = namesByCountry["US"];
namesByCountry["Tx US"] = namesByCountry["US"];
namesByCountry["Ca US"] = namesByCountry["US"];
namesByCountry["Washington US"] = namesByCountry["US"];

namesByCountry["Berlin DE"] = namesByCountry["DE"];
namesByCountry["Bavaria DE"] = namesByCountry["DE"];
namesByCountry["Île-de-France FR"] = namesByCountry["FR"];
namesByCountry["Provence-Alpes-Côte d'Azur FR"] = namesByCountry["FR"];
namesByCountry["London UK"] = namesByCountry["UK"];
namesByCountry["Seoul KR"] = namesByCountry["KR"];
namesByCountry["Ontario CA"] = namesByCountry["CA"];
namesByCountry["Comunidad de Madrid ES"] = namesByCountry["ES"];
namesByCountry["Cataluña ES"] = namesByCountry["ES"];
namesByCountry["Tokyo JP"] = namesByCountry["JP"];
namesByCountry["Osaka JP"] = namesByCountry["JP"];



// 新增加的美国所有电话别名都指向同一个 US 数据
phoneFormats["NY US"] = phoneFormats["US"];
phoneFormats["California US"] = phoneFormats["US"];
phoneFormats["Texas US"] = phoneFormats["US"];
phoneFormats["CA US"] = phoneFormats["US"];
phoneFormats["Illinois US"] = phoneFormats["US"];

phoneFormats["TX US"] = phoneFormats["US"];
phoneFormats["Arizona US"] = phoneFormats["US"];
phoneFormats["Pennsylvania US"] = phoneFormats["US"];
phoneFormats["Tx US"] = phoneFormats["US"];
phoneFormats["Ca US"] = phoneFormats["US"];
phoneFormats["Washington US"] = phoneFormats["US"];

phoneFormats["Berlin DE"] = phoneFormats["DE"];
phoneFormats["Bavaria DE"] = phoneFormats["DE"];
phoneFormats["Île-de-France FR"] = phoneFormats["FR"];
phoneFormats["Provence-Alpes-Côte d'Azur FR"] = phoneFormats["FR"];
phoneFormats["London UK"] = phoneFormats["UK"];
phoneFormats["Seoul KR"] = phoneFormats["KR"];
phoneFormats["Ontario CA"] = phoneFormats["CA"];
phoneFormats["Comunidad de Madrid ES"] = phoneFormats["ES"];
phoneFormats["Cataluña ES"] = phoneFormats["ES"];
phoneFormats["Tokyo JP"] = phoneFormats["JP"];
phoneFormats["Osaka JP"] = phoneFormats["JP"];

// 工具函数
function getRandomLocation(country) {
    const coordsArray = countryCoordinates[country];
    const randomCity = coordsArray[Math.floor(Math.random() * coordsArray.length)];
    const lat = randomCity.lat + (Math.random() - 0.5) * 0.1;
    const lng = randomCity.lng + (Math.random() - 0.5) * 0.1;
    return { lat, lng };
}

function getRandomName(country) {
    if (!namesByCountry[country]) {
        return null;
    }
    const names = namesByCountry[country];
    const firstName = names.first[Math.floor(Math.random() * names.first.length)];
    const lastName = names.last[Math.floor(Math.random() * names.last.length)];
    return `${firstName} ${lastName}`;
}

function generateAreaCode(ranges) {
    const range = ranges[Math.floor(Math.random() * ranges.length)];
    const [min, max] = range;
    return Math.floor(min + Math.random() * (max - min + 1));
}

function getRandomPhoneNumber(country) {
    const format = phoneFormats[country] || phoneFormats["US"];
    let phone = format.format;

    if (format.areaCodeRanges) {
        const areaCode = generateAreaCode(format.areaCodeRanges);
        phone = phone.replace("XXX", areaCode);
        phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
    } else if (format.mobilePrefix) {
        const prefix = format.mobilePrefix[Math.floor(Math.random() * format.mobilePrefix.length)];
        // 先替换前缀
        if (prefix.length === 2) {
            phone = phone.replace(/XX/, prefix);
        } else {
            phone = phone.replace(/X/, prefix);
        }
        // 然后替换剩余的X
        phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
    } else {
        phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
    }
    return phone;
}

function isValidAddress(data) {
    return data && 
           data.address && 
           data.address.house_number && 
           data.address.road && 
           (data.address.city || data.address.town);
}

// 处理CORS请求的headers
const corsHeaders = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type',
};

// HTML 模板
const htmlContent = `<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>随机地址生成器</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    animation: {
                        'gradient': 'gradient 8s linear infinite',
                    },
                    keyframes: {
                        gradient: {
                            '0%, 100%': {
                                'background-size': '200% 200%',
                                'background-position': 'left center'
                            },
                            '50%': {
                                'background-size': '200% 200%',
                                'background-position': 'right center'
                            }
                        }
                    }
                }
            }
        }
    </script>
</head>
    <link rel="shortcut icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>🌐</text></svg>">
<body class="bg-gradient-to-br from-blue-50 via-white to-blue-50 text-gray-800 min-h-screen font-['Noto_Sans_SC']">
    <!-- 头部 -->
    <header class="bg-gradient-to-r from-blue-500 via-sky-500 to-blue-500 animate-gradient w-full p-6 shadow-lg">
        <div class="max-w-4xl mx-auto flex items-center justify-between">
            <div class="flex items-center gap-3">
                
                <h1 class="text-2xl font-bold text-white">随机地址生成器</h1>
            </div>
            <div class="flex items-center gap-3">
                <a href="/" 
                   target="_blank" 
                   class="flex items-center gap-2 text-white hover:text-gray-200 transition-colors">
                    <svg viewBox="0 0 16 16" class="w-6 h-6 fill-current" aria-hidden="true">
                    <img src="https://img.freepik.com/premium-vector/minimal-location-map-icon-logo-symbol-vector-design-transparent_965979-613.jpg?w=2000" alt="Logo" class="w-8 h-8 transform hover:scale-105 transition-transform">
                    </svg>

                </a>
                <a href="/about" 
                   class="flex items-center gap-2 text-white hover:text-gray-200 transition-colors">
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-circle" viewBox="0 0 16 16">
                        <path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
                        <path d="m8.93 6.588-2.29.287-.082.38.45.083c.296.07.366.177.246.475l-.136.283-.14.064c-.296.136-.494.397-.56.745-.107 1.044.44 1.43 1.234 1.43.46.002.832-.166 1.06-.432.246-.28.334-.677.256-1.176-.09-.564-.426-.977-.977-.977-.642 0-.962.514-.962 1.176 0 .242.047.466.126.652.126.309.34.564.626.652.296.09.662.034.962-.126.296-.16.494-.432.56-.744.107-.564-.246-1.098-.832-1.098-.426 0-.764.29-.962.652-.107.19-.186.408-.246.652-.06.244-.082.49-.082.744 0 1.134.732 2.042 1.634 2.042.902 0 1.634-.908 1.634-2.042 0-1.134-.732-2.042-1.634-2.042zm-.634 5.5c-.344 0-.6-.256-.6-.588 0-.332.256-.588.6-.588.344 0 .6.256.6.588 0 .332-.256.588-.6.588z"/>
                    </svg>
                    <span class="font-medium">关于</span>
                </a>
            </div>
        </div>
    </header>

    <!-- 关于页面内容 -->
    <div id="about" class="hidden max-w-4xl mx-auto px-4 py-8">
        <div class="bg-white rounded-2xl shadow-xl p-8 border border-gray-200">
            <h2 class="text-2xl font-bold mb-6 text-blue-600">关于本项目</h2>
            <div class="prose max-w-none">
                <p class="mb-4">随机地址生成器是一个实用工具,可以帮助用户快速生成大多数常用国家和地区的随机地址信息【根据需要,国家和地区不断完善中】。该工具适用于测试、开发以及其他需要模拟地址数据的场景。</p>
                <p class="mb-4">本项目使用 Cloudflare Workers 技术构建,结合 OpenStreetMap 的地理编码服务,能够实时生成准确的地址信息。通过简单的界面操作,您可以轻松获取不同国家的地址、电话号码和个人信息。</p>
                <h3 class="text-xl font-semibold mt-6 mb-3 text-blue-600">主要功能</h3>
                <ul class="list-disc pl-6 space-y-2 mb-4">
                    <li>支持多个国家和地区的地址生成</li>
                    <li>自动生成姓名、性别、电话号码等个人信息</li>
                    <li>技术有限,英国、日本等一些国家的邮编,『地址中倒数第2项提取』</li>
                    <li>集成地图预览功能</li>
                    <li>支持保存常用地址</li>
                    <li>响应式设计适配各种设备</li>
                </ul>
                <h3 class="text-xl font-semibold mt-6 mb-3 text-blue-600">技术特点</h3>
                <ul class="list-disc pl-6 space-y-2 mb-4">
                    <li>基于 Cloudflare Workers 构建,实现无服务器架构</li>
                    <li>使用 Tailwind CSS 实现现代化 UI 设计</li>
                    <li>集成 OpenStreetMap 地理编码 API</li>
                    <li>前端完全静态化,无需后端数据库支持</li>
                    <li>本地存储保存历史记录</li>
                </ul>
                <h3 class="text-xl font-semibold mt-6 mb-3 text-blue-600">使用说明</h3>
                <p class="mb-4">选择国家或地区后点击“获取新地址”按钮即可生成随机地址信息。您可以通过点击各项信息将其复制到剪贴板,也可以点击“保存地址”按钮将当前地址保存到本地浏览器。</p>
                <p class="mb-4">在保存的地址列表中,您可以查看所有已保存的地址,并可随时删除不需要的记录。</p>
                <h3 class="text-xl font-semibold mt-6 mb-3 text-blue-600">联系方式</h3>
                <p>如有任何问题或建议,请访问我们的<a href="https://www.199881.xyz/" target="_blank" class="text-blue-600 hover:underline">官方网站</a>或通过 GitHub 联系我们。</p>
            </div>
        </div>
    </div>

    <!-- 主要内容 -->
    <main id="main-content" class="container mx-auto px-4 py-8 max-w-5xl">
        <!-- 加载动画 -->
        <div id="loading" class="hidden fixed inset-0 bg-white bg-opacity-75 backdrop-blur-sm flex items-center justify-center z-50">
            <div class="bg-white rounded-2xl p-8 flex flex-col items-center shadow-2xl">
                <div class="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500 mb-4"></div>
                <div class="text-gray-800 text-lg font-medium">正在加载...</div>
            </div>
        </div>

        <div id="copied" class="hidden fixed top-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg z-40 transform transition-transform duration-300">
            已复制到剪贴板!
        </div>

        <!-- 国家选择 -->
        <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-200 mb-8">
            <div class="flex flex-col sm:flex-row items-start sm:items-center gap-4">
                <div class="w-full sm:w-auto flex flex-col sm:flex-row items-start sm:items-center gap-2 flex-grow">
                    <label for="country" class="text-blue-600 font-bold whitespace-nowrap">选择国家/地区:</label>
                    <select id="country" 
                            onchange="changeCountry(this.value)" 
                            class="w-full bg-gray-50 border border-gray-200 rounded-xl p-3 text-gray-800 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all">
                    </select>
                </div>
                <button onclick="generateNewAddress(document.getElementById('country').value)" 
                        class="w-full sm:w-auto bg-gradient-to-r from-blue-500 to-sky-500 hover:from-blue-600 hover:to-sky-600 text-white font-bold py-3 px-6 rounded-xl transition-all transform hover:scale-105 hover:shadow-lg focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
                    获取新地址
                </button>
            </div>
        </div>

        <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
            <!-- 左侧面板 -->
            <div class="space-y-2">
                <!-- 信息卡片 -->
                <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-200">
                    <h2 class="text-xl font-bold mb-6 text-blue-600">个人信息</h2>
                    <div class="space-y-2">
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">姓名:</strong><span id="name" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">性别:</strong><span id="gender" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">电话:</strong><span id="phone" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">邮编:</strong><span id="postcode" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">地址:</strong><span id="address" class="ml-2"></span>
                        </div>

                    </div>
                </div>

                <!-- 保存按钮 -->
                <button onclick="saveAddress()" 
                        class="w-full bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600 hover:to-emerald-600 text-white font-bold py-3 px-6 rounded-xl transition-all transform hover:scale-105 hover:shadow-lg focus:ring-2 focus:ring-green-500 focus:ring-opacity-50">
                    保存地址
                </button>
            </div>

            <!-- 右侧面板 -->
            <div class="space-y-2">
                <!-- 地图 -->
                <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-200">
                    <h2 class="text-xl font-bold mb-6 text-blue-600">地图预览</h2>
                    <iframe id="map" class="w-full h-[372px] rounded-xl border border-gray-200"></iframe>
                </div>
            </div>
        </div>

        <!-- 已保存的地址表格 -->
        <div class="mt-8 bg-white rounded-2xl shadow-xl p-6 border border-gray-200">
            <h2 class="text-xl font-bold mb-6 text-blue-600">已保存的地址</h2>
            <div class="overflow-x-auto">
                <table class="w-full border-collapse" id="savedAddressesTable">
                    <thead>
                        <tr class="bg-gradient-to-r from-blue-500 to-sky-500 text-white">
                            <th class="p-4 text-left rounded-tl-lg">姓名</th>
                            <th class="p-4 text-left">性别</th>
                            <th class="p-4 text-left">电话</th>
                            <th class="p-4 text-left">地址</th>
                            <th class="p-4 text-left">备注</th>
                            <th class="p-4 text-left rounded-tr-lg">操作</th>
                        </tr>
                    </thead>
                    <tbody></tbody>
                </table>
            </div>
        </div>
    </main>

    <!-- 页脚 -->
    <footer class="text-center py-8 text-gray-600 text-sm mt-8 bg-gray-50 border-t border-gray-200">
        <p class="max-w-4xl mx-auto px-4">
总访问量 <span id="busuanzi_site_pv"></span> 次 | <span id="timeDate">载入天数...</span> | <a href="https://boke.199881.xyz/" target="_blank"> <span style="color: blue;">博客 | <a href="https://www.199881.xyz/" target="_blank"> <span style="color: green;">导航 
            <a href="https://github.com/jiangnan1224/AddressGenerator/" 
               target="_blank" 
               class="inline-flex items-center hover:text-blue-600 transition-colors">
                <img src="https://pic.imgdb.cn/item/66e7ab36d9c307b7e9cefd24.png" alt="GitHub" class="w-5 h-5 ml-1">
            </a>

            <script language="javascript"> 
            var now = new Date();
            function createtime(){
                var grt= new Date("04/23/2025 00:00:00");/*---这里是网站的启用时间--*/
                now.setTime(now.getTime()+250);
                days = (now - grt ) / 1000 / 60 / 60 / 24;
                dnum = Math.floor(days);
                document.getElementById("timeDate").innerHTML = "运行"+dnum+"天";
            }
            setInterval("createtime()",250); 
        </script> 
                <script defer src="https://bsz.211119.xyz/js"></script>

        </p>
    </footer>

    <script>
        // 国家数据
        const countries = [
            { name: "美国", code: "US" },
            { name: "美国纽约", code: "NY US" },
            { name: "美国洛杉矶", code: "California US" },
            { name: "美国达拉斯", code: "Texas US" },
            { name: "美国圣何塞", code: "CA US" },
            { name: "美国芝加哥", code: "Illinois US" },

            { name: "美国休斯敦", code: "TX US" },
            { name: "美国菲尼克斯", code: "Arizona US" },
            { name: "美国费城", code: "Pennsylvania US" },
            { name: "美国圣安东尼奥", code: "Tx US" },
            { name: "美国圣地亚哥", code: "Ca US" },
            { name: "美国西雅图", code: "Washington US" },

            { name: "德国", code: "DE" },
            { name: "德国柏林", code: "Berlin DE" },
            { name: "德国慕尼黑", code: "Bavaria DE" },
            { name: "法国", code: "FR" },
            { name: "法国巴黎", code: "Île-de-France FR" },
            { name: "法国马赛", code: "Provence-Alpes-Côte d'Azur FR" },
            { name: "越南", code: "VN" }, 
            { name: "巴西", code: "BR" },
            { name: "墨西哥", code: "MX" }, 
            { name: "韩国", code: "Seoul KR" },
            { name: "韩国首尔", code: "KR" },
            { name: "意大利", code: "IT" },
            { name: "西班牙", code: "ES" },
            { name: "西班牙马德里", code: "Comunidad de Madrid ES" },
            { name: "西班牙马德里巴塞罗那", code: "Cataluña ES" },
            { name: "土耳其", code: "TR" },
            { name: "埃及", code: "EG" },
            { name: "印度尼西亚", code: "ID" },        // 以下邮编末知
 
            { name: "英国", code: "UK" }, 
            { name: "英国伦敦", code: "London UK" },
            { name: "中国", code: "CN" },
            { name: "中国台湾", code: "TW" },
            { name: "中国香港", code: "HK" },
            { name: "日本", code: "JP" },
            { name: "日本东京", code: "Tokyo JP" },
            { name: "日本大坂", code: "Osaka JP" },
            { name: "印度", code: "IN" },
            { name: "澳大利亚", code: "AU" },
            { name: "加拿大", code: "CA" },
            { name: "加拿大多伦多", code: "Ontario CA" },
            { name: "俄罗斯", code: "RU" },
            { name: "南非", code: "ZA" },
            { name: "沙特阿拉伯", code: "SA" },
            { name: "阿根廷", code: "AR" },
            { name: "尼日利亚", code: "NG" }
        ];

        // 初始化国家选择下拉框
        function initCountrySelect() {
            const select = document.getElementById('country');
            countries.forEach(country => {
                const option = document.createElement('option');
                option.value = country.code;
                option.textContent = country.name;
                select.appendChild(option);
            });
            
            // 添加返回主页按钮的点击事件
            document.querySelector('a[href="/about"]').addEventListener('click', function(e) {
                e.preventDefault();
                showAbout();
            });
            
            // 添加返回主页按钮的点击事件(在页脚也添加了一个)
            document.querySelector('a[href="/"]').addEventListener('click', function(e) {
                e.preventDefault();
                showHome();
            });
        }

        // 复制到剪贴板
        function copyToClipboard(text) {
            navigator.clipboard.writeText(text).then(() => {
                const copied = document.getElementById('copied');
                copied.classList.remove('hidden');
                copied.classList.add('translate-y-0');
                copied.classList.remove('translate-y-[-100%]');
                
                setTimeout(() => {
                    copied.classList.add('translate-y-[-100%]');
                    copied.classList.remove('translate-y-0');
                    setTimeout(() => {
                        copied.classList.add('hidden');
                    }, 300);
                }, 2000);
            });
        }

        // 显示/隐藏加载动画
        function toggleLoading(show) {
            const loading = document.getElementById('loading');
            if (show) {
                loading.classList.remove('hidden');
            } else {
                loading.classList.add('hidden');
            }
        }

        // 更改国家
        function changeCountry(country) {
            toggleLoading(true);
            generateNewAddress(country);
        }

        // 保存地址
        function saveAddress() {
            const note = prompt('请输入备注(可以留空)') || '';
            const savedAddresses = JSON.parse(localStorage.getItem('savedAddresses') || '[]');
            const newEntry = {
                note: note,
                name: document.getElementById('name').textContent,
                gender: document.getElementById('gender').textContent,
                phone: document.getElementById('phone').textContent,
                address: document.getElementById('address').textContent
            };
            savedAddresses.push(newEntry);
            localStorage.setItem('savedAddresses', JSON.stringify(savedAddresses));
            renderSavedAddresses();
        }

        // 渲染保存的地址
        function renderSavedAddresses() {
            const savedAddresses = JSON.parse(localStorage.getItem('savedAddresses') || '[]');
            const tbody = document.querySelector('#savedAddressesTable tbody');
            tbody.innerHTML = '';
            
            savedAddresses.forEach((entry, index) => {
                const row = tbody.insertRow();
                row.className = 'border-t border-gray-200 hover:bg-gray-50 transition-colors';
                
                const cells = ['name', 'gender', 'phone', 'address', 'note', ''];
                cells.forEach((cell, i) => {
                    const td = row.insertCell();
                    td.className = 'p-4';
                    
                    if (i === cells.length - 1) {
                        const deleteBtn = document.createElement('button');
                        deleteBtn.textContent = '删除';
                        deleteBtn.className = 'bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg transition-all transform hover:scale-105 focus:ring-2 focus:ring-red-500 focus:ring-opacity-50';
                        deleteBtn.onclick = () => {
                            if (confirm('确定要删除这条记录吗?')) {
                                savedAddresses.splice(index, 1);
                                localStorage.setItem('savedAddresses', JSON.stringify(savedAddresses));
                                renderSavedAddresses();
                            }
                        };
                        td.appendChild(deleteBtn);
                    } else {
                        td.textContent = entry[cell];
                    }
                });
            });
        }

        // 生成新地址
        async function generateNewAddress(country) {
            if (!country) {
                country = document.getElementById('country').value;
            }
            toggleLoading(true);
            
            try {
                const response = await fetch(\`\${window.location.href}api?country=\${country}\`);
                const data = await response.json();
                
                if (data.error) {
                    alert(data.error);
                    return;
                }

                // 提取邮编逻辑
                const addressText = data.address || '';
                const postcodeMatch = addressText.match(/\\b\\d{5}(?:\\-\\d{4})?\\b/g); // 匹配国际通用的邮编格式
                const postcode = postcodeMatch ? postcodeMatch[0] : '未知『地址中倒数第2项提取』';

                document.getElementById('name').textContent = data.name;
                document.getElementById('gender').textContent = data.gender;
                document.getElementById('phone').textContent = data.phone;
                document.getElementById('address').textContent = addressText; // 显示完整地址
                document.getElementById('postcode').textContent = postcode; // 显示邮编

                // 更新地图
                document.getElementById('map').src = \`https://www.google.com/maps?q=\${encodeURIComponent(data.address)}&output=embed\`;
            } catch (error) {
                console.error('Error fetching address:', error);
                showErrorModal('获取地址时发生错误,请检查网络后点击重试', () => {
    generateNewAddress(document.getElementById('country').value);
});
            } finally {
                toggleLoading(false);
            }
        }

        // 页面加载时初始化
        window.onload = function() {
            initCountrySelect();
            generateNewAddress();
            renderSavedAddresses();
        };
        
        // 显示关于页面
        function showAbout() {
            document.getElementById('main-content').classList.add('hidden');
            document.getElementById('about').classList.remove('hidden');
        }

        // 返回主页
        function showHome() {
            document.getElementById('about').classList.add('hidden');
            document.getElementById('main-content').classList.remove('hidden');
        }
    </script>
</body>
</html>`;

async function handleRequest(request) {
    const url = new URL(request.url);
    const path = url.pathname;

    // 处理 API 请求
    if (path === '/api') {
        // 原有的 API 处理逻辑
        return handleApiRequest(request);
    }

    // 处理根路径请求,返回 HTML 页面
    return new Response(htmlContent, {
        headers: {
            'Content-Type': 'text/html;charset=UTF-8',
            ...corsHeaders
        }
    });
}

// 将原有的处理逻辑移到单独的函数中
async function handleApiRequest(request) {
    if (request.method === 'OPTIONS') {
        return new Response(null, {
            headers: corsHeaders
        });
    }

    if (request.method !== 'GET') {
        return new Response('Method not allowed', { status: 405 });
    }

    const url = new URL(request.url);
    const country = url.searchParams.get('country') || 'US';

    if (!countryCoordinates[country]) {
        return new Response(JSON.stringify({
            error: 'Invalid country code'
        }), {
            status: 400,
            headers: {
                'Content-Type': 'application/json',
                ...corsHeaders
            }
        });
    }

    let attempts = 0;
    const maxAttempts = 20;
    
    while (attempts < maxAttempts) {
        try {
            const location = getRandomLocation(country);
            const apiUrl = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${location.lat}&lon=${location.lng}&zoom=18&addressdetails=1`;
            
            const response = await fetch(apiUrl, {
                headers: {
                    'User-Agent': 'Cloudflare Worker Random Address Generator'
                }
            });
            
            const data = await response.json();
            
            if (isValidAddress(data)) {
                let city = data.address.city || data.address.town || '';
                city = city.split(';')[0].trim();
                
                const address = `${data.address.house_number} ${data.address.road}, ${city}, ${data.address.postcode || ''}, ${country}`.replace(/\s+/g, ' ').trim();
                
                const name = getRandomName(country);
                const gender = Math.random() > 0.4 ? 'Male男' : 'Female女';
                const phone = getRandomPhoneNumber(country);
                
                const result = {
                    name,
                    gender,
                    phone,
                    address,
                    coordinates: {
                        lat: location.lat,
                        lng: location.lng
                    }
                };
                
                return new Response(JSON.stringify(result), {
                    headers: {
                        'Content-Type': 'application/json',
                        ...corsHeaders
                    }
                });
            }
            
            attempts++;
            if (attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 100));
            }
        } catch (error) {
            attempts++;
            if (attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 100));
            }
        }
    }
    
    return new Response(JSON.stringify({
        error: 'Failed to generate valid address after multiple attempts'
    }), {
        status: 500,
        headers: {
            'Content-Type': 'application/json',
            ...corsHeaders
        }
    });
}

// 注册事件监听器
addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request));
}); 

2025年07月15日更新,界面调整,添加多个国家和地区

// 国家坐标数据
const countryCoordinates = {
    "US": [{ lat: 37.7749, lng: -122.4194 }, { lat: 34.0522, lng: -118.2437 }, { lat: 32.7767, lng: -96.7970 }, { lat: 41.8781, lng: -87.6298 }],
    "NY US": [{ lat: 40.7128, lng: -74.0060 }],
    "California US": [{ lat: 34.0522, lng: -118.2437 }],
    "Texas US": [{ lat: 32.7767, lng: -96.7970 }],
    "CA US": [{ lat: 37.3382, lng: -121.8863 }],
    "Illinois US": [{ lat: 41.8781, lng: -87.6298 }],
    "UK": [{ lat: 51.5074, lng: -0.1278 }, { lat: 53.4808, lng: -2.2426 }],
    "FR": [{ lat: 48.8566, lng: 2.3522 }, { lat: 45.7640, lng: 4.8357 }],
    "DE": [{ lat: 52.5200, lng: 13.4050 }, { lat: 48.1351, lng: 11.5820 }],
    "CN": [{ lat: 39.9042, lng: 116.4074 }, { lat: 31.2304, lng: 121.4737 }],
    "TW": [{ lat: 25.0330, lng: 121.5654 }, { lat: 22.6273, lng: 120.3014 }],
    "HK": [{ lat: 22.3193, lng: 114.1694 },{ lat: 22.3964, lng: 114.1095 }],
    "JP": [{ lat: 35.6895, lng: 139.6917 }, { lat: 34.6937, lng: 135.5023 }],
    "IN": [{ lat: 28.6139, lng: 77.2090 }, { lat: 19.0760, lng: 72.8777 }],
    "AU": [{ lat: -33.8688, lng: 151.2093 }, { lat: -37.8136, lng: 144.9631 }],
    "BR": [{ lat: -23.5505, lng: -46.6333 }, { lat: -22.9068, lng: -43.1729 }],
    "CA": [{ lat: 43.651070, lng: -79.347015 }, { lat: 45.501690, lng: -73.567253 }],
    "RU": [{ lat: 55.7558, lng: 37.6173 }, { lat: 59.9343, lng: 30.3351 }],
    "ZA": [{ lat: -33.9249, lng: 18.4241 }, { lat: -26.2041, lng: 28.0473 }],
    "MX": [{ lat: 19.4326, lng: -99.1332 }, { lat: 20.6597, lng: -103.3496 }],
    "KR": [{ lat: 37.5665, lng: 126.9780 }, { lat: 35.1796, lng: 129.0756 }],
    "IT": [{ lat: 41.9028, lng: 12.4964 }, { lat: 45.4642, lng: 9.1900 }],
    "ES": [{ lat: 40.4168, lng: -3.7038 }, { lat: 41.3851, lng: 2.1734 }],
    "TR": [{ lat: 41.0082, lng: 28.9784 }, { lat: 39.9334, lng: 32.8597 }],
    "SA": [{ lat: 24.7136, lng: 46.6753 }, { lat: 21.3891, lng: 39.8579 }],
    "AR": [{ lat: -34.6037, lng: -58.3816 }, { lat: -31.4201, lng: -64.1888 }],
    "EG": [{ lat: 30.0444, lng: 31.2357 }, { lat: 31.2156, lng: 29.9553 }],
    "NG": [{ lat: 6.5244, lng: 3.3792 }, { lat: 9.0579, lng: 7.4951 }],
    "ID": [{ lat: -6.2088, lng: 106.8456 }, { lat: -7.7956, lng: 110.3695 }],
    "VN": [{ lat: 21.0285, lng: 105.8048 }, { lat: 10.7626, lng: 106.6602 }], // 添加越南坐标数据
};

// 姓名数据
const namesByCountry = {
    "CN": {
        first: ["李", "王", "张", "刘", "陈", "杨", "黄", "赵", "吴", "周", "孙", "苏", "马", "朱", "候", "郭", "黑", "高", "林", "钱", "冯", "卫", "蒋", "许", "吕", "施", "曹", "严", "魏", "陶", "方", "雷"],
        last: ["力", "斌", "那", "真", "杰", "京", "丽", "晴", "来", "东", "勇", "仕", "云", "洁", "山", "伟", "明", "亮", "刚", "飞", "英", "兴", "浩", "曙光", "平", "志远", "志珍", "第一", "建国", "玉仙", "现红", "瑞山", "一山", "新丽", "思思", "青凤", "秀美"]
    },
    "JP": {
        first: ["Sato", "Suzuki", "Takahashi", "Tanaka", "Watanabe", "Ito", "Yamamoto", "Nakamura", "Kobayashi", "Kato"],
        last: ["Shota", "Ren", "Hina", "Yui", "Hiroto", "Sota", "Yota", "Misaki", "Nanami", "Yuto"]
    },
    "KR": {
        first: ["Kim", "Lee", "Park", "Choi", "Jung", "Kang", "Jo", "Yoon", "Jang", "Lim"],
        last: ["Minjun", "Seojun", "Doyun", "Jiho", "Jihun", "Seoyeon", "Seoyun", "Jiwoo", "Seohyun", "Minseo"]
    },
    "TW": {
        first: ["Chen", "Lin", "Huang", "Chang", "Lee", "Wang", "Wu", "Liu", "Tsai", "Yang"],
        last: ["Zhiming", "Jianhong", "Junjie", "Yijun", "Shufen", "Meiling", "Yating", "Jiahao", "Zhihao", "Shuhui"]
    },
    "HK": {
        first: ["Chan", "Lee", "Wong", "Cheung", "Lau", "Wang", "Ng", "Cheng", "Leung", "Ho"],
        last: ["Chiming", "Kayan", "Junjie", "Wingsze", "Kaming", "Meiling", "Kahao", "Winger", "Chihao", "Shukfan"]
    },
    "US": {
        first: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"],
        last: ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Thomas", "Christopher"]
    },
    "NY US": {
        first: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"],
        last: ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Thomas", "Christopher"]
    },
    "California US": {
        first: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"],
        last: ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Thomas", "Christopher"]
    },
    "Texas US": {
        first: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"],
        last: ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Thomas", "Christopher"]
    },
    "CA US": {
        first: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"],
        last: ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Thomas", "Christopher"]
    },
    "Illinois US": {
        first: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"],
        last: ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Thomas", "Christopher"]
    },
    "UK": {
        first: ["Smith", "Jones", "Williams", "Taylor", "Brown", "Davies", "Evans", "Wilson", "Thomas", "Roberts"],
        last: ["Oliver", "Jack", "Harry", "George", "Noah", "Charlie", "Jacob", "Oscar", "William", "Leo"]
    },
    "FR": {
        first: ["Martin", "Bernard", "Dubois", "Thomas", "Robert", "Richard", "Petit", "Durand", "Leroy", "Moreau"],
        last: ["Lucas", "Louis", "Gabriel", "Arthur", "Jules", "Hugo", "Leo", "Adam", "Raphael", "Paul"]
    },
    "DE": {
        first: ["Mueller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer", "Wagner", "Becker", "Schulz", "Hoffmann"],
        last: ["Ben", "Paul", "Leon", "Noah", "Luis", "Finn", "Felix", "Jonas", "Maximilian", "Henry"]
    },
    "IT": {
        first: ["Rossi", "Ferrari", "Russo", "Bianchi", "Romano", "Gallo", "Costa", "Fontana", "Conti", "Esposito"],
        last: ["Leonardo", "Francesco", "Alessandro", "Lorenzo", "Matteo", "Andrea", "Gabriele", "Marco", "Antonio", "Giuseppe"]
    },
    "ES": {
        first: ["Garcia", "Rodriguez", "Gonzalez", "Fernandez", "Lopez", "Martinez", "Sanchez", "Perez", "Martin", "Gomez"],
        last: ["Antonio", "Jose", "Manuel", "Francisco", "David", "Juan", "Miguel", "Javier", "Rafael", "Carlos"]
    },
    "BR": {
        first: ["Silva", "Santos", "Oliveira", "Souza", "Rodrigues", "Ferreira", "Alves", "Pereira", "Lima", "Gomes"],
        last: ["Miguel", "Arthur", "Heitor", "Pedro", "Davi", "Gabriel", "Bernardo", "Lucas", "Matheus", "Rafael"]
    },
    "RU": {
        first: ["Ivanov", "Smirnov", "Kuznetsov", "Popov", "Vasiliev", "Petrov", "Sokolov", "Mikhailov", "Fedorov", "Morozov"],
        last: ["Alexander", "Dmitry", "Maxim", "Ivan", "Andrey", "Mikhail", "Artem", "Daniel", "Roman", "Sergey"]
    },
    "IN": {
        first: ["Kumar", "Singh", "Sharma", "Patel", "Gupta", "Shah", "Verma", "Rao", "Reddy", "Joshi"],
        last: ["Aarav", "Vihaan", "Vivaan", "Aditya", "Arjun", "Reyansh", "Ayaan", "Sai", "Krishna", "Ishaan"]
    },
    "AU": {
        first: ["Smith", "Jones", "Williams", "Brown", "Wilson", "Taylor", "Johnson", "White", "Anderson", "Thompson"],
        last: ["Oliver", "William", "Jack", "Noah", "Thomas", "James", "Lucas", "Henry", "Ethan", "Alexander"]
    },
    "CA": {
        first: ["Smith", "Brown", "Tremblay", "Martin", "Roy", "Wilson", "MacDonald", "Taylor", "Campbell", "Anderson"],
        last: ["Liam", "Noah", "Oliver", "William", "James", "Benjamin", "Lucas", "Henry", "Theodore", "Jack"]
    },
    "MX": {
        first: ["Garcia", "Rodriguez", "Martinez", "Lopez", "Gonzalez", "Perez", "Sanchez", "Ramirez", "Torres", "Flores"],
        last: ["Santiago", "Mateo", "Sebastian", "Leonardo", "Diego", "Daniel", "Gabriel", "Adrian", "David", "Alexander"]
    },
    "TR": {
        first: ["Yilmaz", "Kaya", "Demir", "Sahin", "Celik", "Yildiz", "Erdogan", "Ozturk", "Aydin", "Ozdemir"],
        last: ["Yusuf", "Eymen", "Ömer", "Mustafa", "Ali", "Mehmet", "Ahmet", "Emir", "Hamza", "Ibrahim"]
    },
    "SA": {
        first: ["Al-Saud", "Al-Sheikh", "Al-Rashid", "Al-Qahtani", "Al-Ghamdi", "Al-Zahrani", "Al-Dossari", "Al-Shammari", "Al-Otaibi", "Al-Harbi"],
        last: ["Mohammed", "Abdullah", "Ahmed", "Ali", "Omar", "Ibrahim", "Khalid", "Hassan", "Fahad", "Abdul"]
    },
    "AR": {
        first: ["Gonzalez", "Rodriguez", "Garcia", "Fernandez", "Lopez", "Martinez", "Perez", "Romero", "Sanchez", "Diaz"],
        last: ["Mateo", "Thiago", "Benjamin", "Valentino", "Santiago", "Juan", "Lucas", "Martin", "Nicolas", "Joaquin"]
    },
    "EG": {
        first: ["Mohamed", "Ahmed", "Mahmoud", "Ibrahim", "Ali", "Hassan", "Hussein", "Mostafa", "Kamal", "Samir"],
        last: ["Omar", "Youssef", "Adam", "Malik", "Zain", "Hamza", "Kareem", "Hassan", "Ali", "Ibrahim"]
    },
    "NG": {
        first: ["Okafor", "Adebayo", "Okonkwo", "Eze", "Oluwaseun", "Adegoke", "Afolabi", "Ogunleye", "Adeniyi", "Adesina"],
        last: ["Oluwadamilare", "Oluwatobiloba", "Ayomide", "Temitope", "Oluwaseun", "Adebayo", "Chibuike", "Chisom", "Chidi", "Obinna"]
    },
    "ID": {
        first: ["Wijaya", "Kusuma", "Suryanto", "Halim", "Santoso", "Tanaka", "Wibowo", "Susanto", "Hidayat", "Putra"],
        last: ["Muhammad", "Ahmad", "Abdul", "Aditya", "Budi", "Dimas", "Eko", "Fajar", "Gading", "Hadi"]
    },
    "ZA": {
        first: ["Nkosi", "Van der Merwe", "Botha", "Mkhize", "Khumalo", "Pretorius", "Venter", "Ndlovu", "Fourie", "Nel"],
        last: ["Bandile", "Themba", "Sipho", "Thabo", "Jabu", "Mandla", "Blessing", "Gift", "Lucky", "Precious"]
    },
    "VN": {
        first: ["Nguyen", "Tran", "Le", "Pham", "Hoang", "Vu", "Do", "Dao", "Bui", "Dang"],
        last: ["Van", "Minh", "Thanh", "Ngoc", "Huu", "Quoc", "Xuan", "Duc", "Tuan", "Khanh"]
    }
};

// 电话号码格式配置
const phoneFormats = {
    "US": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[201, 989]]
    },
    "NY US": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[201, 989]]
    },
    "California US": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[201, 989]]
    },
    "Texas US": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[201, 989]]
    },
    "CA US": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[201, 989]]
    },
    "Illinois US": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[201, 989]]
    },
    "CN": {
        format: "+86 1XX-XXXX-XXXX",
        mobilePrefix: ["30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "50", "51", "52", "53", "55", "56", "57", "58", "59", "66", "70", "71", "72", "73", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89"]
    },
    "JP": {
        format: "+81 XX-XXXX-XXXX",
        mobilePrefix: ["70", "80", "90"]
    },
    "KR": {
        format: "+82 10-XXXX-XXXX"
    },
    "UK": {
        format: "+44 7XXX XXXXXX",
        mobilePrefix: ["7"]
    },
    "FR": {
        format: "+33 6 XX XX XX XX",
        mobilePrefix: ["6", "7"]
    },
    "DE": {
        format: "+49 15X XXXXXXXX",
        mobilePrefix: ["15", "16", "17"]
    },
    "TW": {
        format: "+886 9XX-XXX-XXX"
    },
    "HK": {
        format: "+852 XXXX XXXX",
        mobilePrefix: ["5", "6", "9"]
    },
    "AU": {
        format: "+61 4XX XXX XXX",
        mobilePrefix: ["4"]
    },
    "CA": {
        format: "+1 (XXX) XXX-XXXX",
        areaCodeRanges: [[204, 989]]
    },
    "MX": {
        format: "+52 1XX XXX XXXX"
    },
    "TR": {
        format: "+90 5XX XXX XXXX",
        mobilePrefix: ["5"]
    },
    "SA": {
        format: "+966 5XX XXX XXXX",
        mobilePrefix: ["5"]
    },
    "AR": {
        format: "+54 9XX XXXX-XXXX"
    },
    "EG": {
        format: "+20 1XX XXX XXXX",
        mobilePrefix: ["1"]
    },
    "NG": {
        format: "+234 8XX XXX XXXX",
        mobilePrefix: ["7", "8", "9"]
    },
    "ID": {
        format: "+62 8XX-XXXX-XXXX",
        mobilePrefix: ["8"]
    },
    "ZA": {
        format: "+27 8X XXX XXXX",
        mobilePrefix: ["6", "7", "8"]
    },
    "VN": {
        format: "+84 9X XXX XXXX",
        mobilePrefix: ["9", "8"]
    }
};

// 工具函数
function getRandomLocation(country) {
    const coordsArray = countryCoordinates[country];
    const randomCity = coordsArray[Math.floor(Math.random() * coordsArray.length)];
    const lat = randomCity.lat + (Math.random() - 0.5) * 0.1;
    const lng = randomCity.lng + (Math.random() - 0.5) * 0.1;
    return { lat, lng };
}

function getRandomName(country) {
    if (!namesByCountry[country]) {
        return null;
    }
    const names = namesByCountry[country];
    const firstName = names.first[Math.floor(Math.random() * names.first.length)];
    const lastName = names.last[Math.floor(Math.random() * names.last.length)];
    return `${firstName} ${lastName}`;
}

function generateAreaCode(ranges) {
    const range = ranges[Math.floor(Math.random() * ranges.length)];
    const [min, max] = range;
    return Math.floor(min + Math.random() * (max - min + 1));
}

function getRandomPhoneNumber(country) {
    const format = phoneFormats[country] || phoneFormats["US"];
    let phone = format.format;

    if (format.areaCodeRanges) {
        const areaCode = generateAreaCode(format.areaCodeRanges);
        phone = phone.replace("XXX", areaCode);
        phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
    } else if (format.mobilePrefix) {
        const prefix = format.mobilePrefix[Math.floor(Math.random() * format.mobilePrefix.length)];
        // 先替换前缀
        if (prefix.length === 2) {
            phone = phone.replace(/XX/, prefix);
        } else {
            phone = phone.replace(/X/, prefix);
        }
        // 然后替换剩余的X
        phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
    } else {
        phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
    }
    return phone;
}

function isValidAddress(data) {
    return data && 
           data.address && 
           data.address.house_number && 
           data.address.road && 
           (data.address.city || data.address.town);
}

// 处理CORS请求的headers
const corsHeaders = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type',
};

// HTML 模板
const htmlContent = `<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>随机地址生成器</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    animation: {
                        'gradient': 'gradient 8s linear infinite',
                    },
                    keyframes: {
                        gradient: {
                            '0%, 100%': {
                                'background-size': '200% 200%',
                                'background-position': 'left center'
                            },
                            '50%': {
                                'background-size': '200% 200%',
                                'background-position': 'right center'
                            }
                        }
                    }
                }
            }
        }
    </script>
</head>
    <link rel="shortcut icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>🌐</text></svg>">
<body class="bg-gradient-to-br from-blue-50 via-white to-blue-50 text-gray-800 min-h-screen font-['Noto_Sans_SC']">
    <!-- 头部 -->
    <header class="bg-gradient-to-r from-blue-500 via-sky-500 to-blue-500 animate-gradient w-full p-6 shadow-lg">
        <div class="max-w-4xl mx-auto flex items-center justify-between">
            <div class="flex items-center gap-3">
                <img src="https://img.freepik.com/premium-vector/minimal-location-map-icon-logo-symbol-vector-design-transparent_965979-613.jpg?w=2000" alt="Logo" class="w-12 h-12 transform hover:scale-105 transition-transform">
                <h1 class="text-2xl font-bold text-white">随机地址生成器</h1>
            </div>
            <a href="https://www.199881.xyz" 
               target="_blank" 
               class="flex items-center gap-2 text-white hover:text-gray-200 transition-colors">
                <svg viewBox="0 0 16 16" class="w-6 h-6 fill-current" aria-hidden="true">
                    <img src="https://cdn.glitch.global/efdace30-a873-49c7-aaa9-4fa31679ee0c/logo04.png?1741698119024" alt="GitHub" class="w-5 h-5 ml-1">
                </svg>
                <span class="font-medium">导航</span>
            </a>
        </div>
    </header>

    <!-- 主要内容 -->
    <main class="container mx-auto px-4 py-8 max-w-5xl">
        <!-- 加载动画 -->
        <div id="loading" class="hidden fixed inset-0 bg-white bg-opacity-75 backdrop-blur-sm flex items-center justify-center z-50">
            <div class="bg-white rounded-2xl p-8 flex flex-col items-center shadow-2xl">
                <div class="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500 mb-4"></div>
                <div class="text-gray-800 text-lg font-medium">正在加载...</div>
            </div>
        </div>

        <div id="copied" class="hidden fixed top-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg z-40 transform transition-transform duration-300">
            已复制到剪贴板!
        </div>

        <!-- 国家选择 -->
        <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-200 mb-8">
            <div class="flex flex-col sm:flex-row items-start sm:items-center gap-4">
                <div class="w-full sm:w-auto flex flex-col sm:flex-row items-start sm:items-center gap-2 flex-grow">
                    <label for="country" class="text-blue-600 font-bold whitespace-nowrap">选择国家/地区:</label>
                    <select id="country" 
                            onchange="changeCountry(this.value)" 
                            class="w-full bg-gray-50 border border-gray-200 rounded-xl p-3 text-gray-800 focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all">
                    </select>
                </div>
                <button onclick="generateNewAddress(document.getElementById('country').value)" 
                        class="w-full sm:w-auto bg-gradient-to-r from-blue-500 to-sky-500 hover:from-blue-600 hover:to-sky-600 text-white font-bold py-3 px-6 rounded-xl transition-all transform hover:scale-105 hover:shadow-lg focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
                    获取新地址
                </button>
            </div>
        </div>

        <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
            <!-- 左侧面板 -->
            <div class="space-y-6">
                <!-- 信息卡片 -->
                <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-200">
                    <h2 class="text-xl font-bold mb-6 text-blue-600">个人信息</h2>
                    <div class="space-y-4">
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">姓名:</strong><span id="name" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">性别:</strong><span id="gender" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">电话:</strong><span id="phone" class="ml-2"></span>
                        </div>
                        <div class="bg-gray-50 p-4 rounded-xl cursor-pointer hover:bg-gray-100 transition-all transform hover:scale-[1.02] hover:shadow-lg" 
                             onclick="copyToClipboard(this.querySelector('span').textContent)">
                            <strong class="text-blue-600">地址:</strong><span id="address" class="ml-2"></span>
                        </div>
                    </div>
                </div>

                <!-- 保存按钮 -->
                <button onclick="saveAddress()" 
                        class="w-full bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600 hover:to-emerald-600 text-white font-bold py-3 px-6 rounded-xl transition-all transform hover:scale-105 hover:shadow-lg focus:ring-2 focus:ring-green-500 focus:ring-opacity-50">
                    保存地址
                </button>
            </div>

            <!-- 右侧面板 -->
            <div class="space-y-6">
                <!-- 地图 -->
                <div class="bg-white rounded-2xl shadow-xl p-6 border border-gray-200">
                    <h2 class="text-xl font-bold mb-6 text-blue-600">地图预览</h2>
                    <iframe id="map" class="w-full h-[360px] rounded-xl border border-gray-200"></iframe>
                </div>
            </div>
        </div>

        <!-- 已保存的地址表格 -->
        <div class="mt-8 bg-white rounded-2xl shadow-xl p-6 border border-gray-200">
            <h2 class="text-xl font-bold mb-6 text-blue-600">已保存的地址</h2>
            <div class="overflow-x-auto">
                <table class="w-full border-collapse" id="savedAddressesTable">
                    <thead>
                        <tr class="bg-gradient-to-r from-blue-500 to-sky-500 text-white">
                            <th class="p-4 text-left rounded-tl-lg">姓名</th>
                            <th class="p-4 text-left">性别</th>
                            <th class="p-4 text-left">电话</th>
                            <th class="p-4 text-left">地址</th>
                            <th class="p-4 text-left">备注</th>
                            <th class="p-4 text-left rounded-tr-lg">操作</th>
                        </tr>
                    </thead>
                    <tbody></tbody>
                </table>
            </div>
        </div>
    </main>

    <!-- 页脚 -->
    <footer class="text-center py-8 text-gray-600 text-sm mt-8 bg-gray-50 border-t border-gray-200">
        <p class="max-w-4xl mx-auto px-4">
总访问量 <span id="busuanzi_site_pv"></span> 次 | <span id="timeDate">载入天数...</span> | <a href="https://boke.199881.xyz/" target="_blank"> <span style="color: blue;">博客 | <a href="https://www.199881.xyz/" target="_blank"> <span style="color: green;">导航 
            <a href="https://github.com/jiangnan1224/AddressGenerator/" 
               target="_blank" 
               class="inline-flex items-center hover:text-blue-600 transition-colors">
                <img src="https://pic.imgdb.cn/item/66e7ab36d9c307b7e9cefd24.png" alt="GitHub" class="w-5 h-5 ml-1">
            </a>

            <script language="javascript"> 
            var now = new Date();
            function createtime(){
                var grt= new Date("04/23/2025 00:00:00");/*---这里是网站的启用时间--*/
                now.setTime(now.getTime()+250);
                days = (now - grt ) / 1000 / 60 / 60 / 24;
                dnum = Math.floor(days);
                document.getElementById("timeDate").innerHTML = "运行"+dnum+"天";
            }
            setInterval("createtime()",250); 
        </script> 
                <script defer src="https://bsz.211119.xyz/js"></script>

        </p>
    </footer>

    <script>
        // 国家数据
        const countries = [
            { name: "美国", code: "US" },
            { name: "美国纽约", code: "NY US" },
            { name: "美国洛杉矶", code: "California US" },
            { name: "美国达拉斯", code: "Texas US" },
            { name: "美国圣何塞", code: "CA US" },
            { name: "美国芝加哥", code: "Illinois US" },
            { name: "英国", code: "UK" },
            { name: "法国", code: "FR" },
            { name: "德国", code: "DE" },
            { name: "中国", code: "CN" },
            { name: "中国台湾", code: "TW" },
            { name: "中国香港", code: "HK" },
            { name: "日本", code: "JP" },
            { name: "越南", code: "VN" },
            { name: "印度", code: "IN" },
            { name: "澳大利亚", code: "AU" },
            { name: "巴西", code: "BR" },
            { name: "加拿大", code: "CA" },
            { name: "俄罗斯", code: "RU" },
            { name: "南非", code: "ZA" },
            { name: "墨西哥", code: "MX" },
            { name: "韩国", code: "KR" },
            { name: "意大利", code: "IT" },
            { name: "西班牙", code: "ES" },
            { name: "土耳其", code: "TR" },
            { name: "沙特阿拉伯", code: "SA" },
            { name: "阿根廷", code: "AR" },
            { name: "埃及", code: "EG" },
            { name: "尼日利亚", code: "NG" },
            { name: "印度尼西亚", code: "ID" }
        ];

        // 初始化国家选择下拉框
        function initCountrySelect() {
            const select = document.getElementById('country');
            countries.forEach(country => {
                const option = document.createElement('option');
                option.value = country.code;
                option.textContent = country.name;
                select.appendChild(option);
            });
        }

        // 复制到剪贴板
        function copyToClipboard(text) {
            navigator.clipboard.writeText(text).then(() => {
                const copied = document.getElementById('copied');
                copied.classList.remove('hidden');
                copied.classList.add('translate-y-0');
                copied.classList.remove('translate-y-[-100%]');
                
                setTimeout(() => {
                    copied.classList.add('translate-y-[-100%]');
                    copied.classList.remove('translate-y-0');
                    setTimeout(() => {
                        copied.classList.add('hidden');
                    }, 300);
                }, 2000);
            });
        }

        // 显示/隐藏加载动画
        function toggleLoading(show) {
            const loading = document.getElementById('loading');
            if (show) {
                loading.classList.remove('hidden');
            } else {
                loading.classList.add('hidden');
            }
        }

        // 更改国家
        function changeCountry(country) {
            toggleLoading(true);
            generateNewAddress(country);
        }

        // 保存地址
        function saveAddress() {
            const note = prompt('请输入备注(可以留空)') || '';
            const savedAddresses = JSON.parse(localStorage.getItem('savedAddresses') || '[]');
            const newEntry = {
                note: note,
                name: document.getElementById('name').textContent,
                gender: document.getElementById('gender').textContent,
                phone: document.getElementById('phone').textContent,
                address: document.getElementById('address').textContent
            };
            savedAddresses.push(newEntry);
            localStorage.setItem('savedAddresses', JSON.stringify(savedAddresses));
            renderSavedAddresses();
        }

        // 渲染保存的地址
        function renderSavedAddresses() {
            const savedAddresses = JSON.parse(localStorage.getItem('savedAddresses') || '[]');
            const tbody = document.querySelector('#savedAddressesTable tbody');
            tbody.innerHTML = '';
            
            savedAddresses.forEach((entry, index) => {
                const row = tbody.insertRow();
                row.className = 'border-t border-gray-200 hover:bg-gray-50 transition-colors';
                
                const cells = ['name', 'gender', 'phone', 'address', 'note', ''];
                cells.forEach((cell, i) => {
                    const td = row.insertCell();
                    td.className = 'p-4';
                    
                    if (i === cells.length - 1) {
                        const deleteBtn = document.createElement('button');
                        deleteBtn.textContent = '删除';
                        deleteBtn.className = 'bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg transition-all transform hover:scale-105 focus:ring-2 focus:ring-red-500 focus:ring-opacity-50';
                        deleteBtn.onclick = () => {
                            if (confirm('确定要删除这条记录吗?')) {
                                savedAddresses.splice(index, 1);
                                localStorage.setItem('savedAddresses', JSON.stringify(savedAddresses));
                                renderSavedAddresses();
                            }
                        };
                        td.appendChild(deleteBtn);
                    } else {
                        td.textContent = entry[cell];
                    }
                });
            });
        }

        // 生成新地址
        async function generateNewAddress(country) {
            if (!country) {
                country = document.getElementById('country').value;
            }
            toggleLoading(true);
            
            try {
                const response = await fetch(\`\${window.location.href}api?country=\${country}\`);
                const data = await response.json();
                
                if (data.error) {
                    alert(data.error);
                    return;
                }
                
                document.getElementById('name').textContent = data.name;
                document.getElementById('gender').textContent = data.gender;
                document.getElementById('phone').textContent = data.phone;
                document.getElementById('address').textContent = data.address;
                
                // 更新地图
                document.getElementById('map').src = \`https://www.google.com/maps?q=\${encodeURIComponent(data.address)}&output=embed\`;
            } catch (error) {
                console.error('Error fetching address:', error);
                alert('获取地址时发生错误,请重试');
            } finally {
                toggleLoading(false);
            }
        }

        // 页面加载时初始化
        window.onload = function() {
            initCountrySelect();
            generateNewAddress();
            renderSavedAddresses();
        };
    </script>
</body>
</html>`;

async function handleRequest(request) {
    const url = new URL(request.url);
    const path = url.pathname;

    // 处理 API 请求
    if (path === '/api') {
        // 原有的 API 处理逻辑
        return handleApiRequest(request);
    }

    // 处理根路径请求,返回 HTML 页面
    return new Response(htmlContent, {
        headers: {
            'Content-Type': 'text/html;charset=UTF-8',
            ...corsHeaders
        }
    });
}

// 将原有的处理逻辑移到单独的函数中
async function handleApiRequest(request) {
    if (request.method === 'OPTIONS') {
        return new Response(null, {
            headers: corsHeaders
        });
    }

    if (request.method !== 'GET') {
        return new Response('Method not allowed', { status: 405 });
    }

    const url = new URL(request.url);
    const country = url.searchParams.get('country') || 'US';

    if (!countryCoordinates[country]) {
        return new Response(JSON.stringify({
            error: 'Invalid country code'
        }), {
            status: 400,
            headers: {
                'Content-Type': 'application/json',
                ...corsHeaders
            }
        });
    }

    let attempts = 0;
    const maxAttempts = 20;
    
    while (attempts < maxAttempts) {
        try {
            const location = getRandomLocation(country);
            const apiUrl = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${location.lat}&lon=${location.lng}&zoom=18&addressdetails=1`;
            
            const response = await fetch(apiUrl, {
                headers: {
                    'User-Agent': 'Cloudflare Worker Random Address Generator'
                }
            });
            
            const data = await response.json();
            
            if (isValidAddress(data)) {
                let city = data.address.city || data.address.town || '';
                city = city.split(';')[0].trim();
                
                const address = `${data.address.house_number} ${data.address.road}, ${city}, ${data.address.postcode || ''}, ${country}`.replace(/\s+/g, ' ').trim();
                
                const name = getRandomName(country);
                const gender = Math.random() > 0.4 ? 'Male男' : 'Female女';
                const phone = getRandomPhoneNumber(country);
                
                const result = {
                    name,
                    gender,
                    phone,
                    address,
                    coordinates: {
                        lat: location.lat,
                        lng: location.lng
                    }
                };
                
                return new Response(JSON.stringify(result), {
                    headers: {
                        'Content-Type': 'application/json',
                        ...corsHeaders
                    }
                });
            }
            
            attempts++;
            if (attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 100));
            }
        } catch (error) {
            attempts++;
            if (attempts < maxAttempts) {
                await new Promise(resolve => setTimeout(resolve, 100));
            }
        }
    }
    
    return new Response(JSON.stringify({
        error: 'Failed to generate valid address after multiple attempts'
    }), {
        status: 500,
        headers: {
            'Content-Type': 'application/json',
            ...corsHeaders
        }
    });
}

// 注册事件监听器
addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request));
}); 
0

评论 (0)

取消
您是第 165284 位访客