| | |
| | | localeOptions: [], |
| | | currentLocale: "zh-CN", |
| | | loginLoading: false, |
| | | passkeyLoading: false, |
| | | mfaLoading: false, |
| | | toolsDialogVisible: false, |
| | | textDialogVisible: false, |
| | | uploadDialogVisible: false, |
| | | mfaDialogVisible: false, |
| | | licenseBase64: "", |
| | | titleClickCount: 0, |
| | | titleClickTimer: null, |
| | | loginForm: { |
| | | mobile: "", |
| | | password: "" |
| | | }, |
| | | mfaForm: { |
| | | code: "" |
| | | }, |
| | | mfaPending: { |
| | | ticket: "", |
| | | username: "" |
| | | }, |
| | | textDialog: { |
| | | title: "", |
| | |
| | | password: [ |
| | | { required: true, message: "请输入密码", trigger: "blur" } |
| | | ] |
| | | }, |
| | | mfaRules: { |
| | | 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" |
| | | } |
| | | ] |
| | | } |
| | | }; |
| | | }, |
| | |
| | | var localeTick = this.localeTick; |
| | | void localeTick; |
| | | if (window.WCS_I18N && typeof window.WCS_I18N.t === "function") { |
| | | return window.WCS_I18N.t(key); |
| | | var translated = window.WCS_I18N.t(key); |
| | | if (translated && translated !== key) { |
| | | return translated; |
| | | } |
| | | } |
| | | return fallback || key; |
| | | }, |
| | | refreshRuleMessages: function () { |
| | | var vm = this; |
| | | vm.loginRules = { |
| | | mobile: [ |
| | | { required: true, message: vm.text("login.validation.usernameRequired", "请输入账号"), trigger: "blur" } |
| | | ], |
| | | password: [ |
| | | { required: true, message: vm.text("login.validation.passwordRequired", "请输入密码"), trigger: "blur" } |
| | | ] |
| | | }; |
| | | vm.mfaRules = { |
| | | code: [ |
| | | { required: true, message: vm.text("login.validation.mfaRequired", "请输入6位验证码"), trigger: "blur" }, |
| | | { |
| | | validator: function (rule, value, callback) { |
| | | if (!/^\d{6}$/.test(String(value || "").trim())) { |
| | | callback(new Error(vm.text("login.validation.mfaInvalid", "请输入6位数字验证码"))); |
| | | return; |
| | | } |
| | | callback(); |
| | | }, |
| | | trigger: "blur" |
| | | } |
| | | ] |
| | | }; |
| | | }, |
| | | initLanguageSwitch: function () { |
| | | var vm = this; |
| | | if (!window.WCS_I18N || typeof window.WCS_I18N.onReady !== "function") { |
| | | vm.refreshRuleMessages(); |
| | | return; |
| | | } |
| | | window.WCS_I18N.onReady(function (i18n) { |
| | | vm.localeOptions = i18n.getLocaleOptions(); |
| | | vm.currentLocale = i18n.getLocale(); |
| | | document.title = i18n.t("login.title"); |
| | | vm.refreshRuleMessages(); |
| | | vm.localeTick++; |
| | | }); |
| | | }, |
| | |
| | | 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; |
| | |
| | | }, |
| | | method: "POST", |
| | | success: function (res) { |
| | | var payload = res && res.data ? res.data : {}; |
| | | if (Number(res.code) === 200) { |
| | | localStorage.setItem("token", res.data.token); |
| | | localStorage.setItem("username", res.data.username); |
| | | window.location.href = "index.html"; |
| | | return; |
| | | } |
| | | vm.$message.error(res.msg || "登录失败"); |
| | | if (payload.mfaRequired) { |
| | | vm.openMfaDialog(payload); |
| | | return; |
| | | } |
| | | vm.finishLogin(payload); |
| | | return; |
| | | } |
| | | vm.$message.error(res.msg || vm.text("login.error.loginFailed", "登录失败")); |
| | | }, |
| | | error: function () { |
| | | vm.$message.error("登录失败"); |
| | | vm.$message.error(vm.text("login.error.loginFailed", "登录失败")); |
| | | }, |
| | | complete: function () { |
| | | vm.loginLoading = false; |
| | | } |
| | | }); |
| | | }, |
| | | openMfaDialog: function (payload) { |
| | | this.mfaPending = { |
| | | ticket: payload.mfaTicket || "", |
| | | username: payload.username || this.loginForm.mobile || "" |
| | | }; |
| | | this.mfaForm.code = ""; |
| | | this.mfaDialogVisible = true; |
| | | if (this.$refs.mfaForm) { |
| | | this.$nextTick(function () { |
| | | this.$refs.mfaForm.clearValidate(); |
| | | }); |
| | | } |
| | | }, |
| | | closeMfaDialog: function () { |
| | | this.mfaDialogVisible = false; |
| | | this.mfaLoading = false; |
| | | this.mfaPending = { |
| | | ticket: "", |
| | | username: "" |
| | | }; |
| | | this.mfaForm.code = ""; |
| | | if (this.$refs.mfaForm) { |
| | | this.$refs.mfaForm.clearValidate(); |
| | | } |
| | | }, |
| | | handleMfaLogin: function () { |
| | | var vm = this; |
| | | if (!vm.$refs.mfaForm) { |
| | | return; |
| | | } |
| | | vm.$refs.mfaForm.validate(function (valid) { |
| | | if (!valid) { |
| | | return false; |
| | | } |
| | | vm.submitMfaLogin(); |
| | | return true; |
| | | }); |
| | | }, |
| | | submitMfaLogin: function () { |
| | | var vm = this; |
| | | if (!vm.mfaPending.ticket) { |
| | | vm.$message.error(vm.text("login.error.mfaTicketExpired", "登录票据已失效,请重新登录")); |
| | | vm.closeMfaDialog(); |
| | | return; |
| | | } |
| | | vm.mfaLoading = true; |
| | | ajaxJson({ |
| | | url: baseUrl + "/login/mfa.action", |
| | | data: { |
| | | ticket: vm.mfaPending.ticket, |
| | | code: vm.mfaForm.code |
| | | }, |
| | | method: "POST", |
| | | success: function (res) { |
| | | if (Number(res.code) === 200) { |
| | | vm.finishLogin(res.data || {}); |
| | | return; |
| | | } |
| | | vm.$message.error(res.msg || vm.text("login.error.mfaFailed", "验证失败")); |
| | | }, |
| | | error: function () { |
| | | vm.$message.error(vm.text("login.error.mfaFailed", "验证失败")); |
| | | }, |
| | | complete: function () { |
| | | vm.mfaLoading = false; |
| | | } |
| | | }); |
| | | }, |
| | | 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、Edge 或 Safari"); |
| | | } |
| | | if (err && err.name === "NotAllowedError") { |
| | | return this.text(fallbackKey, fallbackText); |
| | | } |
| | | return this.text(fallbackKey, fallbackText); |
| | | }, |
| | | openTextDialog: function (title, label, text, tip) { |
| | | var pretty = ""; |
| | |
| | | textarea.select(); |
| | | document.execCommand("copy"); |
| | | document.body.removeChild(textarea); |
| | | this.$message.success("已复制到剪贴板"); |
| | | this.$message.success(this.text("login.dialog.copied", "已复制到剪贴板")); |
| | | } catch (err) { |
| | | this.$message.error("复制失败"); |
| | | this.$message.error(this.text("login.dialog.copyFailed", "复制失败")); |
| | | } |
| | | }, |
| | | copyText: function () { |
| | |
| | | var text = vm.textDialog.text || ""; |
| | | if (navigator.clipboard && navigator.clipboard.writeText) { |
| | | navigator.clipboard.writeText(text).then(function () { |
| | | vm.$message.success("已复制到剪贴板"); |
| | | vm.$message.success(vm.text("login.dialog.copied", "已复制到剪贴板")); |
| | | }).catch(function () { |
| | | vm.fallbackCopy(text); |
| | | }); |
| | |
| | | method: "GET", |
| | | success: function (res) { |
| | | if (Number(res.code) === 200) { |
| | | vm.openTextDialog("获取请求码", "请求码", res.msg || "", "请求码中已包含项目名称,直接发给许可证服务端即可。"); |
| | | vm.openTextDialog( |
| | | vm.text("login.requestCode.title", "获取请求码"), |
| | | vm.text("login.requestCode.label", "请求码"), |
| | | res.msg || "", |
| | | vm.text("login.requestCode.tip", "请求码中已包含项目名称,直接发给许可证服务端即可。") |
| | | ); |
| | | return; |
| | | } |
| | | vm.$message.error(res.msg || "获取请求码失败"); |
| | | vm.$message.error(res.msg || vm.text("login.error.requestCodeFailed", "获取请求码失败")); |
| | | }, |
| | | error: function () { |
| | | vm.$message.error("获取请求码失败"); |
| | | vm.$message.error(vm.text("login.error.requestCodeFailed", "获取请求码失败")); |
| | | } |
| | | }); |
| | | }, |
| | |
| | | url: baseUrl + "/license/getServerInfos", |
| | | method: "GET", |
| | | success: function (res) { |
| | | vm.openTextDialog("获取系统配置", "系统配置信息", res, "老项目仍可继续使用这份硬件信息 JSON 申请许可证。"); |
| | | vm.openTextDialog( |
| | | vm.text("login.serverInfo.title", "获取系统配置"), |
| | | vm.text("login.serverInfo.label", "系统配置信息"), |
| | | res, |
| | | vm.text("login.serverInfo.tip", "老项目仍可继续使用这份硬件信息 JSON 申请许可证。") |
| | | ); |
| | | }, |
| | | error: function () { |
| | | vm.$message.error("获取系统配置信息失败"); |
| | | vm.$message.error(vm.text("login.error.serverInfoFailed", "获取系统配置信息失败")); |
| | | } |
| | | }); |
| | | }, |
| | | submitLicense: function () { |
| | | var vm = this; |
| | | if (!vm.licenseBase64) { |
| | | vm.$message.warning("许可证内容不能为空"); |
| | | vm.$message.warning(vm.text("login.error.licenseEmpty", "许可证内容不能为空")); |
| | | return; |
| | | } |
| | | ajaxJson({ |
| | |
| | | if (Number(res.code) === 200) { |
| | | vm.uploadDialogVisible = false; |
| | | vm.licenseBase64 = ""; |
| | | vm.$message.success("许可证更新成功"); |
| | | vm.$message.success(vm.text("login.license.success", "许可证更新成功")); |
| | | return; |
| | | } |
| | | vm.$message.error(res.msg || "许可证更新失败"); |
| | | vm.$message.error(res.msg || vm.text("login.error.licenseUpdateFailed", "许可证更新失败")); |
| | | }, |
| | | error: function () { |
| | | vm.$message.error("许可证录入失败"); |
| | | vm.$message.error(vm.text("login.error.licenseImportFailed", "许可证录入失败")); |
| | | } |
| | | }); |
| | | }, |
| | | activateLicense: function () { |
| | | var vm = this; |
| | | vm.$confirm("确定执行一键激活吗?", "提示", { |
| | | vm.$confirm(vm.text("login.activate.confirm", "确定执行一键激活吗?"), vm.text("common.prompt", "提示"), { |
| | | type: "warning", |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消" |
| | | confirmButtonText: vm.text("common.ok", "确定"), |
| | | cancelButtonText: vm.text("login.mfa.cancel", "取消") |
| | | }).then(function () { |
| | | ajaxJson({ |
| | | url: baseUrl + "/license/activate", |
| | | method: "POST", |
| | | success: function (res) { |
| | | if (Number(res.code) === 200) { |
| | | vm.$message.success("激活成功"); |
| | | vm.$message.success(vm.text("login.activate.success", "激活成功")); |
| | | return; |
| | | } |
| | | vm.$message.error(res.msg || "激活失败"); |
| | | vm.$message.error(res.msg || vm.text("login.activate.failed", "激活失败")); |
| | | }, |
| | | error: function () { |
| | | vm.$message.error("激活失败"); |
| | | vm.$message.error(vm.text("login.activate.failed", "激活失败")); |
| | | } |
| | | }); |
| | | }).catch(function () { |
| | |
| | | method: "GET", |
| | | success: function (res) { |
| | | if (Number(res.code) === 200) { |
| | | vm.$alert(res.msg || "", "项目名称", { |
| | | confirmButtonText: "确定" |
| | | vm.$alert(res.msg || "", vm.text("login.projectName.title", "项目名称"), { |
| | | confirmButtonText: vm.text("common.ok", "确定") |
| | | }); |
| | | return; |
| | | } |
| | | vm.$message.error(res.msg || "获取项目名称失败"); |
| | | vm.$message.error(res.msg || vm.text("login.error.projectNameFailed", "获取项目名称失败")); |
| | | }, |
| | | error: function () { |
| | | vm.$message.error("获取项目名称失败"); |
| | | vm.$message.error(vm.text("login.error.projectNameFailed", "获取项目名称失败")); |
| | | } |
| | | }); |
| | | } |