(function () {
|
function ajaxJson(options) {
|
$.ajax(options);
|
}
|
|
new Vue({
|
el: "#app",
|
data: function () {
|
return {
|
localeTick: 0,
|
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: "",
|
label: "",
|
text: "",
|
tip: ""
|
},
|
loginRules: {
|
mobile: [
|
{ required: true, message: "请输入账号", trigger: "blur" }
|
],
|
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"
|
}
|
]
|
}
|
};
|
},
|
created: function () {
|
this.initLanguageSwitch();
|
},
|
methods: {
|
text: function (key, fallback) {
|
var localeTick = this.localeTick;
|
void localeTick;
|
if (window.WCS_I18N && typeof window.WCS_I18N.t === "function") {
|
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++;
|
});
|
},
|
handleLocaleChange: function () {
|
var vm = this;
|
if (!window.WCS_I18N || typeof window.WCS_I18N.setLocale !== "function") {
|
return;
|
}
|
window.WCS_I18N.setLocale(vm.currentLocale);
|
setTimeout(function () {
|
document.title = vm.text("login.title", "系统登录");
|
vm.localeTick++;
|
}, 0);
|
},
|
handleTitleClick: function () {
|
var vm = this;
|
vm.titleClickCount++;
|
if (vm.titleClickTimer) {
|
clearTimeout(vm.titleClickTimer);
|
}
|
if (vm.titleClickCount >= 3) {
|
vm.titleClickCount = 0;
|
vm.toolsDialogVisible = true;
|
return;
|
}
|
vm.titleClickTimer = setTimeout(function () {
|
vm.titleClickCount = 0;
|
}, 500);
|
},
|
handleLogin: function () {
|
var vm = this;
|
vm.$refs.loginForm.validate(function (valid) {
|
if (!valid) {
|
return false;
|
}
|
vm.submitLogin();
|
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;
|
ajaxJson({
|
url: baseUrl + "/login.action",
|
data: {
|
mobile: vm.loginForm.mobile,
|
password: hex_md5(vm.loginForm.password)
|
},
|
method: "POST",
|
success: function (res) {
|
var payload = res && res.data ? res.data : {};
|
if (Number(res.code) === 200) {
|
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.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 = "";
|
try {
|
pretty = typeof text === "string" ? text : JSON.stringify(text, null, 2);
|
} catch (e) {
|
pretty = String(text || "");
|
}
|
this.textDialog = {
|
title: title,
|
label: label,
|
text: pretty,
|
tip: tip || ""
|
};
|
this.textDialogVisible = true;
|
},
|
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(this.text("login.dialog.copied", "已复制到剪贴板"));
|
} catch (err) {
|
this.$message.error(this.text("login.dialog.copyFailed", "复制失败"));
|
}
|
},
|
copyText: function () {
|
var vm = this;
|
var text = vm.textDialog.text || "";
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
navigator.clipboard.writeText(text).then(function () {
|
vm.$message.success(vm.text("login.dialog.copied", "已复制到剪贴板"));
|
}).catch(function () {
|
vm.fallbackCopy(text);
|
});
|
return;
|
}
|
vm.fallbackCopy(text);
|
},
|
requestCode: function () {
|
var vm = this;
|
ajaxJson({
|
url: baseUrl + "/license/getRequestCode",
|
method: "GET",
|
success: function (res) {
|
if (Number(res.code) === 200) {
|
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.text("login.error.requestCodeFailed", "获取请求码失败"));
|
},
|
error: function () {
|
vm.$message.error(vm.text("login.error.requestCodeFailed", "获取请求码失败"));
|
}
|
});
|
},
|
getServerInfo: function () {
|
var vm = this;
|
ajaxJson({
|
url: baseUrl + "/license/getServerInfos",
|
method: "GET",
|
success: function (res) {
|
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.text("login.error.serverInfoFailed", "获取系统配置信息失败"));
|
}
|
});
|
},
|
submitLicense: function () {
|
var vm = this;
|
if (!vm.licenseBase64) {
|
vm.$message.warning(vm.text("login.error.licenseEmpty", "许可证内容不能为空"));
|
return;
|
}
|
ajaxJson({
|
url: baseUrl + "/license/updateLicense",
|
method: "POST",
|
contentType: "application/json;charset=UTF-8",
|
data: JSON.stringify({ license: vm.licenseBase64 }),
|
success: function (res) {
|
if (Number(res.code) === 200) {
|
vm.uploadDialogVisible = false;
|
vm.licenseBase64 = "";
|
vm.$message.success(vm.text("login.license.success", "许可证更新成功"));
|
return;
|
}
|
vm.$message.error(res.msg || vm.text("login.error.licenseUpdateFailed", "许可证更新失败"));
|
},
|
error: function () {
|
vm.$message.error(vm.text("login.error.licenseImportFailed", "许可证录入失败"));
|
}
|
});
|
},
|
activateLicense: function () {
|
var vm = this;
|
vm.$confirm(vm.text("login.activate.confirm", "确定执行一键激活吗?"), vm.text("common.prompt", "提示"), {
|
type: "warning",
|
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.text("login.activate.success", "激活成功"));
|
return;
|
}
|
vm.$message.error(res.msg || vm.text("login.activate.failed", "激活失败"));
|
},
|
error: function () {
|
vm.$message.error(vm.text("login.activate.failed", "激活失败"));
|
}
|
});
|
}).catch(function () {
|
});
|
},
|
getProjectName: function () {
|
var vm = this;
|
ajaxJson({
|
url: baseUrl + "/license/getProjectName",
|
method: "GET",
|
success: function (res) {
|
if (Number(res.code) === 200) {
|
vm.$alert(res.msg || "", vm.text("login.projectName.title", "项目名称"), {
|
confirmButtonText: vm.text("common.ok", "确定")
|
});
|
return;
|
}
|
vm.$message.error(res.msg || vm.text("login.error.projectNameFailed", "获取项目名称失败"));
|
},
|
error: function () {
|
vm.$message.error(vm.text("login.error.projectNameFailed", "获取项目名称失败"));
|
}
|
});
|
}
|
}
|
});
|
})();
|