Przeglądaj źródła

中英文切换3.0

yes-yes-yes-k 1 miesiąc temu
rodzic
commit
ffd7824faa

+ 105 - 187
src/components/layout/AppLayout.vue

@@ -14,19 +14,18 @@
       :class="{ 'transparent-header': isSpecialBg }"
     >
       <div class="logo-title-row">
-        <!-- 左侧:Logo和标题 -->
+        <!-- 左侧:Logo 和标题 -->
         <div class="left-section">
           <img src="@/assets/logo.png" alt="Logo" class="logo" />
           <span class="project-name" :class="{ 'light-text': isSpecialBg }">
-            <!-- 土壤酸化智能预测专家系统 -->
-             {{ t('Header.title') }}
+            {{ t('Header.title') }}
           </span>
         </div>
 
         <!-- 右侧:用户信息 -->
         <div class="right-section" v-if="!isSelectCity">
           <div class="user-info-row">
-            <div class="lang-buttons" v-if="true"> <!-- 设为false隐藏,选择一种方式即可 -->
+            <div class="lang-buttons">
             <!-- 语言切换按钮 -->
             <button 
               class="lang-toggle-btn"
@@ -52,12 +51,12 @@
               </span>
               <template #dropdown>
                 <el-dropdown-menu>
-                  <el-dropdown-item disabled
-                    > {{ t('Header.username') }} {{ userInfo.name }}</el-dropdown-item
-                  >
-                  <el-dropdown-item divided @click="handleLogout"
-                    > {{ t('Header.logout') }} </el-dropdown-item
-                  >
+                  <el-dropdown-item disabled>
+                    {{ t('Header.username') }}: {{ userInfo.name }}
+                  </el-dropdown-item>
+                  <el-dropdown-item divided @click="handleLogout">
+                    {{ t('Header.logout') }}
+                  </el-dropdown-item>
                 </el-dropdown-menu>
               </template>
             </el-dropdown>
@@ -122,12 +121,11 @@
 import { ref, reactive, computed, watch, defineAsyncComponent } from "vue";
 import { useRouter, useRoute } from "vue-router";
 import { useTokenStore } from "@/stores/mytoken";
-import { ElMessageBox, ElMessage } from "element-plus"; // 确保导入ElMessage
+import { ElMessageBox, ElMessage } from "element-plus";
 import { logout } from "@/API/users";
 import { useI18n } from 'vue-i18n'
 
-
-const { t,locale } =useI18n()
+const { t, locale } = useI18n()
 const currentLang = ref(locale.value || 'zh')
 
 // 切换语言
