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

---
 src/main/resources/i18n/zh-CN/messages.properties |   40 ++++++++++
 src/main/resources/i18n/en-US/messages.properties |   40 ++++++++++
 src/main/webapp/views/login.html                  |   53 ++++++-------
 src/main/webapp/static/js/login/login.js          |  103 ++++++++++++++++++-------
 4 files changed, 177 insertions(+), 59 deletions(-)

diff --git a/src/main/resources/i18n/en-US/messages.properties b/src/main/resources/i18n/en-US/messages.properties
index aeedcc1..2d7e9ec 100644
--- a/src/main/resources/i18n/en-US/messages.properties
+++ b/src/main/resources/i18n/en-US/messages.properties
@@ -16,6 +16,17 @@
 common.workPage=Work Page
 common.businessPage=Business Page
 login.title=WCS System V3.0
+login.subtitle=Please enter your account and password to access the system.
+login.hero.title=The WCS keeps device scheduling, task execution, and on-site monitoring within the same business workflow.
+login.hero.subtitle=The Warehouse Control System is built for automated AS/RS execution, coordinating cranes, RGVs, conveyor stations, task commands, location status, and log tracing through unified dispatch and visualization. It helps warehouse operations stay stable, traceable, and collaborative. Zhejiang Zhongyang Warehouse Technology Co., Ltd. focuses on automated AS/RS and intelligent intralogistics systems, covering solution design, software control, equipment integration, and project delivery.
+login.hero.metric.dispatch.title=Dispatch
+login.hero.metric.dispatch.desc=Unified orchestration of on-site execution tasks
+login.hero.metric.trace.title=Traceability
+login.hero.metric.trace.desc=End-to-end traceability across jobs, devices, and logs
+login.hero.metric.integration.title=Integration
+login.hero.metric.integration.desc=Integrates WMS, devices, and business rules
+login.hero.company.name=Zhejiang Zhongyang Warehouse Technology Co., Ltd.
+login.hero.company.solution=Automated AS/RS and intelligent logistics system solutions
 login.username=Account
 login.password=Password
 login.submit=Sign In
@@ -37,6 +48,35 @@
 login.serverInfo.title=System Configuration
 login.serverInfo.label=System Configuration
 login.serverInfo.tip=Legacy projects can still use this hardware JSON to apply for a license.
+login.dialog.close=Close
+login.mfa.title=MFA Verification
+login.mfa.tip=Account and password verified. Enter the 6-digit code from your authenticator app to continue signing in.
+login.mfa.currentAccount=Current account:
+login.mfa.codeLabel=Verification Code
+login.mfa.codePlaceholder=Please enter the 6-digit code
+login.mfa.cancel=Cancel
+login.mfa.submit=Verify and Sign In
+login.license.label=License Base64
+login.license.tip=Paste the full license field returned by the license service here.
+login.license.submit=Submit
+login.license.success=License updated successfully
+login.validation.usernameRequired=Please enter the account
+login.validation.passwordRequired=Please enter the password
+login.validation.mfaRequired=Please enter the 6-digit verification code
+login.validation.mfaInvalid=Please enter a 6-digit numeric verification code
+login.error.loginFailed=Login failed
+login.error.mfaTicketExpired=The login ticket has expired. Please sign in again
+login.error.mfaFailed=Verification failed
+login.error.requestCodeFailed=Failed to get request code
+login.error.serverInfoFailed=Failed to get system configuration
+login.error.licenseEmpty=License content cannot be empty
+login.error.licenseUpdateFailed=Failed to update license
+login.error.licenseImportFailed=Failed to import license
+login.activate.confirm=Are you sure you want to activate now?
+login.activate.success=Activation successful
+login.activate.failed=Activation failed
+login.projectName.title=Project Name
+login.error.projectNameFailed=Failed to get project name
 index.searchMenu=Search menu
 index.noMatchedMenu=No matching menus
 index.noAvailableMenu=No available menus for current account
