From bd6b518aae61608ddc2d82b43ccc283dc95b9c54 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期三, 11 三月 2026 13:59:33 +0800
Subject: [PATCH] #

---
 src/main/webapp/static/js/login/login.js |   73 ++++++++++++++++++++++++++++++++++++
 1 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/src/main/webapp/static/js/login/login.js b/src/main/webapp/static/js/login/login.js
index 9a7b1d0..2fb1a1b 100644
--- a/src/main/webapp/static/js/login/login.js
+++ b/src/main/webapp/static/js/login/login.js
@@ -11,6 +11,7 @@
                 localeOptions: [],
                 currentLocale: "zh-CN",
                 loginLoading: false,
+                passkeyLoading: false,
                 mfaLoading: false,
                 toolsDialogVisible: false,
                 textDialogVisible: false,
@@ -152,6 +153,33 @@
                     return true;
                 });
             },
+            handlePasskeyLogin: function () {
+                var vm = this;
+                if (!window.WCS_WEBAUTHN || !window.WCS_WEBAUTHN.isSupported()) {
+                    vm.$message.error(vm.resolvePasskeyErrorMessage({ message: window.isSecureContext ? "not-supported" : "secure-context" }, "login.error.passkeyOptionsFailed", "鑾峰彇閫氳瀵嗛挜鐧诲綍鍙傛暟澶辫触"));
+                    return;
+                }
+                vm.passkeyLoading = true;
+                ajaxJson({
+                    url: baseUrl + "/login/passkey/options.action",
+                    data: {
+                        mobile: vm.loginForm.mobile
+                    },
+                    method: "POST",
+                    success: function (res) {
+                        if (Number(res.code) !== 200) {
+                            vm.passkeyLoading = false;
+                            vm.$message.error(res.msg || vm.text("login.error.passkeyOptionsFailed", "鑾峰彇閫氳瀵嗛挜鐧诲綍鍙傛暟澶辫触"));
+                            return;
+                        }
+                        vm.executePasskeyLogin(res.data || {});
+                    },
+                    error: function () {
+                        vm.passkeyLoading = false;
+                        vm.$message.error(vm.text("login.error.passkeyOptionsFailed", "鑾峰彇閫氳瀵嗛挜鐧诲綍鍙傛暟澶辫触"));
+                    }
+                });
+            },
             submitLogin: function () {
                 var vm = this;
                 vm.loginLoading = true;
@@ -250,12 +278,57 @@
                     }
                 });
             },
+            executePasskeyLogin: function (payload) {
+                var vm = this;
+                window.WCS_WEBAUTHN.authenticate(payload).then(function (assertion) {
+                    ajaxJson({
+                        url: baseUrl + "/login/passkey/verify.action",
+                        data: {
+                            ticket: payload.ticket,
+                            credentialId: assertion.credentialId,
+                            clientDataJSON: assertion.clientDataJSON,
+                            authenticatorData: assertion.authenticatorData,
+                            signature: assertion.signature
+                        },
+                        method: "POST",
+                        success: function (res) {
+                            if (Number(res.code) === 200) {
+                                vm.finishLogin(res.data || {});
+                                return;
+                            }
+                            vm.$message.error(res.msg || vm.text("login.error.passkeyVerifyFailed", "閫氳瀵嗛挜楠岃瘉澶辫触"));
+                        },
+                        error: function () {
+                            vm.$message.error(vm.text("login.error.passkeyVerifyFailed", "閫氳瀵嗛挜楠岃瘉澶辫触"));
+                        },
+                        complete: function () {
+                            vm.passkeyLoading = false;
+                        }
+                    });
+                }).catch(function (err) {
+                    vm.passkeyLoading = false;
+                    vm.$message.error(vm.resolvePasskeyErrorMessage(err, "login.error.passkeyVerifyFailed", "閫氳瀵嗛挜楠岃瘉澶辫触"));
+                });
+            },
             finishLogin: function (payload) {
                 localStorage.setItem("token", payload.token || "");
                 localStorage.setItem("username", payload.username || this.loginForm.mobile || "");
                 this.closeMfaDialog();
                 window.location.href = "index.html";
             },
+            resolvePasskeyErrorMessage: function (err, fallbackKey, fallbackText) {
+                var message = err && err.message ? String(err.message) : "";
+                if (message === "secure-context") {
+                    return this.text("login.passkey.secureContext", "閫氳瀵嗛挜浠呮敮鎸佸湪 HTTPS 鎴� localhost 鐜涓嬩娇鐢�");
+                }
+                if (message === "not-supported" || message === "extension-unsupported" || message === "public-key-missing") {
+                    return this.text("login.passkey.browserUnsupported", "褰撳墠娴忚鍣ㄤ笉鏀寔閫氳瀵嗛挜锛岃浣跨敤鏈�鏂扮増 Chrome銆丒dge 鎴� Safari");
+                }
+                if (err && err.name === "NotAllowedError") {
+                    return this.text(fallbackKey, fallbackText);
+                }
+                return this.text(fallbackKey, fallbackText);
+            },
             openTextDialog: function (title, label, text, tip) {
                 var pretty = "";
                 try {

--
Gitblit v1.9.1