Junjie
5 天以前 1a3f0ed6b7f6d4112069a3c8679e7192365d5eef
src/main/webapp/static/js/detail/detail.js
@@ -15,18 +15,48 @@
                saving: false,
                passwordDialogVisible: false,
                passwordSaving: false,
                mfaDialogVisible: false,
                mfaDialogMode: "enable",
                mfaSetupLoading: false,
                mfaSubmitting: false,
                passkeyDialogVisible: false,
                passkeyDialogMode: "register",
                passkeySubmitting: false,
                form: {
                    id: "",
                    roleName: "",
                    username: "",
                    mobile: "",
                    password: "",
                    createTime$: ""
                    createTime$: "",
                    mfaAllow: 0,
                    mfaAllow$: "否",
                    mfaEnabled: 0,
                    mfaEnabled$: "否",
                    mfaBoundTime$: "",
                    mfaMaskedSecret: "",
                    passkeyBound: false,
                    passkeyName: "",
                    passkeyBoundTime$: "",
                    passkeyLastUsedTime$: "",
                    passkeyTransports: ""
                },
                passwordForm: {
                    oldPassword: "",
                    password: "",
                    rePassword: ""
                },
                mfaForm: {
                    currentPassword: "",
                    code: ""
                },
                mfaSetup: {
                    secret: "",
                    qrCode: "",
                    otpAuth: ""
                },
                passkeyForm: {
                    name: "",
                    currentPassword: ""
                },
                rules: {
                    username: [
@@ -38,25 +68,7 @@
                },
                passwordRules: {
                    oldPassword: [
                        { required: true, message: "请输入当前密码", trigger: "blur" },
                        {
                            validator: function (rule, value, callback) {
                                if (!value) {
                                    callback(new Error("请输入当前密码"));
                                    return;
                                }
                                if (!this.form.password) {
                                    callback(new Error("未读取到当前用户密码"));
                                    return;
                                }
                                if (hex_md5(value) !== this.form.password) {
                                    callback(new Error("密码不匹配"));
                                    return;
                                }
                                callback();
                            }.bind(this),
                            trigger: "blur"
                        }
                        { required: true, message: "请输入当前密码", trigger: "blur" }
                    ],
                    password: [
                        { required: true, message: "请输入新密码", trigger: "blur" },
@@ -70,7 +82,7 @@
                                    callback(new Error("不能少于4个字符"));
                                    return;
                                }
                                if (this.form.password && hex_md5(value) === this.form.password) {
                                if (value === this.passwordForm.oldPassword) {
                                    callback(new Error("与旧密码不能相同"));
                                    return;
                                }
@@ -91,6 +103,29 @@
                            }.bind(this),
                            trigger: "blur"
                        }
                    ]
                },
                mfaRules: {
                    currentPassword: [
                        { required: true, message: "请输入当前密码", trigger: "blur" }
                    ],
                    code: [
                        { required: true, message: "请输入6位验证码", trigger: "blur" },
                        {
                            validator: function (rule, value, callback) {
                                if (!/^\d{6}$/.test(String(value || "").trim())) {
                                    callback(new Error("请输入6位数字验证码"));
                                    return;
                                }
                                callback();
                            },
                            trigger: "blur"
                        }
                    ]
                },
                passkeyRules: {
                    currentPassword: [
                        { required: true, message: "请输入当前密码", trigger: "blur" }
                    ]
                }
            };