diff --git a/src/main/resources/i18n/zh-CN/messages.properties b/src/main/resources/i18n/zh-CN/messages.properties
index 19a8d5d..3c465bd 100644
--- a/src/main/resources/i18n/zh-CN/messages.properties
+++ b/src/main/resources/i18n/zh-CN/messages.properties
@@ -16,6 +16,17 @@
 common.workPage=宸ヤ綔椤甸潰
 common.businessPage=涓氬姟椤甸潰
 login.title=WCS绯荤粺V3.0
+login.subtitle=璇疯緭鍏ヨ处鍙峰拰瀵嗙爜杩涘叆绯荤粺銆�
+login.hero.title=WCS绯荤粺璁╄澶囪皟搴︺�佷换鍔℃墽琛屼笌鐜板満鐩戞帶淇濇寔鍦ㄥ悓涓�濂椾笟鍔¢摼璺腑銆�
+login.hero.subtitle=Warehouse Control System 闈㈠悜鑷姩鍖栫珛浣撲粨搴撶幇鍦烘墽琛屽眰锛屽洿缁曞爢鍨涙満銆丷GV銆佽緭閫佺珯鍙般�佷换鍔℃寚浠ゃ�佸簱浣嶇姸鎬佸拰鏃ュ織杩借釜杩涜缁熶竴璋冨害涓庡彲瑙嗗寲绠$悊锛屽府鍔╀粨鍌ㄧ郴缁熷疄鐜扮ǔ瀹氥�佸彲杩芥函銆佸彲鑱斿姩鐨勪綔涓氭帶鍒躲�傛禉姹熶腑鎵珛搴撴妧鏈湁闄愬叕鍙搁暱鏈熶笓娉ㄤ簬鑷姩鍖栫珛浣撲粨搴撲笌鏅鸿兘鐗╂祦绯荤粺寤鸿锛岃鐩栨柟妗堣璁°�佽蒋浠舵帶鍒躲�佽澶囬泦鎴愪笌椤圭洰瀹炴柦浜や粯銆�
+login.hero.metric.dispatch.title=璋冨害
+login.hero.metric.dispatch.desc=缁熶竴缂栨帓鐜板満鎵ц浠诲姟
+login.hero.metric.trace.title=杩芥函
+login.hero.metric.trace.desc=浣滀笟銆佽澶囥�佹棩蹇楀叏閾捐矾鐣欑棔
+login.hero.metric.integration.title=闆嗘垚
+login.hero.metric.integration.desc=瀵规帴WMS銆佽澶囦笌涓氬姟瑙勫垯
+login.hero.company.name=娴欐睙涓壃绔嬪簱鎶�鏈湁闄愬叕鍙�
+login.hero.company.solution=鑷姩鍖栫珛浣撲粨搴撲笌鏅鸿兘鐗╂祦绯荤粺瑙e喅鏂规
 login.username=璐﹀彿
 login.password=瀵嗙爜
 login.submit=鐧诲綍
@@ -37,6 +48,35 @@
 login.serverInfo.title=鑾峰彇绯荤粺閰嶇疆
 login.serverInfo.label=绯荤粺閰嶇疆淇℃伅
 login.serverInfo.tip=鑰侀」鐩粛鍙户缁娇鐢ㄨ繖浠界‖浠朵俊鎭� JSON 鐢宠璁稿彲璇併��
