定位天气预报edit icon

作者:
邓朝元
Fork(复制)
下载
嵌入
BUG反馈
index.html
现在支持上传本地图片了!
index.html
            
            <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能定位权限请求系统</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        :root {
            --primary: #3b82f6;
            --primary-dark: #2563eb;
            --secondary: #8b5cf6;
            --success: #10b981;
            --danger: #ef4444;
            --warning: #f59e0b;
            --info: #3b82f6;
            --light: #f8fafc;
            --dark: #0f172a;
            --gray: #94a3b8;
            --border: #e2e8f0;
            --card-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
            --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }

        body {
            background: linear-gradient(135deg, #e0f2fe 0%, #dbeafe 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
            position: relative;
            overflow-x: hidden;
        }

        .container {
            width: 100%;
            max-width: 1000px;
            display: flex;
            flex-direction: column;
            gap: 30px;
        }

        .header {
            text-align: center;
            padding: 20px;
            animation: fadeInDown 0.8s ease;
        }

        .header h1 {
            font-size: 2.5rem;
            background: linear-gradient(135deg, var(--primary), var(--secondary));
            -webkit-background-clip: text;
            background-clip: text;
            color: transparent;
            margin-bottom: 10px;
            font-weight: 800;
        }

        .header p {
            color: var(--gray);
            font-size: 1.1rem;
            max-width: 600px;
            margin: 0 auto;
        }

        .main-content {
            display: flex;
            gap: 30px;
            flex-wrap: wrap;
        }

        .card {
            background: white;
            border-radius: 20px;
            box-shadow: var(--card-shadow);
            padding: 30px;
            transition: var(--transition);
            position: relative;
            overflow: hidden;
        }

        .card::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 4px;
            background: linear-gradient(90deg, var(--primary), var(--secondary));
        }

        .permission-section {
            flex: 1;
            min-width: 350px;
            animation: slideInLeft 0.6s ease;
        }

        .location-section {
            width: 350px;
            animation: slideInRight 0.6s ease;
        }

        .section-title {
            font-size: 1.5rem;
            color: var(--dark);
            margin-bottom: 20px;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .permission-prompt {
            background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
            border-radius: 15px;
            padding: 25px;
            text-align: center;
            margin-bottom: 25px;
        }

        .permission-icon {
            font-size: 3.5rem;
            color: var(--primary);
            margin-bottom: 20px;
        }

        .permission-title {
            font-size: 1.4rem;
            color: var(--dark);
            margin-bottom: 15px;
        }

        .permission-desc {
            color: var(--gray);
            font-size: 1rem;
            line-height: 1.6;
            margin-bottom: 25px;
        }

        .permission-benefits {
            text-align: left;
            margin: 20px 0;
        }

        .benefit-item {
            display: flex;
            align-items: flex-start;
            gap: 12px;
            margin-bottom: 15px;
        }

        .benefit-icon {
            color: var(--success);
            margin-top: 3px;
        }

        .benefit-text {
            color: var(--dark);
            font-size: 0.95rem;
        }

        .btn {
            width: 100%;
            padding: 14px;
            border: none;
            border-radius: 12px;
            font-size: 1.1rem;
            font-weight: 600;
            cursor: pointer;
            transition: var(--transition);
            position: relative;
            overflow: hidden;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            margin-top: 15px;
        }

        .btn-primary {
            background: linear-gradient(135deg, var(--primary), var(--secondary));
            color: white;
            box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
        }

        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 20px rgba(59, 130, 246, 0.4);
        }

        .btn-outline {
            background: transparent;
            border: 2px solid var(--primary);
            color: var(--primary);
        }

        .btn-outline:hover {
            background: var(--primary);
            color: white;
        }

        .btn-warning {
            background: linear-gradient(135deg, var(--warning), #f97316);
            color: white;
        }

        .btn-warning:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 20px rgba(249, 115, 22, 0.4);
        }

        .message {
            padding: 15px 20px;
            border-radius: 12px;
            margin-bottom: 20px;
            display: none;
            animation: slideInUp 0.3s ease;
            font-size: 0.95rem;
            font-weight: 500;
        }

        .message.success {
            background: rgba(16, 185, 129, 0.1);
            border: 1px solid var(--success);
            color: var(--success);
        }

        .message.error {
            background: rgba(239, 68, 68, 0.1);
            border: 1px solid var(--danger);
            color: var(--danger);
        }

        .message.info {
            background: rgba(59, 130, 246, 0.1);
            border: 1px solid var(--info);
            color: var(--info);
        }

        .message.warning {
            background: rgba(245, 158, 11, 0.1);
            border: 1px solid var(--warning);
            color: var(--warning);
        }

        .location-info {
            background: #f0f9ff;
            border-radius: 15px;
            padding: 20px;
            margin-bottom: 25px;
        }

        .location-item {
            display: flex;
            justify-content: space-between;
            padding: 12px 0;
            border-bottom: 1px solid var(--border);
        }

        .location-item:last-child {
            border-bottom: none;
        }

        .location-label {
            font-weight: 500;
            color: var(--dark);
        }

        .location-value {
            color: var(--gray);
        }

        .status-indicator {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 15px;
            border-radius: 12px;
            margin-top: 20px;
            background: #f0f9ff;
        }

        .status-dot {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: var(--gray);
        }

        .status-dot.active {
            background: var(--success);
            box-shadow: 0 0 10px var(--success);
        }

        .status-dot.warning {
            background: var(--warning);
            box-shadow: 0 0 10px var(--warning);
        }

        .status-text {
            font-size: 0.95rem;
            color: var(--dark);
        }

        .loading {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 3px solid rgba(255,255,255,.3);
            border-radius: 50%;
            border-top-color: #fff;
            animation: spin 1s ease-in-out infinite;
        }

        .denied-prompt {
            display: none;
            background: #fff7ed;
            border-radius: 15px;
            padding: 25px;
            text-align: center;
            margin-bottom: 25px;
            border: 1px solid #fed7aa;
        }

        .denied-icon {
            font-size: 3rem;
            color: var(--warning);
            margin-bottom: 20px;
        }

        .denied-title {
            font-size: 1.4rem;
            color: var(--dark);
            margin-bottom: 15px;
        }

        .denied-desc {
            color: var(--gray);
            font-size: 1rem;
            line-height: 1.6;
            margin-bottom: 25px;
        }

        .manual-instructions {
            text-align: left;
            background: #ffedd5;
            border-radius: 10px;
            padding: 15px;
            margin: 20px 0;
        }

        .instruction-title {
            font-weight: 600;
            margin-bottom: 10px;
            color: var(--dark);
        }

        .instruction-steps {
            padding-left: 20px;
        }

        .instruction-steps li {
            margin-bottom: 8px;
            color: var(--gray);
            font-size: 0.9rem;
        }

        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        @keyframes fadeInDown {
            from {
                opacity: 0;
                transform: translateY(-20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        @keyframes slideInLeft {
            from {
                opacity: 0;
                transform: translateX(-30px);
            }
            to {
                opacity: 1;
                transform: translateX(0);
            }
        }

        @keyframes slideInRight {
            from {
                opacity: 0;
                transform: translateX(30px);
            }
            to {
                opacity: 1;
                transform: translateX(0);
            }
        }

        @keyframes slideInUp {
            from {
                opacity: 0;
                transform: translateY(20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        @keyframes spin {
            to { transform: rotate(360deg); }
        }

        @media (max-width: 768px) {
            .main-content {
                flex-direction: column;
            }
            
            .permission-section, .location-section {
                min-width: 100%;
            }
            
            .header h1 {
                font-size: 2rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1><i class="fas fa-map-marker-alt"></i> 智能定位权限请求</h1>
            <p>优雅地处理用户定位权限请求,提供重新请求选项</p>
        </div>
        
        <div class="main-content">
            <div class="permission-section">
                <div class="card">
                    <h2 class="section-title"><i class="fas fa-key"></i> 定位权限请求</h2>
                    
                    <div id="permissionMessage" class="message"></div>
                    
                    <!-- 权限请求提示 -->
                    <div class="permission-prompt" id="permissionPrompt">
                        <div class="permission-icon">
                            <i class="fas fa-location-dot"></i>
                        </div>
                        <h3 class="permission-title">我们需要您的位置信息</h3>
                        <p class="permission-desc">
                            为了为您提供准确的天气信息和个性化服务,我们需要获取您的当前位置。
                            您的位置信息仅用于改善您的使用体验。
                        </p>
                        
                        <div class="permission-benefits">
                            <div class="benefit-item">
                                <i class="fas fa-check-circle benefit-icon"></i>
                                <div class="benefit-text">获取当前位置的实时天气信息</div>
                            </div>
                            <div class="benefit-item">
                                <i class="fas fa-check-circle benefit-icon"></i>
                                <div class="benefit-text">提供基于位置的个性化服务</div>
                            </div>
                            <div class="benefit-item">
                                <i class="fas fa-check-circle benefit-icon"></i>
                                <div class="benefit-text">您的隐私信息将得到严格保护</div>
                            </div>
                        </div>
                        
                        <button class="btn btn-primary" id="requestPermissionBtn">
                            <span id="requestPermissionText"><i class="fas fa-location-arrow"></i> 授予位置权限</span>
                        </button>
                        
                        <button class="btn btn-outline" id="skipPermissionBtn">
                            <i class="fas fa-times"></i> 暂时跳过
                        </button>
                    </div>
                    
                    <!-- 权限被拒绝后的提示 -->
                    <div class="denied-prompt" id="deniedPrompt">
                        <div class="denied-icon">
                            <i class="fas fa-exclamation-triangle"></i>
                        </div>
                        <h3 class="denied-title">位置权限被拒绝</h3>
                        <p class="denied-desc">
                            您之前拒绝了位置权限请求。为了提供完整功能,我们建议您授予位置权限。
                        </p>
                        
                        <div class="manual-instructions">
                            <div class="instruction-title">如何手动授予权限:</div>
                            <ol class="instruction-steps">
                                <li>点击浏览器地址栏左侧的锁图标</li>
                                <li>选择"网站设置"</li>
                                <li>找到"位置"选项并设置为"允许"</li>
                                <li>刷新页面以重新请求权限</li>
                            </ol>
                        </div>
                        
                        <button class="btn btn-warning" id="retryPermissionBtn">
                            <span id="retryPermissionText"><i class="fas fa-redo"></i> 重新请求权限</span>
                        </button>
                        
                        <button class="btn btn-outline" id="continueWithoutLocationBtn">
                            <i class="fas fa-arrow-right"></i> 继续使用(无位置功能)
                        </button>
                    </div>
                    
                    <div class="status-indicator">
                        <div class="status-dot" id="permissionStatus"></div>
                        <div class="status-text" id="permissionStatusText">等待用户操作</div>
                    </div>
                </div>
            </div>
            
            <div class="location-section">
                <div class="card">
                    <h2 class="section-title"><i class="fas fa-location-dot"></i> 位置信息</h2>
                    
                    <div id="locationMessage" class="message"></div>
                    
                    <div class="location-info">
                        <div class="location-item">
                            <span class="location-label">纬度:</span>
                            <span class="location-value" id="latitude">--</span>
                        </div>
                        <div class="location-item">
                            <span class="location-label">经度:</span>
                            <span class="location-value" id="longitude">--</span>
                        </div>
                        <div class="location-item">
                            <span class="location-label">精度:</span>
                            <span class="location-value" id="accuracy">-- 米</span>
                        </div>
                        <div class="location-item">
                            <span class="location-label">获取时间:</span>
                            <span class="location-value" id="timestamp">--</span>
                        </div>
                        <div class="location-item">
                            <span class="location-label">地址:</span>
                            <span class="location-value" id="address">--</span>
                        </div>
                    </div>
                    
                    <button class="btn btn-primary" id="getLocationBtn" disabled>
                        <span id="getLocationText"><i class="fas fa-sync-alt"></i> 刷新位置</span>
                    </button>
                    
                    <div class="status-indicator">
                        <div class="status-dot" id="locationStatus"></div>
                        <div class="status-text" id="locationStatusText">等待权限授予</div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 全局变量
        let currentPosition = null;
        let permissionState = 'prompt'; // 'prompt', 'granted', 'denied'
        let permissionRequested = false;
        
        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', function() {
            // 绑定按钮事件
            document.getElementById('requestPermissionBtn').addEventListener('click', requestLocationPermission);
            document.getElementById('skipPermissionBtn').addEventListener('click', skipPermission);
            document.getElementById('retryPermissionBtn').addEventListener('click', retryPermission);
            document.getElementById('continueWithoutLocationBtn').addEventListener('click', continueWithoutLocation);
            document.getElementById('getLocationBtn').addEventListener('click', refreshLocation);
            
            // 检查初始权限状态
            checkInitialPermission();
        });

        // 检查初始权限状态
        function checkInitialPermission() {
            if ('permissions' in navigator) {
                navigator.permissions.query({name: 'geolocation'})
                    .then(function(permissionStatus) {
                        permissionState = permissionStatus.state;
                        updatePermissionStatus();
                        
                        // 如果权限已授予,自动获取位置
                        if (permissionState === 'granted') {
                            getLocation();
                        }
                    })
                    .catch(function(error) {
                        console.log('权限API不可用:', error);
                        // 降级到直接请求位置
                        getLocation();
                    });
            } else {
                // 降级到直接请求位置
                getLocation();
            }
        }

        // 请求位置权限
        function requestLocationPermission() {
            const btn = document.getElementById('requestPermissionBtn');
            const btnText = document.getElementById('requestPermissionText');
            
            // 显示加载状态
            btn.disabled = true;
            btnText.innerHTML = '<span class="loading"></span> 请求权限中...';
            
            // 更新状态
            updatePermissionStatus('请求中...', 'warning');
            permissionRequested = true;
            
            // 请求位置权限
            if (navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(
                    function(position) {
                        // 权限授予成功
                        permissionState = 'granted';
                        updatePermissionStatus('权限已授予', 'active');
                        showMessage('permissionMessage', '位置权限授予成功!', 'success');
                        
                        // 获取位置信息
                        currentPosition = {
                            latitude: position.coords.latitude,
                            longitude: position.coords.longitude,
                            accuracy: position.coords.accuracy,
                            timestamp: new Date(position.timestamp)
                        };
                        
                        updateLocationDisplay();
                        enableLocationButton();
                        
                        // 获取地址信息
                        getAddressFromCoordinates(currentPosition.latitude, currentPosition.longitude);
                        
                        // 恢复按钮状态
                        btn.disabled = false;
                        btnText.innerHTML = '<i class="fas fa-location-arrow"></i> 授予位置权限';
                    },
                    function(error) {
                        // 权限被拒绝或获取位置失败
                        permissionState = 'denied';
                        updatePermissionStatus('权限被拒绝', 'error');
                        
                        let errorMessage = '';
                        switch(error.code) {
                            case error.PERMISSION_DENIED:
                                errorMessage = '用户拒绝了位置权限请求';
                                showDeniedPrompt();
                                break;
                            case error.POSITION_UNAVAILABLE:
                                errorMessage = '位置信息不可用';
                                break;
                            case error.TIMEOUT:
                                errorMessage = '获取位置超时';
                                break;
                            default:
                                errorMessage = '获取位置时发生未知错误';
                        }
                        
                        showMessage('permissionMessage', errorMessage, 'error');
                        
                        // 恢复按钮状态
                        btn.disabled = false;
                        btnText.innerHTML = '<i class="fas fa-location-arrow"></i> 授予位置权限';
                    },
                    {
                        enableHighAccuracy: true,
                        timeout: 15000,
                        maximumAge: 0
                    }
                );
            } else {
                showMessage('permissionMessage', '您的浏览器不支持地理定位功能', 'error');
                btn.disabled = false;
                btnText.innerHTML = '<i class="fas fa-location-arrow"></i> 授予位置权限';
                updatePermissionStatus('浏览器不支持', 'error');
            }
        }

        // 跳过权限请求
        function skipPermission() {
            permissionState = 'prompt';
            updatePermissionStatus('用户跳过', 'warning');
            showMessage('permissionMessage', '您已跳过位置权限请求,可以稍后重新请求', 'info');
            showDeniedPrompt();
        }

        // 重新请求权限
        function retryPermission() {
            // 重置状态
            permissionState = 'prompt';
            updatePermissionStatus('重新请求中...', 'warning');
            
            // 隐藏拒绝提示,显示权限请求提示
            document.getElementById('deniedPrompt').style.display = 'none';
            document.getElementById('permissionPrompt').style.display = 'block';
            
            // 自动触发权限请求
            setTimeout(requestLocationPermission, 500);
        }

        // 继续使用(无位置功能)
        function continueWithoutLocation() {
            permissionState = 'denied';
            updatePermissionStatus('无位置功能', 'warning');
            showMessage('permissionMessage', '您已选择继续使用(无位置功能)', 'info');
            
            // 隐藏拒绝提示
            document.getElementById('deniedPrompt').style.display = 'none';
        }

        // 刷新位置
        function refreshLocation() {
            if (permissionState === 'granted') {
                getLocation();
            } else {
                showMessage('locationMessage', '请先授予位置权限', 'warning');
            }
        }

        // 获取位置信息
        function getLocation() {
            if (!navigator.geolocation) {
                showMessage('locationMessage', '您的浏览器不支持地理定位功能', 'error');
                updateLocationStatus('浏览器不支持', 'error');
                return;
            }
            
            const btn = document.getElementById('getLocationBtn');
            const btnText = document.getElementById('getLocationText');
            
            // 显示加载状态
            btn.disabled = true;
            btnText.innerHTML = '<span class="loading"></span> 获取位置中...';
            
            // 更新状态
            updateLocationStatus('获取中...', 'warning');
            
            navigator.geolocation.getCurrentPosition(
                function(position) {
                    // 成功获取位置
                    currentPosition = {
                        latitude: position.coords.latitude,
                        longitude: position.coords.longitude,
                        accuracy: position.coords.accuracy,
                        timestamp: new Date(position.timestamp)
                    };
                    
                    updateLocationDisplay();
                    updateLocationStatus('位置已获取', 'active');
                    showMessage('locationMessage', '位置获取成功!', 'success');
                    
                    // 获取地址信息
                    getAddressFromCoordinates(currentPosition.latitude, currentPosition.longitude);
                    
                    // 恢复按钮状态
                    btn.disabled = false;
                    btnText.innerHTML = '<i class="fas fa-sync-alt"></i> 刷新位置';
                },
                function(error) {
                    // 获取位置失败
                    updateLocationStatus('获取失败', 'error');
                    
                    let errorMessage = '';
                    switch(error.code) {
                        case error.PERMISSION_DENIED:
                            errorMessage = '位置权限被拒绝';
                            permissionState = 'denied';
                            updatePermissionStatus('权限被拒绝', 'error');
                            showDeniedPrompt();
                            break;
                        case error.POSITION_UNAVAILABLE:
                            errorMessage = '位置信息不可用';
                            break;
                        case error.TIMEOUT:
                            errorMessage = '获取位置超时';
                            break;
                        default:
                            errorMessage = '获取位置时发生未知错误';
                    }
                    
                    showMessage('locationMessage', errorMessage, 'error');
                    
                    // 恢复按钮状态
                    btn.disabled = false;
                    btnText.innerHTML = '<i class="fas fa-sync-alt"></i> 刷新位置';
                },
                {
                    enableHighAccuracy: true,
                    timeout: 15000,
                    maximumAge: 0
                }
            );
        }

        // 通过坐标获取地址信息
        function getAddressFromCoordinates(lat, lon) {
            // 使用免费的Nominatim服务获取地址(无需API密钥)
            const apiUrl = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lon}&accept-language=zh-CN&addressdetails=1`;
            
            fetch(apiUrl)
                .then(response => {
                    if (!response.ok) {
                        throw new Error('地址获取失败');
                    }
                    return response.json();
                })
                .then(data => {
                    if (data && data.display_name) {
                        document.getElementById('address').textContent = data.display_name;
                    } else {
                        document.getElementById('address').textContent = '地址信息不可用';
                    }
                })
                .catch(error => {
                    console.error('获取地址信息失败:', error);
                    document.getElementById('address').textContent = '地址获取失败';
                });
        }

        // 更新位置显示
        function updateLocationDisplay() {
            if (currentPosition) {
                document.getElementById('latitude').textContent = currentPosition.latitude.toFixed(6);
                document.getElementById('longitude').textContent = currentPosition.longitude.toFixed(6);
                document.getElementById('accuracy').textContent = currentPosition.accuracy.toFixed(0) + ' 米';
                document.getElementById('timestamp').textContent = currentPosition.timestamp.toLocaleString();
            }
        }

        // 启用位置按钮
        function enableLocationButton() {
            document.getElementById('getLocationBtn').disabled = false;
        }

        // 显示权限被拒绝提示
        function showDeniedPrompt() {
            document.getElementById('permissionPrompt').style.display = 'none';
            document.getElementById('deniedPrompt').style.display = 'block';
        }

        // 更新权限状态指示器
        function updatePermissionStatus(text, status) {
            const statusDot = document.getElementById('permissionStatus');
            const statusText = document.getElementById('permissionStatusText');
            
            if (text) statusText.textContent = text;
            
            // 移除所有状态类
            statusDot.className = 'status-dot';
            
            // 添加相应状态类
            if (status === 'active') {
                statusDot.classList.add('active');
            } else if (status === 'error') {
                statusDot.style.background = 'var(--danger)';
            } else if (status === 'warning') {
                statusDot.classList.add('warning');
            }
        }

        // 更新位置状态指示器
        function updateLocationStatus(text, status) {
            const statusDot = document.getElementById('locationStatus');
            const statusText = document.getElementById('locationStatusText');
            
            if (text) statusText.textContent = text;
            
            // 移除所有状态类
            statusDot.className = 'status-dot';
            
            // 添加相应状态类
            if (status === 'active') {
                statusDot.classList.add('active');
            } else if (status === 'error') {
                statusDot.style.background = 'var(--danger)';
            } else if (status === 'warning') {
                statusDot.classList.add('warning');
            }
        }

        // 显示消息
        function showMessage(elementId, message, type) {
            const messageElement = document.getElementById(elementId);
            if (messageElement) {
                messageElement.textContent = message;
                messageElement.className = 'message ' + type;
                messageElement.style.display = 'block';
                
                // 3秒后自动隐藏消息
                setTimeout(() => {
                    messageElement.style.display = 'none';
                }, 3000);
            }
        }
    </script>
</body>
</html>
        
编辑器加载中
预览
控制台