@@ -155,10 +190,10 @@
                var vm = this;
                vm.passwordSaving = true;
                $.ajax({
                    url: baseUrl + "/user/update/auth",
                    url: baseUrl + "/user/password/update/auth",
                    headers: { token: localStorage.getItem("token") },
                    data: {
                        id: vm.form.id,
                        oldPassword: hex_md5(vm.passwordForm.oldPassword),
                        password: hex_md5(vm.passwordForm.password)
                    },
                    method: "POST",
@@ -170,7 +205,6 @@
                            vm.$message.error(res.msg || "密码修改失败");
                            return;
                        }
                        vm.form.password = hex_md5(vm.passwordForm.password);
                        vm.passwordDialogVisible = false;
                        vm.$alert("密码修改成功,请重新登录", "提示", {
                            confirmButtonText: "确定",
@@ -188,6 +222,304 @@
                    }
                });
            },
            openMfaEnableDialog: function () {
                if (Number(this.form.mfaAllow) !== 1) {
                    this.$message.warning("当前账号未开通MFA使用权限");
                    return;
                }
                this.mfaDialogMode = "enable";
                this.resetMfaDialogState();
                this.mfaDialogVisible = true;
                this.loadMfaSetup();
            },
            openMfaDisableDialog: function () {
                this.mfaDialogMode = "disable";
                this.resetMfaDialogState();
                this.mfaDialogVisible = true;
            },
            closeMfaDialog: function () {
                this.mfaDialogVisible = false;
                this.resetMfaDialogState();
            },
            resetMfaDialogState: function () {
                this.mfaSubmitting = false;
                this.mfaSetupLoading = false;
                this.mfaForm = {
                    currentPassword: "",
                    code: ""
                };
                this.mfaSetup = {
                    secret: "",
                    qrCode: "",
                    otpAuth: ""
                };
                if (this.$refs.mfaForm) {
                    this.$refs.mfaForm.clearValidate();
                }
            },
            loadMfaSetup: function () {
                var vm = this;
                vm.mfaSetupLoading = true;
                $.ajax({
                    url: baseUrl + "/user/mfa/setup/auth",
                    headers: { token: localStorage.getItem("token") },
                    method: "POST",
                    success: function (res) {
                        if (handleForbidden(res)) {
                            return;
                        }
                        if (Number(res.code) !== 200) {
                            vm.$message.error(res.msg || "MFA配置加载失败");
                            return;
                        }
                        vm.mfaSetup = Object.assign({}, vm.mfaSetup, res.data || {});
                    },
                    error: function () {
                        vm.$message.error("MFA配置加载失败");
                    },
                    complete: function () {
                        vm.mfaSetupLoading = false;
                    }
                });
            },
            handleMfaSubmit: function () {
                var vm = this;
                if (!vm.$refs.mfaForm) {
                    return;
                }
                vm.$refs.mfaForm.validate(function (valid) {
                    if (!valid) {
                        return false;
                    }
                    if (vm.mfaDialogMode === "enable" && !vm.mfaSetup.secret) {
                        vm.$message.error("MFA密钥尚未生成,请稍后重试");
                        return false;
                    }
                    vm.submitMfaAction();
                    return true;
                });
            },
            submitMfaAction: function () {
                var vm = this;
                vm.mfaSubmitting = true;
                $.ajax({
                    url: baseUrl + (vm.mfaDialogMode === "enable" ? "/user/mfa/enable/auth" : "/user/mfa/disable/auth"),
                    headers: { token: localStorage.getItem("token") },
                    data: {
                        currentPassword: hex_md5(vm.mfaForm.currentPassword),
                        code: vm.mfaForm.code,
                        secret: vm.mfaSetup.secret
                    },
                    method: "POST",
                    success: function (res) {
                        if (handleForbidden(res)) {
                            return;
                        }
                        if (Number(res.code) !== 200) {
                            vm.$message.error(res.msg || "MFA操作失败");
                            return;
                        }
                        vm.$message.success(vm.mfaDialogMode === "enable" ? "MFA已启用" : "MFA已停用");
                        vm.closeMfaDialog();
                        vm.loadDetail();
                    },
                    error: function () {
                        vm.$message.error("MFA操作失败");
                    },
                    complete: function () {
                        vm.mfaSubmitting = false;
                    }
                });
            },
            openPasskeyRegisterDialog: function () {
                if (!window.WCS_WEBAUTHN || !window.WCS_WEBAUTHN.isSupported()) {
                    this.$message.error(this.resolvePasskeyErrorMessage({ message: window.isSecureContext ? "not-supported" : "secure-context" }, "当前环境不支持通行密钥"));
                    return;
                }
                this.passkeyDialogMode = "register";
                this.resetPasskeyDialogState();
                this.passkeyDialogVisible = true;
            },
            openPasskeyRemoveDialog: function () {
                this.passkeyDialogMode = "remove";
                this.resetPasskeyDialogState();
                this.passkeyDialogVisible = true;
            },
            closePasskeyDialog: function () {
                this.passkeyDialogVisible = false;
                this.resetPasskeyDialogState();
            },
            resetPasskeyDialogState: function () {
                this.passkeySubmitting = false;
                this.passkeyForm = {
                    name: "",
                    currentPassword: ""
                };
                if (this.$refs.passkeyForm) {
                    this.$refs.passkeyForm.clearValidate();
                }
            },
            handlePasskeySubmit: function () {
                var vm = this;
                if (!vm.$refs.passkeyForm) {
                    return;
                }
                vm.$refs.passkeyForm.validate(function (valid) {
                    if (!valid) {
                        return false;
                    }
                    if (vm.passkeyDialogMode === "register") {
                        vm.submitPasskeyRegister();
                    } else {
                        vm.submitPasskeyRemove();
                    }
                    return true;
                });
            },
            submitPasskeyRegister: function () {
                var vm = this;
                if (!window.WCS_WEBAUTHN || !window.WCS_WEBAUTHN.isSupported()) {
                    vm.$message.error(vm.resolvePasskeyErrorMessage({ message: window.isSecureContext ? "not-supported" : "secure-context" }, "当前环境不支持通行密钥"));
                    return;
                }
                vm.passkeySubmitting = true;
                $.ajax({
                    url: baseUrl + "/user/passkey/register/options/auth",
                    headers: { token: localStorage.getItem("token") },
                    method: "POST",
                    success: function (res) {
                        if (handleForbidden(res)) {
                            return;
                        }
                        if (Number(res.code) !== 200) {
                            vm.passkeySubmitting = false;
                            vm.$message.error(res.msg || "通行密钥配置加载失败");
                            return;
                        }
                        vm.executePasskeyRegister(res.data || {});
                    },
                    error: function () {
                        vm.passkeySubmitting = false;
                        vm.$message.error("通行密钥配置加载失败");
                    }
                });
            },
            executePasskeyRegister: function (optionsPayload) {
                var vm = this;
                window.WCS_WEBAUTHN.register(optionsPayload).then(function (credential) {
                    $.ajax({
                        url: baseUrl + "/user/passkey/register/finish/auth",
                        headers: { token: localStorage.getItem("token") },
                        data: {
                            ticket: optionsPayload.ticket,
                            currentPassword: hex_md5(vm.passkeyForm.currentPassword),
                            name: vm.passkeyForm.name,
                            credentialId: credential.credentialId,
                            clientDataJSON: credential.clientDataJSON,
                            authenticatorData: credential.authenticatorData,
                            publicKey: credential.publicKey,
                            publicKeyAlgorithm: credential.publicKeyAlgorithm,
                            transports: credential.transports
                        },
                        method: "POST",
                        success: function (res) {
                            if (handleForbidden(res)) {
                                return;
                            }
                            if (Number(res.code) !== 200) {
                                vm.$message.error(res.msg || "通行密钥绑定失败");
                                return;
                            }
                            vm.$message.success("通行密钥已绑定");
                            vm.closePasskeyDialog();
                            vm.loadDetail();
                        },
                        error: function () {
                            vm.$message.error("通行密钥绑定失败");
                        },
                        complete: function () {
                            vm.passkeySubmitting = false;
                        }
                    });
                }).catch(function (err) {
                    vm.passkeySubmitting = false;
                    vm.$message.error(vm.resolvePasskeyErrorMessage(err, "通行密钥绑定失败"));
                });
            },
            submitPasskeyRemove: function () {
                var vm = this;
                vm.passkeySubmitting = true;
                $.ajax({
                    url: baseUrl + "/user/passkey/remove/auth",
                    headers: { token: localStorage.getItem("token") },
                    data: {
                        currentPassword: hex_md5(vm.passkeyForm.currentPassword)
                    },
                    method: "POST",
                    success: function (res) {
                        if (handleForbidden(res)) {
                            return;
                        }
                        if (Number(res.code) !== 200) {
                            vm.$message.error(res.msg || "通行密钥解绑失败");
                            return;
                        }
                        vm.$message.success("通行密钥已解绑");
                        vm.closePasskeyDialog();
                        vm.loadDetail();
                    },
                    error: function () {
                        vm.$message.error("通行密钥解绑失败");
                    },
                    complete: function () {
                        vm.passkeySubmitting = false;
                    }
                });
            },
            resolvePasskeyErrorMessage: function (err, fallback) {
                var message = err && err.message ? String(err.message) : "";
                if (message === "secure-context") {
                    return "通行密钥仅支持在 HTTPS 或 localhost 环境下使用";
                }
                if (message === "not-supported" || message === "extension-unsupported" || message === "public-key-missing") {
                    return "当前浏览器不支持通行密钥,请使用最新版 Chrome、Edge 或 Safari";
                }
                if (err && err.name === "NotAllowedError") {
                    return "已取消通行密钥操作或验证超时";
                }
                return fallback || "通行密钥操作失败";
            },
            copySecret: function () {
                var vm = this;
                var text = vm.mfaSetup.secret || "";
                if (!text) {
                    return;
                }
                if (navigator.clipboard && navigator.clipboard.writeText) {
                    navigator.clipboard.writeText(text).then(function () {
                        vm.$message.success("密钥已复制");
                    }).catch(function () {
                        vm.fallbackCopy(text);
                    });
                    return;
                }
                vm.fallbackCopy(text);
            },
            fallbackCopy: function (text) {
                try {
                    var textarea = document.createElement("textarea");
                    textarea.value = text;
                    textarea.style.position = "fixed";
                    textarea.style.opacity = "0";
                    document.body.appendChild(textarea);
                    textarea.select();
                    document.execCommand("copy");
                    document.body.removeChild(textarea);
                    this.$message.success("密钥已复制");
                } catch (err) {
                    this.$message.error("复制失败");
                }
            },
            handleSave: function () {
                var vm = this;
                vm.$refs.profileForm.validate(function (valid) {