+login.dialog.close=鍏抽棴
+login.mfa.title=MFA浜屾楠岃瘉
+login.mfa.tip=璐﹀彿瀵嗙爜宸查�氳繃锛岃杈撳叆韬唤楠岃瘉鍣ㄤ腑鐨� 6 浣嶅姩鎬侀獙璇佺爜鍚庣户缁櫥褰曘��
+login.mfa.currentAccount=褰撳墠璐﹀彿锛�
+login.mfa.codeLabel=楠岃瘉鐮�
+login.mfa.codePlaceholder=璇疯緭鍏�6浣嶅姩鎬佺爜
+login.mfa.cancel=鍙栨秷
+login.mfa.submit=楠岃瘉骞剁櫥褰�
+login.license.label=璁稿彲璇� Base64
+login.license.tip=灏嗚鍙瘉鏈嶅姟绔繑鍥炵殑 license 瀛楁瀹屾暣绮樿创鍒拌繖閲屻��
+login.license.submit=鎻愪氦
+login.license.success=璁稿彲璇佹洿鏂版垚鍔�
+login.validation.usernameRequired=璇疯緭鍏ヨ处鍙�
+login.validation.passwordRequired=璇疯緭鍏ュ瘑鐮�
+login.validation.mfaRequired=璇疯緭鍏�6浣嶉獙璇佺爜
+login.validation.mfaInvalid=璇疯緭鍏�6浣嶆暟瀛楅獙璇佺爜
+login.error.loginFailed=鐧诲綍澶辫触
+login.error.mfaTicketExpired=鐧诲綍绁ㄦ嵁宸插け鏁堬紝璇烽噸鏂扮櫥褰�
+login.error.mfaFailed=楠岃瘉澶辫触
+login.error.requestCodeFailed=鑾峰彇璇锋眰鐮佸け璐�
+login.error.serverInfoFailed=鑾峰彇绯荤粺閰嶇疆淇℃伅澶辫触
+login.error.licenseEmpty=璁稿彲璇佸唴瀹逛笉鑳戒负绌�
+login.error.licenseUpdateFailed=璁稿彲璇佹洿鏂板け璐�
+login.error.licenseImportFailed=璁稿彲璇佸綍鍏ュけ璐�
+login.activate.confirm=纭畾鎵ц涓�閿縺娲诲悧锛�
+login.activate.success=婵�娲绘垚鍔�
+login.activate.failed=婵�娲诲け璐�
+login.projectName.title=椤圭洰鍚嶇О
+login.error.projectNameFailed=鑾峰彇椤圭洰鍚嶇О澶辫触
 index.searchMenu=鎼滅储鑿滃崟
 index.noMatchedMenu=娌℃湁鍖归厤鑿滃崟
 index.noAvailableMenu=褰撳墠璐﹀彿娌℃湁鍙敤鑿滃崟
diff --git a/src/main/webapp/static/js/login/login.js b/src/main/webapp/static/js/login/login.js
index 6365e4c..9a7b1d0 100644
--- a/src/main/webapp/static/js/login/login.js
+++ b/src/main/webapp/static/js/login/login.js
@@ -69,19 +69,50 @@
                 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++;
                 });
             },
@@ -138,13 +169,13 @@
                                 vm.openMfaDialog(payload);
                                 return;
                             }
-                            vm.finishLogin(payload);
-                            return;
-                        }
-                        vm.$message.error(res.msg || "鐧诲綍澶辫触");
+                        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;
@@ -192,7 +223,7 @@
             submitMfaLogin: function () {
                 var vm = this;
                 if (!vm.mfaPending.ticket) {
-                    vm.$message.error("鐧诲綍绁ㄦ嵁宸插け鏁堬紝璇烽噸鏂扮櫥褰�");
+                    vm.$message.error(vm.text("login.error.mfaTicketExpired", "鐧诲綍绁ㄦ嵁宸插け鏁堬紝璇烽噸鏂扮櫥褰�"));
                     vm.closeMfaDialog();
                     return;
                 }
@@ -209,10 +240,10 @@
                             vm.finishLogin(res.data || {});
                             return;
                         }
-                        vm.$message.error(res.msg || "楠岃瘉澶辫触");
+                        vm.$message.error(res.msg || vm.text("login.error.mfaFailed", "楠岃瘉澶辫触"));
                     },
                     error: function () {
-                        vm.$message.error("楠岃瘉澶辫触");
+                        vm.$message.error(vm.text("login.error.mfaFailed", "楠岃瘉澶辫触"));
                     },
                     complete: function () {
                         vm.mfaLoading = false;
@@ -250,9 +281,9 @@
                     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 () {
@@ -260,7 +291,7 @@
                 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);
                     });
@@ -275,13 +306,18 @@
                     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", "鑾峰彇璇锋眰鐮佸け璐�"));
                     }
                 });
             },
