{"id":135,"date":"2025-11-22T21:39:38","date_gmt":"2025-11-22T13:39:38","guid":{"rendered":"http:\/\/gcm-erp.ddns.net\/GCMERP\/?page_id=135"},"modified":"2025-11-24T15:43:02","modified_gmt":"2025-11-24T07:43:02","slug":"zj-erp-login","status":"publish","type":"page","link":"http:\/\/gcm-erp.ddns.net\/GCMERP\/zj-erp-login\/","title":{"rendered":"ZJ-\u7cfb\u7d71\u767b\u5165\u9801\u9762"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"135\" class=\"elementor elementor-135\">\n\t\t\t\t<div class=\"elementor-element elementor-element-44d8588 e-con-full e-flex e-con e-parent\" data-id=\"44d8588\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-5b93c11 elementor-widget elementor-widget-shortcode\" data-id=\"5b93c11\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\"><!DOCTYPE html>\n<html lang=\"zh-TW\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>\u93ae\u6c5f\u5de5\u7a0b\u7ba1\u7406\u7cfb\u7d71<\/title>\n    <style>\n        #wpadminbar {\n            display: none !important;\n        }\n        \n        html {\n            margin-top: 0 !important;\n        }\n        \n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n        \n        body {\n            font-family: 'Microsoft JhengHei', Arial, sans-serif;\n            background-image: url('http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-content\/uploads\/2025\/11\/souhlin\u91c7\u96c6\u5230\u7d20\u6750\u56fe\u7247-3.png');\n            background-size: cover;\n            background-position: center;\n            background-repeat: no-repeat;\n            background-attachment: fixed;\n            margin: 0;\n            padding: 20px;\n            min-height: 100vh;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n        }\n\n        .login-container {\n            width: 100%;\n            max-width: 420px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n        }\n\n        .login-box {\n            background: rgba(255, 255, 255, 0.95);\n            padding: 40px;\n            border-radius: 15px;\n            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.3);\n            width: 100%;\n            backdrop-filter: blur(10px);\n        }\n\n        h1 {\n            text-align: center;\n            color: #667eea;\n            margin-bottom: 30px;\n            font-size: 26px;\n            font-weight: bold;\n        }\n\n        .form-group {\n            margin-bottom: 20px;\n        }\n\n        label {\n            display: block;\n            margin-bottom: 8px;\n            color: #333;\n            font-weight: 500;\n        }\n\n        input[type=\"text\"], input[type=\"password\"] {\n            width: 100%;\n            padding: 12px;\n            border: 2px solid #e0e0e0;\n            border-radius: 8px;\n            font-size: 16px;\n            transition: all 0.3s ease;\n            box-sizing: border-box;\n        }\n\n        input[type=\"text\"]:focus, input[type=\"password\"]:focus {\n            outline: none;\n            border-color: #667eea;\n            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);\n        }\n\n        .checkbox-group {\n            margin: 20px 0;\n            display: flex;\n            align-items: center;\n            gap: 10px;\n        }\n\n        .checkbox-group input[type=\"checkbox\"] {\n            width: 20px;\n            height: 20px;\n            cursor: pointer;\n        }\n\n        .checkbox-group label {\n            margin: 0;\n            cursor: pointer;\n            user-select: none;\n            font-weight: normal;\n        }\n\n        .buttons {\n            display: flex;\n            gap: 12px;\n            margin-top: 25px;\n        }\n\n        button {\n            flex: 1;\n            padding: 12px;\n            border: none;\n            border-radius: 8px;\n            font-size: 16px;\n            font-weight: bold;\n            cursor: pointer;\n            transition: all 0.3s ease;\n        }\n\n        .login-btn {\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            color: white;\n        }\n\n        .login-btn:hover {\n            transform: translateY(-2px);\n            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);\n        }\n\n        .login-btn:active {\n            transform: translateY(0);\n        }\n\n        .login-btn:disabled {\n            opacity: 0.6;\n            cursor: not-allowed;\n            transform: none;\n        }\n\n        .cancel-btn {\n            background: #6c757d;\n            color: white;\n        }\n\n        .cancel-btn:hover {\n            background: #545b62;\n        }\n\n        .message {\n            margin-top: 20px;\n            padding: 12px;\n            border-radius: 8px;\n            text-align: center;\n            display: none;\n            animation: fadeIn 0.3s ease;\n        }\n\n        @keyframes fadeIn {\n            from { opacity: 0; transform: translateY(-10px); }\n            to { opacity: 1; transform: translateY(0); }\n        }\n\n        .success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }\n        .error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }\n        .info { background: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }\n\n        .version {\n            text-align: center;\n            margin-top: 20px;\n            font-size: 12px;\n            color: #999;\n        }\n\n        .card-hint {\n            font-size: 13px;\n            color: #667eea;\n            margin-top: 8px;\n            padding: 8px;\n            background: rgba(102, 126, 234, 0.1);\n            border-radius: 5px;\n            display: none;\n        }\n\n        .card-hint.show {\n            display: block;\n        }\n\n        .loading-indicator {\n            display: inline-block;\n            width: 16px;\n            height: 16px;\n            border: 3px solid rgba(255, 255, 255, 0.3);\n            border-top: 3px solid white;\n            border-radius: 50%;\n            animation: spin 1s linear infinite;\n            margin-left: 8px;\n            vertical-align: middle;\n        }\n\n        @keyframes spin {\n            0% { transform: rotate(0deg); }\n            100% { transform: rotate(360deg); }\n        }\n\n        @media (max-width: 768px) {\n            body {\n                padding: 15px;\n            }\n            \n            .login-box {\n                padding: 30px 25px;\n            }\n        }\n\n        @media (max-width: 480px) {\n            body {\n                padding: 10px;\n                align-items: flex-start;\n                padding-top: 60px;\n            }\n            \n            .login-box {\n                padding: 25px 20px;\n            }\n            \n            h1 {\n                font-size: 22px;\n                margin-bottom: 25px;\n            }\n            \n            input[type=\"text\"], input[type=\"password\"] {\n                font-size: 16px;\n                padding: 10px;\n            }\n            \n            button {\n                padding: 10px;\n                font-size: 15px;\n            }\n        }\n    <\/style>\n<\/head>\n<body>\n    <div class=\"login-container\">\n        <div class=\"login-box\">\n            <h1>\ud83d\udd27 \u93ae\u6c5f\u5de5\u7a0b\u7ba1\u7406\u7cfb\u7d71<\/h1>\n            \n            <form id=\"loginForm\">\n                <div class=\"form-group\">\n                    <label>\u4f7f\u7528\u8005\u5e33\u865f<\/label>\n                    <input type=\"text\" id=\"username\" required autocomplete=\"username\">\n                    <div class=\"card-hint\" id=\"cardHint\">\ud83d\udcb3 \u555f\u7528 ID \u5361\u6a21\u5f0f\u5f8c,\u8acb\u5237\u5361\u6216\u8f38\u5165 10 \u4f4d\u6578\u5b57\u5361\u865f<\/div>\n                <\/div>\n                \n                <div class=\"form-group\">\n                    <label>\u5bc6\u78bc<\/label>\n                    <input type=\"password\" id=\"password\" required autocomplete=\"current-password\">\n                <\/div>\n\n                <div class=\"checkbox-group\">\n                    <input type=\"checkbox\" id=\"enableCard\" checked>\n                    <label for=\"enableCard\">\u555f\u7528 ID \u5361\u5237\u5361\u767b\u5165<\/label>\n                <\/div>\n                \n                <div class=\"buttons\">\n                    <button type=\"submit\" class=\"login-btn\" id=\"loginBtn\">\u767b\u5165<\/button>\n                    <button type=\"button\" class=\"cancel-btn\" onclick=\"\u6e05\u9664()\">\u6e05\u9664<\/button>\n                <\/div>\n                \n                <div class=\"version\">\u7248\u672c\u865f: 2025112402<\/div>\n            <\/form>\n\n            <div id=\"message\" class=\"message\"><\/div>\n        <\/div>\n    <\/div>\n\n    <script>\n        const ajaxurl = 'http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-admin\/admin-ajax.php';\n        \n        \/\/ ==================== \u88dd\u7f6e\u5224\u65b7\u8207\u5206\u6d41 ====================\n        \n        \/\/ \u6aa2\u67e5\u662f\u5426\u70ba\u79fb\u52d5\u88dd\u7f6e\n        function \u662f\u5426\u70ba\u79fb\u52d5\u88dd\u7f6e() {\n            \/\/ \u6aa2\u67e5 viewport \u5bec\u5ea6\n            const isMobileViewport = window.innerWidth <= 1024;\n            \n            \/\/ \u6aa2\u67e5 User Agent\n            const userAgent = navigator.userAgent.toLowerCase();\n            const isMobileUA = \/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile|tablet\/i.test(userAgent);\n            \n            \/\/ \u6aa2\u67e5\u89f8\u63a7\u652f\u63f4\n            const isTouchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);\n            \n            return isMobileViewport || (isMobileUA && isTouchDevice);\n        }\n        \n        \/\/ \u53d6\u5f97\u767b\u5165\u6210\u529f\u5f8c\u7684\u8df3\u8f49\u8def\u5f91(\u6839\u64da\u88dd\u7f6e\u81ea\u52d5\u5206\u6d41)\n        function \u53d6\u5f97\u8df3\u8f49\u8def\u5f91() {\n            if (\u662f\u5426\u70ba\u79fb\u52d5\u88dd\u7f6e()) {\n                console.log('\u5075\u6e2c\u5230\u79fb\u52d5\u88dd\u7f6e,\u5c07\u8df3\u8f49\u81f3\u624b\u6a5f\u7248');\n                return '\/GCMERP\/zj-erp-mobile\/';\n            } else {\n                console.log('\u5075\u6e2c\u5230PC\u88dd\u7f6e,\u5c07\u8df3\u8f49\u81f3PC\u7248');\n                return '\/GCMERP\/zj-erp-main\/';\n            }\n        }\n        \n        \/\/ ==================== \u767b\u5165\u76f8\u95dc\u529f\u80fd ====================\n        \n        \/\/ \u76e3\u807d ID \u5361\u52fe\u9078\u72c0\u614b\n        document.getElementById('enableCard').addEventListener('change', function() {\n            const cardHint = document.getElementById('cardHint');\n            const usernameInput = document.getElementById('username');\n            \n            if (this.checked) {\n                cardHint.classList.add('show');\n                usernameInput.placeholder = '\u8acb\u5237\u5361\u6216\u8f38\u5165\u5e33\u865f';\n                usernameInput.focus();\n            } else {\n                cardHint.classList.remove('show');\n                usernameInput.placeholder = '';\n            }\n        });\n\n        \/\/ \u76e3\u807d\u5e33\u865f\u8f38\u5165(ID \u5361\u81ea\u52d5\u767b\u5165)\n        let cardLoginTimeout = null;\n        document.getElementById('username').addEventListener('input', function(e) {\n            const enableCard = document.getElementById('enableCard').checked;\n            const value = this.value;\n            \n            \/\/ \u6e05\u9664\u4e4b\u524d\u7684\u8a08\u6642\u5668\n            if (cardLoginTimeout) {\n                clearTimeout(cardLoginTimeout);\n            }\n            \n            \/\/ \u5982\u679c\u555f\u7528 ID \u5361\u6a21\u5f0f\u4e14\u8f38\u5165\u70ba 10 \u4f4d\u6578\u5b57\n            if (enableCard && \/^\\d{10}$\/.test(value)) {\n                \u986f\u793a\u8a0a\u606f('\u5075\u6e2c\u5230 ID \u5361,\u6b63\u5728\u9a57\u8b49...', 'info');\n                \n                \/\/ \u5ef6\u9072 500ms \u5f8c\u81ea\u52d5\u767b\u5165(\u907f\u514d\u8f38\u5165\u904e\u7a0b\u4e2d\u89f8\u767c)\n                cardLoginTimeout = setTimeout(() => {\n                    ID\u5361\u767b\u5165(value);\n                }, 500);\n            }\n        });\n\n        function \u6e05\u9664() {\n            document.getElementById('username').value = '';\n            document.getElementById('password').value = '';\n            document.getElementById('message').style.display = 'none';\n        }\n\n        function \u986f\u793a\u8a0a\u606f(\u6587\u5b57, \u985e\u578b) {\n            const msg = document.getElementById('message');\n            msg.textContent = \u6587\u5b57;\n            msg.className = 'message ' + \u985e\u578b;\n            msg.style.display = 'block';\n        }\n\n        function \u8a2d\u5b9a\u6309\u9215\u8f09\u5165\u72c0\u614b(\u8f09\u5165\u4e2d) {\n            const loginBtn = document.getElementById('loginBtn');\n            if (\u8f09\u5165\u4e2d) {\n                loginBtn.disabled = true;\n                loginBtn.innerHTML = '\u767b\u5165\u4e2d<span class=\"loading-indicator\"><\/span>';\n            } else {\n                loginBtn.disabled = false;\n                loginBtn.innerHTML = '\u767b\u5165';\n            }\n        }\n\n        \/\/ ID \u5361\u767b\u5165\u51fd\u6578\n        async function ID\u5361\u767b\u5165(\u5361\u865f) {\n            \u8a2d\u5b9a\u6309\u9215\u8f09\u5165\u72c0\u614b(true);\n            \n            try {\n                const formData = new URLSearchParams();\n                formData.append('action', 'zj_card_login');\n                formData.append('card_id', \u5361\u865f);\n\n                const response = await fetch(ajaxurl, {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application\/x-www-form-urlencoded' },\n                    body: formData\n                });\n\n                const result = await response.json();\n                \n                if (result.success) {\n                    \/\/ ID \u5361\u9a57\u8b49\u6210\u529f\n                    const \u88dd\u7f6e\u985e\u578b = \u662f\u5426\u70ba\u79fb\u52d5\u88dd\u7f6e() ? '\u624b\u6a5f\u7248' : 'PC\u7248';\n                    \u986f\u793a\u8a0a\u606f('ID \u5361\u9a57\u8b49\u6210\u529f\uff01\u6b61\u8fce ' + result.data.user.\u59d3\u540d + ',\u6b63\u5728\u8df3\u8f49\u81f3' + \u88dd\u7f6e\u985e\u578b + '...', 'success');\n                    \n                    \/\/ \u5132\u5b58\u4f7f\u7528\u8005\u8cc7\u8a0a\n                    \u5132\u5b58\u4f7f\u7528\u8005\u8cc7\u8a0a(result.data.user);\n                    \n                    \/\/ \u6839\u64da\u88dd\u7f6e\u8df3\u8f49\u5230\u5c0d\u61c9\u9801\u9762\n                    setTimeout(() => {\n                        window.location.href = \u53d6\u5f97\u8df3\u8f49\u8def\u5f91();\n                    }, 1500);\n                    \n                } else {\n                    \u986f\u793a\u8a0a\u606f('ID \u5361\u9a57\u8b49\u5931\u6557: ' + result.data.message, 'error');\n                    \u6e05\u9664();\n                    \u8a2d\u5b9a\u6309\u9215\u8f09\u5165\u72c0\u614b(false);\n                }\n                \n            } catch (error) {\n                console.error('ID \u5361\u767b\u5165\u932f\u8aa4:', error);\n                \u986f\u793a\u8a0a\u606f('ID \u5361\u9a57\u8b49\u932f\u8aa4: ' + error.message, 'error');\n                \u8a2d\u5b9a\u6309\u9215\u8f09\u5165\u72c0\u614b(false);\n            }\n        }\n\n        \/\/ \u4e00\u822c\u767b\u5165\u51fd\u6578\n        document.getElementById('loginForm').addEventListener('submit', async function(e) {\n            e.preventDefault();\n            \n            const \u5e33\u865f = document.getElementById('username').value.trim();\n            const \u5bc6\u78bc = document.getElementById('password').value;\n            \n            if (!\u5e33\u865f || !\u5bc6\u78bc) {\n                \u986f\u793a\u8a0a\u606f('\u8acb\u8f38\u5165\u5e33\u865f\u548c\u5bc6\u78bc', 'error');\n                return;\n            }\n\n            await \u57f7\u884c\u767b\u5165(\u5e33\u865f, \u5bc6\u78bc);\n        });\n\n        async function \u57f7\u884c\u767b\u5165(\u5e33\u865f, \u5bc6\u78bc) {\n            \u986f\u793a\u8a0a\u606f('\u6b63\u5728\u9023\u7dda\u4e2d...', 'info');\n            \u8a2d\u5b9a\u6309\u9215\u8f09\u5165\u72c0\u614b(true);\n\n            try {\n                const formData = new URLSearchParams();\n                formData.append('action', 'zj_user_login');\n                formData.append('username', \u5e33\u865f);\n                formData.append('password', \u5bc6\u78bc);\n\n                const response = await fetch(ajaxurl, {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application\/x-www-form-urlencoded' },\n                    body: formData\n                });\n\n                if (!response.ok) {\n                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n                }\n\n                const result = await response.json();\n                \n                if (result.success) {\n                    const \u88dd\u7f6e\u985e\u578b = \u662f\u5426\u70ba\u79fb\u52d5\u88dd\u7f6e() ? '\u624b\u6a5f\u7248' : 'PC\u7248';\n                    \u986f\u793a\u8a0a\u606f('\u767b\u5165\u6210\u529f\uff01\u6b61\u8fce ' + result.data.user.\u59d3\u540d + ',\u6b63\u5728\u8df3\u8f49\u81f3' + \u88dd\u7f6e\u985e\u578b + '...', 'success');\n                    \n                    \/\/ \u5132\u5b58\u4f7f\u7528\u8005\u8cc7\u8a0a\n                    \u5132\u5b58\u4f7f\u7528\u8005\u8cc7\u8a0a(result.data.user);\n                    \n                    \/\/ \u6839\u64da\u88dd\u7f6e\u8df3\u8f49\u5230\u5c0d\u61c9\u9801\u9762\n                    setTimeout(() => {\n                        window.location.href = \u53d6\u5f97\u8df3\u8f49\u8def\u5f91();\n                    }, 1500);\n                    \n                } else {\n                    \u986f\u793a\u8a0a\u606f('\u767b\u5165\u5931\u6557: ' + result.data.message, 'error');\n                    document.getElementById('password').value = '';\n                    \u8a2d\u5b9a\u6309\u9215\u8f09\u5165\u72c0\u614b(false);\n                }\n                \n            } catch (error) {\n                console.error('\u767b\u5165\u932f\u8aa4:', error);\n                \u986f\u793a\u8a0a\u606f('\u9023\u7dda\u932f\u8aa4: ' + error.message, 'error');\n                \u8a2d\u5b9a\u6309\u9215\u8f09\u5165\u72c0\u614b(false);\n            }\n        }\n\n        function \u5132\u5b58\u4f7f\u7528\u8005\u8cc7\u8a0a(user) {\n            const userInfo = {\n                \u59d3\u540d: user.\u59d3\u540d || '\u672a\u77e5\u4f7f\u7528\u8005',\n                \u8077\u7a31: user.\u8077\u7a31 || '\u672a\u77e5\u8077\u7a31',\n                \u5e33\u865f: user.\u5e33\u865f || '',\n                ID\u5361\u865f: user.ID\u5361\u865f || '',\n                \u767b\u5165\u6642\u9593: new Date().toLocaleString('zh-TW', {\n                    year: 'numeric',\n                    month: '2-digit',\n                    day: '2-digit',\n                    hour: '2-digit',\n                    minute: '2-digit',\n                    second: '2-digit'\n                })\n            };\n            \n            sessionStorage.setItem('zj_user_info', JSON.stringify(userInfo));\n        }\n\n        \/\/ \u9801\u9762\u8f09\u5165\u6642\u6aa2\u67e5\u662f\u5426\u5df2\u767b\u5165\n        window.addEventListener('load', async function() {\n            \/\/ \u986f\u793a ID \u5361\u63d0\u793a\n            if (document.getElementById('enableCard').checked) {\n                document.getElementById('cardHint').classList.add('show');\n            }\n            \n            \/\/ \u6aa2\u67e5\u767b\u5165\u72c0\u614b\n            try {\n                const response = await fetch(ajaxurl + '?action=zj_check_login');\n                const result = await response.json();\n                \n                if (result.success && result.data.logged_in) {\n                    \/\/ \u5df2\u767b\u5165,\u6839\u64da\u88dd\u7f6e\u8df3\u8f49\u5230\u5c0d\u61c9\u9801\u9762\n                    console.log('\u4f7f\u7528\u8005\u5df2\u767b\u5165,\u81ea\u52d5\u8df3\u8f49...');\n                    window.location.href = \u53d6\u5f97\u8df3\u8f49\u8def\u5f91();\n                }\n            } catch (error) {\n                console.log('\u6aa2\u67e5\u767b\u5165\u72c0\u614b\u5931\u6557:', error);\n            }\n        });\n    <\/script>\n<\/body>\n<\/html>\n    <\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":999,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_jetpack_hide_featured_image":false,"_jetpack_featured_image_duplicate_dismissed":0,"footnotes":""},"class_list":["post-135","page","type-page","status-publish","hentry"],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-json\/wp\/v2\/pages\/135","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-json\/wp\/v2\/comments?post=135"}],"version-history":[{"count":5,"href":"http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-json\/wp\/v2\/pages\/135\/revisions"}],"predecessor-version":[{"id":382,"href":"http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-json\/wp\/v2\/pages\/135\/revisions\/382"}],"wp:attachment":[{"href":"http:\/\/gcm-erp.ddns.net\/GCMERP\/wp-json\/wp\/v2\/media?parent=135"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}