@@ -143,7 +141,7 @@ const changeLanguage = () => {
 
 // 按钮点击切换
 const toggleLanguage = () => {
-  currentLang.value = currentLang.value === 'zh' ?'en':'zh'
+  currentLang.value = currentLang.value === 'zh' ? 'en' : 'zh'
   changeLanguage()
 }
 
@@ -152,7 +150,6 @@ watch(locale, (newVal) => {
   currentLang.value = newVal
 })
 
-
 const router = useRouter();
 const route = useRoute();
 const tokenStore = useTokenStore();
@@ -161,11 +158,10 @@ const currentBgImage = ref("");
 // ============ 新增状态 ============
 const showGlobalMessage = ref(false);
 const globalMessage = ref("");
-const messageType = ref("success"); // 消息类型: success/error
+const messageType = ref("success");
 
 currentBgImage.value = `url(${new URL('../../assets/bg/background.jpg', import.meta.url).href})`;
 
-// 是否特殊背景(始终返回true → 所有页面应用特殊背景样式)
 const isSpecialBg = computed(() => true);
 
 const backgroundStyle = computed(() => ({
@@ -186,152 +182,79 @@ const userInfo = reactive({
 });
 
 const tabs = computed(() => {
-
- if (userInfo.type === "admin") {
-
-  return [
-
-   {
-
-    name: "dataManagement",
-
-    label: t('Menu.dataManagement'),
-
-    icon: "el-icon-folder",
-
-    routes: [
-
-     "/soilAcidReductionData",
-
-     "/soilAcidificationData",
-
-     "/crossSectionSampleData",
-
-     "/irrigationWaterInputFluxData",
-
-     "/heavyMetalEnterpriseData",
-
-     "/atmosphericSampleData",
-
-    ],
-
-   },
-
-   {
-
-    name: "infoManagement",
-
-    label: t('Menu.infoManagement'),
-
-    icon: "el-icon-document",
-
-    routes: ["/IntroductionUpdate"],
-
-   },
-
-   {
-
-    name: "modelManagement",
-
-    label: t('Menu.modelManagement'),
-
-    icon: "el-icon-cpu",
-
-    routes: ["/ModelSelection", "/thres", "/ModelTrain"],
-
-   },
-
-   {
-
-    name: "userManagement",
-
-    label: t('Menu.userManagement'),
-
-    icon: "el-icon-user",
-
-    routes: ["/UserManagement"],
-
-   },
-
-  ];
-
- } else {
-
-  return [
-
-   {
-
-    name: "introduction",
-
-    label: t('Menu.swIntroduce'),
-
-    icon: "el-icon-info-filled",
-
-    routes: ["/SoilPro", "/Overview", "/ResearchFindings", "/Unit"],
-
-   },
-
-   {
-   name: "acidmodelmap",
-
-    label: t('Menu.mapcalulate'),
-
-    icon: "el-icon-data-analysis",
-
-    routes: ["/shaoguan_acidmodelmap","nanxiong_acidmodelmap"],
-
-   },
-
-   {
-
-    name: "Calculation",
-
-    label: t('Menu.reversionmodel'),
-
-    icon: "el-icon-data-analysis",
-
-    routes: ["/Calculation", "/SoilAcidReductionIterativeEvolution"],
-
-   },
-
-   {
-
-    name: "AcidNeutralizationModel",
-
-    label: t('Menu.reducemodel'),
-
-    icon: "el-icon-data-analysis",
-
-    routes: ["/AcidNeutralizationModel", "/SoilAcidificationIterativeEvolution"],
-
-   },
-
-   {
-
-    name: "dataStatistics",
-
-    label: t('Menu.dataStatistics'),
-
-    icon: "el-icon-pie-chart",
-
-    routes: [
-
-     "/DetectionStatistics",
-
-     "/FarmlandPollutionStatistics",
-
-     "/LandClutivatesStatistics",
-
-     "/SoilacidificationStatistics",
-
-   ],
-
-   },
-
-  ];
-
- }
-
+  if (userInfo.type === "admin") {
+    return [
+      {
+        name: "dataManagement",
+        label: t('Menu.dataManagement'),
+        icon: "el-icon-folder",
+        routes: [
+          "/soilAcidReductionData",
+          "/soilAcidificationData",
+          "/crossSectionSampleData",
+          "/irrigationWaterInputFluxData",
+          "/heavyMetalEnterpriseData",
+          "/atmosphericSampleData",
+        ],
+      },
+      {
+        name: "infoManagement",
+        label: t('Menu.infoManagement'),
+        icon: "el-icon-document",
+        routes: ["/IntroductionUpdate"],
+      },
+      {
+        name: "modelManagement",
+        label: t('Menu.modelManagement'),
+        icon: "el-icon-cpu",
+        routes: ["/ModelSelection", "/thres", "/ModelTrain"],
+      },
+      {
+        name: "userManagement",
+        label: t('Menu.userManagement'),
+        icon: "el-icon-user",
+        routes: ["/UserManagement"],
+      },
+    ];
+  } else {
+    return [
+      {
+        name: "introduction",
+        label: t('Menu.swIntroduce'),
+        icon: "el-icon-info-filled",
+        routes: ["/SoilPro", "/Overview", "/ResearchFindings", "/Unit"],
+      },
+      {
+        name: "acidmodelmap",
+        label: t('Menu.mapcalulate'),
+        icon: "el-icon-data-analysis",
+        routes: ["/shaoguan_acidmodelmap","nanxiong_acidmodelmap"],
+      },
+      {
+        name: "Calculation",
+        label: t('Menu.reversionmodel'),
+        icon: "el-icon-data-analysis",
+        routes: ["/Calculation", "/SoilAcidReductionIterativeEvolution"],
+      },
+      {
+        name: "AcidNeutralizationModel",
+        label: t('Menu.reducemodel'),
+        icon: "el-icon-data-analysis",
+        routes: ["/AcidNeutralizationModel", "/SoilAcidificationIterativeEvolution"],
+      },
+      {
+        name: "dataStatistics",
+        label: t('Menu.dataStatistics'),
+        icon: "el-icon-pie-chart",
+        routes: [
+          "/DetectionStatistics",
+          "/FarmlandPollutionStatistics",
+          "/LandClutivatesStatistics",
+          "/SoilacidificationStatistics",
+        ],
+      },
+    ];
+  }
 });
 
 const activeName = ref(tabs.value[0]?.name || "");
@@ -356,13 +279,11 @@ watch(
   { immediate: true }
 );
 
-// 点击 tab
 const activeAsideTab = ref(activeName.value || "");
 const handleClick = (tab: any, _event?: Event) => {
   activeAsideTab.value = tab.name || tab.props?.name;
 };
 
-// 动态加载侧边栏
 const AsideComponent = computed(() => {
   return [
     "dataManagement",
@@ -374,50 +295,47 @@ const AsideComponent = computed(() => {
     : defineAsyncComponent(() => import("./AppAside.vue"));
 });
 
-// 是否显示侧边栏
 const showAside = computed(
   () =>
     !isFullScreen.value && !["cropRiskAssessment"].includes(activeName.value)
 );
 
-// ============ 显示全局消息 ============
+const mainStyle = computed(() => ({
+  padding: ["mapView", "infoManagement"].includes(activeName.value)
+    ? "0"
+    : "20px",
+  overflow: "hidden",
+}));
 
-// 登出逻辑
+// 登出逻辑 - 支持多语言
 const handleLogout = async () => {
   try {
-    await ElMessageBox.confirm("确定要退出登录吗?", "提示", {
-      confirmButtonText: "确定",
-      cancelButtonText: "取消",
-      type: "warning",
-    });
+    await ElMessageBox.confirm(
+      t('logout.confirmMessage'), 
+      t('logout.confirmTitle'),
+      {
+        confirmButtonText: t('logout.confirmButton'),
+        cancelButtonText: t('logout.cancelButton'),
+        type: "warning",
+      }
+    );
     await logout();
     tokenStore.clearToken();
     
-    // 使用ElMessage而不是全局消息提示
-    ElMessage.success("退出登录成功");
+    ElMessage.success(t('logout.logoutSuccess'));
     
-    // 延迟跳转,让用户看到提示消息
     setTimeout(() => {
       router.push("/login");
     }, 1000);
   } catch (error) {
-    // 区分用户取消操作和真正的错误
     if (error === 'cancel' || error?.toString().includes('cancel')) {
-      // console.log("用户取消退出登录");
+      // 用户取消操作
     } else {
-      ElMessage.error("退出失败,请重试");
+      ElMessage.error(t('logout.logoutFailed'));
       console.error("退出登录错误:", error);
     }
   }
 };
-
-// 内容区样式
-const mainStyle = computed(() => ({
-  padding: ["mapView", "infoManagement"].includes(activeName.value)
-    ? "0"
-    : "20px",
-  overflow: "hidden",
-}));
 </script>
 
 <style>

+ 13 - 3
src/locales/en.json

@@ -7,7 +7,7 @@
     "title": "Intelligent Expert System for Soil Acidification Prediction",
     "welcome": "welcome",
     "loginsucc": "Login successful",
-    "username": "User Name",
+    "username": "UserName",
     "logout": "Log Out"
   },
   "login": {
@@ -17,6 +17,14 @@
     "loginSuccess": "Login successful",
     "loginFailed": "Login failed, please check your username or password"
   },
+  "logout": {
+    "confirmMessage": "Are you sure you want to logout?",
+    "confirmTitle": "Confirmation",
+    "confirmButton": "Confirm",
+    "cancelButton": "Cancel",
+    "logoutSuccess": "Logout successful",
+    "logoutFailed": "Logout failed, please try again"
+  },
   "register": {
     "title": "User Registration",
     "registerButton": "Register",
@@ -25,11 +33,13 @@
     "registerFailed": "Registration failed",
     "autoLoginPrompt": "Registration successful, redirecting to login page..."
   },
-  "validation": {
+  "validation": {"account": "Account",
+    "password": "Password",
+    "confirmPassword": "Confirm Password",
     "usernameRequired": "Please enter your username",
     "passwordRequired": "Please enter your password",
     "passwordLength": "Password must be between 3 and 16 characters",
-    "confirmPasswordRequired": "Please confirm your password",
+    "confirmPasswordRequired": "Confirm password",
     "passwordMismatch": "The two passwords you entered do not match"
   },
   "Menu": {

+ 12 - 1
src/locales/zh.json

@@ -13,6 +13,14 @@
     "loginSuccess": "登录成功",
     "loginFailed": "登录失败,请检查用户名或密码"
   },
+  "logout": {
+    "confirmMessage": "确定要退出登录吗?",
+    "confirmTitle": "提示",
+    "confirmButton": "确定",
+    "cancelButton": "取消",
+    "logoutSuccess": "退出登录成功",
+    "logoutFailed": "退出失败,请重试"
+  },
   "register": {
     "title": "用户注册",
     "registerButton": "注册",
@@ -22,7 +30,10 @@
     "autoLoginPrompt": "注册成功,正在跳转到登录页面..."
   },
   "validation": {
-    "usernameRequired": "请输入用户名",
+    "account": "账户",
+    "password": "密码",
+    "confirmPassword": "确认密码",
+    "usernameRequired": "请输入账号",
     "passwordRequired": "请输入密码",
     "passwordLength": "密码长度为 3 到 16 个字符",
     "confirmPasswordRequired": "请确认密码",

+ 19 - 16
src/views/Admin/userManagement/UserRegistration.vue

@@ -9,21 +9,21 @@
     >
       <!-- 输入账号 -->
       <div class="input-frame">
-        <el-form-item label="账号:" prop="name">
+        <el-form-item :label="$t('validation.username') + ':'" prop="name">
           <el-input v-model="registerForm.name" />
         </el-form-item>
       </div>
 
       <!-- 输入密码 -->
       <div class="input-frame">
-        <el-form-item label="密码:" prop="password">
+        <el-form-item :label="$t('validation.password') + ':'" prop="password">
           <el-input type="password" v-model="registerForm.password" />
         </el-form-item>
       </div>
 
       <!-- 确认密码 -->
       <div class="input-frame">
-        <el-form-item label="确认密码:" prop="confirmPassword">
+        <el-form-item :label="$t('validation.confirmPassword') + ':'" prop="confirmPassword">
           <el-input type="password" v-model="registerForm.confirmPassword" />
         </el-form-item>
       </div>
@@ -37,7 +37,7 @@
             :loading="loading"
             class="register-button"
           >
-            注册
+            {{ $t('register.registerButton') }}
           </el-button>
         </div>
       </el-form-item>
@@ -49,7 +49,9 @@
 import { reactive, ref } from "vue";
 import { ElForm, ElMessage } from "element-plus";
 import { register } from "@/API/users";
+import { useI18n } from 'vue-i18n';
 
+const { t } = useI18n();
 const loading = ref(false);
 
 // 注册表单数据
@@ -58,16 +60,18 @@ const registerFormRef = ref<InstanceType<typeof ElForm> | null>(null);
 
 // 表单校验规则
 const registerRules = {
-  name: [{ required: true, message: "请输入账号", trigger: "blur" }],
+  name: [
+    { required: true, message: t('validation.usernameRequired'), trigger: "blur" }
+  ],
   password: [
-    { required: true, message: "请输入密码", trigger: "blur" },
-    { min: 3, max: 16, message: "密码长度应为3-16位", trigger: "blur" }
+    { required: true, message: t('validation.passwordRequired'), trigger: "blur" },
+    { min: 3, max: 16, message: t('validation.passwordLength'), trigger: "blur" }
   ],
   confirmPassword: [
-    { required: true, message: "请确认密码", trigger: "blur" },
+    { required: true, message: t('validation.confirmPasswordRequired'), trigger: "blur" },
     {
       validator: (_rule: any, value: string, callback: (error?: Error) => void) => {
-        if (value !== registerForm.password) callback(new Error("两次密码输入不一致"));
+        if (value !== registerForm.password) callback(new Error(t('validation.passwordMismatch')));
         else callback();
       },
       trigger: "blur"
@@ -89,15 +93,15 @@ const onRegister = async () => {
     });
 
     if (res.data?.message) {
-      ElMessage.success(res.data.message);
+      ElMessage.success(t('register.registerSuccess'));
       registerForm.name = "";
       registerForm.password = "";
       registerForm.confirmPassword = "";
     } else {
-      ElMessage.error(res.data?.message || "注册失败");
+      ElMessage.error(res.data?.message || t('register.registerFailed'));
     }
   } catch (error: any) {
-    ElMessage.error(error?.response?.data?.detail || "注册异常");
+    ElMessage.error(error?.response?.data?.detail || t('register.registerFailed'));
   } finally {
     loading.value = false;
   }
@@ -138,8 +142,8 @@ const onRegister = async () => {
 
 .register-button {
   width: 300px;
-  max-width: 3000px; /* 最大宽度 */
-  height: 56px;     /* 高度加大 */
+  max-width: 3000px;
+  height: 56px;
   font-size: 20px;
   border-radius: 15px;
   background: linear-gradient(to right, #8df9f0, #26b046);
@@ -154,5 +158,4 @@ const onRegister = async () => {
   display: flex;
   justify-content: center;
 }
-</style>
-
+</style>

+ 18 - 19
src/views/login/loginView.vue

@@ -17,10 +17,10 @@
           <h2 class="form-title">{{ t("login.userTitle") }}</h2>
         </div>
 
-        <el-form-item label="账号" prop="name">
+        <el-form-item :label="t('Header.username')" prop="name">
           <el-input
             v-model="form.name"
-            placeholder="请输入用户名"
+            :placeholder="t('validation.usernameRequired')"
             autocomplete="username"
           >
             <template #prefix>
@@ -29,12 +29,12 @@
           </el-input>
         </el-form-item>
 
-        <el-form-item label="密码" prop="password">
+        <el-form-item :label="t('Header.logout').replace('退出', '').trim()" prop="password">
           <el-input
             v-model="form.password"
             type="password"
             show-password
-            placeholder="请输入密码"
+            :placeholder="t('validation.passwordRequired')"
             autocomplete="current-password"
           >
             <template #prefix>
@@ -65,9 +65,9 @@
         </el-form-item>
 
         <div class="switch-form">
-          还没有账户
+          {{ t("login.registerLink").split('?')[0] }}
           <span class="link" @click="toggleForm">{{
-            t("login.registerLink")
+            t("login.registerLink").split('?')[1] || t("login.registerLink")
           }}</span>
         </div>
       </el-form>
@@ -84,10 +84,10 @@
           <h2 class="form-title">{{ t("register.title") }}</h2>
         </div>
 
-        <el-form-item label="账号" prop="name" class="form-item-full">
+        <el-form-item :label="t('Header.username')" prop="name" class="form-item-full">
           <el-input
             v-model="registerForm.name"
-            placeholder="建议使用邮箱或手机号"
+            :placeholder="t('validation.usernameRequired')"
           >
             <template #prefix>
               <i class="icon-user"></i>
@@ -95,12 +95,12 @@
           </el-input>
         </el-form-item>
 
-        <el-form-item label="密码" prop="password" class="form-item-full">
+        <el-form-item :label="t('Header.logout').replace('退出', '').trim()" prop="password" class="form-item-full">
           <el-input
             v-model="registerForm.password"
             type="password"
             show-password
-            placeholder="长度 3-16 位"
+            :placeholder="t('validation.passwordLength')"
           >
             <template #prefix>
               <i class="icon-lock"></i>
@@ -109,7 +109,7 @@
         </el-form-item>
 
         <el-form-item
-          label="确认密码"
+          :label="t('validation.confirmPasswordRequired').replace('请', '')"
           prop="confirmPassword"
           class="form-item-full"
         >
@@ -117,7 +117,7 @@
             v-model="registerForm.confirmPassword"
             type="password"
             show-password
-            placeholder="请再次输入密码"
+            :placeholder="t('validation.confirmPasswordRequired')"
           >
             <template #prefix>
               <i class="icon-check"></i>
@@ -147,7 +147,7 @@
         </el-form-item>
 
         <div class="switch-form">
-          已有账户?
+          {{ t("register.backToLoginButton").includes('登录') ? '已有账户?' : 'Have an account?' }}
           <span class="link" @click="toggleForm">{{
             t("register.backToLoginButton")
           }}</span>
@@ -257,12 +257,11 @@ const onSubmit = async () => {
     });
 
     ElMessage.success(t("login.loginSuccess"));
-    // 替换原来的 router.push 行
-const targetRoute = userData.userType === 'admin' 
-  ? 'soilAcidReductionData' 
-  : 'SoilPro';
+    const targetRoute = userData.userType === 'admin' 
+      ? 'soilAcidReductionData' 
+      : 'SoilPro';
 
-await router.push({ name: targetRoute });
+    await router.push({ name: targetRoute });
   } catch (error: any) {
     const errorMsg =
       error.response?.data?.message || error.message || t("login.loginFailed");
@@ -647,4 +646,4 @@ $text-light: #e6dccd;
     transform: translateY(0);
   }
 }
-</style>
+</style>