@@ -291,17 +327,22 @@
                     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({
@@ -313,35 +354,35 @@
                         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 () {
@@ -354,15 +395,15 @@
                     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", "鑾峰彇椤圭洰鍚嶇О澶辫触"));
                     }
                 });
             }
diff --git a/src/main/webapp/views/login.html b/src/main/webapp/views/login.html
index 1574206..47918db 100644
--- a/src/main/webapp/views/login.html
+++ b/src/main/webapp/views/login.html
@@ -454,37 +454,34 @@
     <div class="login-layout">
         <section class="hero-panel animate__animated animate__fadeInLeft">
             <div class="brand-chip">Zoneyung WCS</div>
-            <div class="hero-title">WCS绯荤粺璁╄澶囪皟搴︺�佷换鍔℃墽琛屼笌鐜板満鐩戞帶淇濇寔鍦ㄥ悓涓�濂椾笟鍔¢摼璺腑銆�</div>
+            <div class="hero-title">{{ text('login.hero.title', 'WCS绯荤粺璁╄澶囪皟搴︺�佷换鍔℃墽琛屼笌鐜板満鐩戞帶淇濇寔鍦ㄥ悓涓�濂椾笟鍔¢摼璺腑銆�') }}</div>
             <div class="hero-subtitle">
-                Warehouse Control System 闈㈠悜鑷姩鍖栫珛浣撲粨搴撶幇鍦烘墽琛屽眰锛屽洿缁曞爢鍨涙満銆丷GV銆佽緭閫佺珯鍙般�佷换鍔℃寚浠ゃ��
-                搴撲綅鐘舵�佸拰鏃ュ織杩借釜杩涜缁熶竴璋冨害涓庡彲瑙嗗寲绠$悊锛屽府鍔╀粨鍌ㄧ郴缁熷疄鐜扮ǔ瀹氥�佸彲杩芥函銆佸彲鑱斿姩鐨勪綔涓氭帶鍒躲��
-                娴欐睙涓壃绔嬪簱鎶�鏈湁闄愬叕鍙搁暱鏈熶笓娉ㄤ簬鑷姩鍖栫珛浣撲粨搴撲笌鏅鸿兘鐗╂祦绯荤粺寤鸿锛岃鐩栨柟妗堣璁°�佽蒋浠舵帶鍒躲��
-                璁惧闆嗘垚涓庨」鐩疄鏂戒氦浠樸��
+                {{ text('login.hero.subtitle', 'Warehouse Control System 闈㈠悜鑷姩鍖栫珛浣撲粨搴撶幇鍦烘墽琛屽眰锛屽洿缁曞爢鍨涙満銆丷GV銆佽緭閫佺珯鍙般�佷换鍔℃寚浠ゃ�佸簱浣嶇姸鎬佸拰鏃ュ織杩借釜杩涜缁熶竴璋冨害涓庡彲瑙嗗寲绠$悊锛屽府鍔╀粨鍌ㄧ郴缁熷疄鐜扮ǔ瀹氥�佸彲杩芥函銆佸彲鑱斿姩鐨勪綔涓氭帶鍒躲�傛禉姹熶腑鎵珛搴撴妧鏈湁闄愬叕鍙搁暱鏈熶笓娉ㄤ簬鑷姩鍖栫珛浣撲粨搴撲笌鏅鸿兘鐗╂祦绯荤粺寤鸿锛岃鐩栨柟妗堣璁°�佽蒋浠舵帶鍒躲�佽澶囬泦鎴愪笌椤圭洰瀹炴柦浜や粯銆�') }}
             </div>
             <div class="hero-metrics">
                 <div class="metric-card">
-                    <div class="metric-value">璋冨害</div>
-                    <div class="metric-label">缁熶竴缂栨帓鐜板満鎵ц浠诲姟</div>
+                    <div class="metric-value">{{ text('login.hero.metric.dispatch.title', '璋冨害') }}</div>
+                    <div class="metric-label">{{ text('login.hero.metric.dispatch.desc', '缁熶竴缂栨帓鐜板満鎵ц浠诲姟') }}</div>
                 </div>
                 <div class="metric-card">
-                    <div class="metric-value">杩芥函</div>
-                    <div class="metric-label">浣滀笟銆佽澶囥�佹棩蹇楀叏閾捐矾鐣欑棔</div>
+                    <div class="metric-value">{{ text('login.hero.metric.trace.title', '杩芥函') }}</div>
+                    <div class="metric-label">{{ text('login.hero.metric.trace.desc', '浣滀笟銆佽澶囥�佹棩蹇楀叏閾捐矾鐣欑棔') }}</div>
                 </div>
                 <div class="metric-card">
-                    <div class="metric-value">闆嗘垚</div>
-                    <div class="metric-label">瀵规帴WMS銆佽澶囦笌涓氬姟瑙勫垯</div>
+                    <div class="metric-value">{{ text('login.hero.metric.integration.title', '闆嗘垚') }}</div>
+                    <div class="metric-label">{{ text('login.hero.metric.integration.desc', '瀵规帴WMS銆佽澶囦笌涓氬姟瑙勫垯') }}</div>
                 </div>
             </div>
             <div class="hero-footer">
-                <span>娴欐睙涓壃绔嬪簱鎶�鏈湁闄愬叕鍙�</span>
-                <span>鑷姩鍖栫珛浣撲粨搴撲笌鏅鸿兘鐗╂祦绯荤粺瑙e喅鏂规</span>
+                <span>{{ text('login.hero.company.name', '娴欐睙涓壃绔嬪簱鎶�鏈湁闄愬叕鍙�') }}</span>
+                <span>{{ text('login.hero.company.solution', '鑷姩鍖栫珛浣撲粨搴撲笌鏅鸿兘鐗╂祦绯荤粺瑙e喅鏂规') }}</span>
             </div>
         </section>
 
         <section class="login-card animate__animated animate__fadeInUp">
             <div class="login-head">
                 <h1 class="login-title" @click="handleTitleClick">{{ text('login.title', 'WCS绯荤粺V3.0') }}</h1>
-                <div class="login-subtitle">璇疯緭鍏ヨ处鍙峰拰瀵嗙爜杩涘叆绯荤粺銆�</div>
+                <div class="login-subtitle">{{ text('login.subtitle', '璇疯緭鍏ヨ处鍙峰拰瀵嗙爜杩涘叆绯荤粺銆�') }}</div>
             </div>
             <div class="login-body">
                 <el-form ref="loginForm" class="login-form" :model="loginForm" :rules="loginRules" @submit.native.prevent>
@@ -506,7 +503,7 @@
         </section>
     </div>
 
-    <el-dialog class="tools-dialog" title="绯荤粺宸ュ叿" :visible.sync="toolsDialogVisible" width="560px" :close-on-click-modal="true" append-to-body>
+    <el-dialog class="tools-dialog" :title="text('login.tools.title', '绯荤粺宸ュ叿')" :visible.sync="toolsDialogVisible" width="560px" :close-on-click-modal="true" append-to-body>
         <div class="tool-group">
             <div class="tool-title">{{ text('login.tools.recommended', '鎺ㄨ崘鎿嶄綔') }}</div>
             <div class="tool-actions">
@@ -527,22 +524,22 @@
 
     <el-dialog
         class="mfa-dialog"
-        title="MFA浜屾楠岃瘉"
+        :title="text('login.mfa.title', 'MFA浜屾楠岃瘉')"
         :visible.sync="mfaDialogVisible"
         width="420px"
         :close-on-click-modal="false"
         @close="closeMfaDialog"
         append-to-body>
-        <div class="mfa-tip">璐﹀彿瀵嗙爜宸查�氳繃锛岃杈撳叆韬唤楠岃瘉鍣ㄤ腑鐨� 6 浣嶅姩鎬侀獙璇佺爜鍚庣户缁櫥褰曘��</div>
-        <div class="mfa-account">褰撳墠璐﹀彿锛�<strong>{{ mfaPending.username || loginForm.mobile || '--' }}</strong></div>
+        <div class="mfa-tip">{{ text('login.mfa.tip', '璐﹀彿瀵嗙爜宸查�氳繃锛岃杈撳叆韬唤楠岃瘉鍣ㄤ腑鐨� 6 浣嶅姩鎬侀獙璇佺爜鍚庣户缁櫥褰曘��') }}</div>
+        <div class="mfa-account">{{ text('login.mfa.currentAccount', '褰撳墠璐﹀彿锛�') }}<strong>{{ mfaPending.username || loginForm.mobile || '--' }}</strong></div>
         <el-form ref="mfaForm" :model="mfaForm" :rules="mfaRules" label-width="82px" size="small" @submit.native.prevent>
-            <el-form-item label="楠岃瘉鐮�" prop="code">
-                <el-input v-model.trim="mfaForm.code" maxlength="6" placeholder="璇疯緭鍏�6浣嶅姩鎬佺爜" @keyup.enter.native="handleMfaLogin"></el-input>
+            <el-form-item :label="text('login.mfa.codeLabel', '楠岃瘉鐮�')" prop="code">
+                <el-input v-model.trim="mfaForm.code" maxlength="6" :placeholder="text('login.mfa.codePlaceholder', '璇疯緭鍏�6浣嶅姩鎬佺爜')" @keyup.enter.native="handleMfaLogin"></el-input>
             </el-form-item>
         </el-form>
         <div class="mfa-footer">
-            <el-button @click="closeMfaDialog">鍙栨秷</el-button>
-            <el-button type="primary" :loading="mfaLoading" @click="handleMfaLogin">楠岃瘉骞剁櫥褰�</el-button>
+            <el-button @click="closeMfaDialog">{{ text('login.mfa.cancel', '鍙栨秷') }}</el-button>
+            <el-button type="primary" :loading="mfaLoading" @click="handleMfaLogin">{{ text('login.mfa.submit', '楠岃瘉骞剁櫥褰�') }}</el-button>
         </div>
     </el-dialog>
 
@@ -551,18 +548,18 @@
         <div v-if="textDialog.tip" class="dialog-text-tip">{{ textDialog.tip }}</div>
         <el-input v-model="textDialog.text" type="textarea" :rows="10" readonly></el-input>
         <div class="text-footer">
-            <el-button @click="textDialogVisible = false">鍏抽棴</el-button>
+            <el-button @click="textDialogVisible = false">{{ text('login.dialog.close', '鍏抽棴') }}</el-button>
             <el-button type="primary" @click="copyText">{{ text('copy', '澶嶅埗') }}</el-button>
         </div>
     </el-dialog>
 
-    <el-dialog class="upload-dialog" title="褰曞叆璁稿彲璇�" :visible.sync="uploadDialogVisible" width="760px" append-to-body>
-        <div class="dialog-text-label">璁稿彲璇� Base64</div>
-        <div class="dialog-text-tip">灏嗚鍙瘉鏈嶅姟绔繑鍥炵殑 license 瀛楁瀹屾暣绮樿创鍒拌繖閲屻��</div>
+    <el-dialog class="upload-dialog" :title="text('login.tools.uploadLicense', '褰曞叆璁稿彲璇�')" :visible.sync="uploadDialogVisible" width="760px" append-to-body>
+        <div class="dialog-text-label">{{ text('login.license.label', '璁稿彲璇� Base64') }}</div>
+        <div class="dialog-text-tip">{{ text('login.license.tip', '灏嗚鍙瘉鏈嶅姟绔繑鍥炵殑 license 瀛楁瀹屾暣绮樿创鍒拌繖閲屻��') }}</div>
         <el-input v-model.trim="licenseBase64" type="textarea" :rows="10"></el-input>
         <div class="upload-footer">
-            <el-button @click="uploadDialogVisible = false">鍙栨秷</el-button>
-            <el-button type="primary" @click="submitLicense">鎻愪氦</el-button>
+            <el-button @click="uploadDialogVisible = false">{{ text('login.mfa.cancel', '鍙栨秷') }}</el-button>
+            <el-button type="primary" @click="submitLicense">{{ text('login.license.submit', '鎻愪氦') }}</el-button>
         </div>
     </el-dialog>
 </div>

--
Gitblit v1.9.1