Explorar el Código

feat: add LangDropdown component for language selection

- Introduce LangDropdown.vue component to encapsulate language switching functionality
- Update layout and login views to utilize LangDropdown for cleaner code and improved maintainability
- Remove inline language switcher code from layout and login views, enhancing separation of concerns
yb hace 3 semanas
padre
commit
05150a7906
Se han modificado 4 ficheros con 76 adiciones y 103 borrados
  1. 1 0
      src/components.d.ts
  2. 57 0
      src/components/LangDropdown.vue
  3. 3 47
      src/layout/index.vue
  4. 15 56
      src/views/login/index.vue

+ 1 - 0
src/components.d.ts

@@ -56,6 +56,7 @@ declare module 'vue' {
     ElText: typeof import('element-plus/es')['ElText']
     ElUpload: typeof import('element-plus/es')['ElUpload']
     HelloWorld: typeof import('./components/HelloWorld.vue')['default']
+    LangDropdown: typeof import('./components/LangDropdown.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     VideoPlayer: typeof import('./components/VideoPlayer.vue')['default']

+ 57 - 0
src/components/LangDropdown.vue

@@ -0,0 +1,57 @@
+<template>
+  <el-dropdown class="lang-dropdown" @command="handleLanguage">
+    <span class="lang-trigger">
+      <Icon icon="mdi:translate" width="18" height="18" />
+      {{ currentLangLabel }}
+      <el-icon><ArrowDown /></el-icon>
+    </span>
+    <template #dropdown>
+      <el-dropdown-menu>
+        <el-dropdown-item command="zh-cn" :class="{ active: appStore.language === 'zh-cn' }">
+          简体中文
+        </el-dropdown-item>
+        <el-dropdown-item command="en" :class="{ active: appStore.language === 'en' }">
+          English
+        </el-dropdown-item>
+      </el-dropdown-menu>
+    </template>
+  </el-dropdown>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue'
+import { ArrowDown } from '@element-plus/icons-vue'
+import { Icon } from '@iconify/vue'
+import { useAppStore } from '@/store/app'
+
+const appStore = useAppStore()
+
+const currentLangLabel = computed(() => {
+  return appStore.language === 'zh-cn' ? '简体中文' : 'English'
+})
+
+function handleLanguage(lang: string) {
+  appStore.changeLanguage(lang)
+}
+</script>
+
+<style lang="scss" scoped>
+.lang-dropdown {
+  .lang-trigger {
+    display: flex;
+    align-items: center;
+    gap: 4px;
+    padding: 6px 10px;
+    border-radius: 6px;
+    color: var(--lang-color, #5a5e66);
+    font-size: 13px;
+    cursor: pointer;
+    transition: background-color 0.2s;
+
+    &:hover {
+      background-color: var(--lang-hover-bg, #f5f7fa);
+      color: var(--lang-hover-color, #409eff);
+    }
+  }
+}
+</style>

+ 3 - 47
src/layout/index.vue

@@ -70,28 +70,12 @@
           </el-icon>
           <el-breadcrumb separator="/">
             <el-breadcrumb-item v-for="item in breadcrumbs" :key="item.path">
-              {{ t(item.meta?.title) }}
+              {{ t(item.meta?.title as string) }}
             </el-breadcrumb-item>
           </el-breadcrumb>
         </div>
         <div class="header-right">
-          <el-dropdown class="lang-dropdown" @command="handleLanguage">
-            <span class="lang-trigger">
-              <Icon icon="mdi:translate" width="18" height="18" />
-              {{ currentLangLabel }}
-              <el-icon><ArrowDown /></el-icon>
-            </span>
-            <template #dropdown>
-              <el-dropdown-menu>
-                <el-dropdown-item command="zh-cn" :class="{ active: appStore.language === 'zh-cn' }">
-                  简体中文
-                </el-dropdown-item>
-                <el-dropdown-item command="en" :class="{ active: appStore.language === 'en' }">
-                  English
-                </el-dropdown-item>
-              </el-dropdown-menu>
-            </template>
-          </el-dropdown>
+          <LangDropdown />
 
           <el-dropdown @command="handleCommand">
             <span class="user-info">
@@ -160,7 +144,7 @@ import {
   UserFilled,
   VideoPlay
 } from '@element-plus/icons-vue'
-import { Icon } from '@iconify/vue'
+import LangDropdown from '@/components/LangDropdown.vue'
 import { useAppStore } from '@/store/app'
 import { useUserStore } from '@/store/user'
 import { changePassword } from '@/api/login'
@@ -173,15 +157,6 @@ const { t } = useI18n()
 const sidebarOpened = computed(() => appStore.sidebarOpened)
 const userInfo = computed(() => userStore.userInfo)
 
-// 语言切换
-const currentLangLabel = computed(() => {
-  return appStore.language === 'zh-cn' ? '简体中文' : 'English'
-})
-
-function handleLanguage(lang: string) {
-  appStore.changeLanguage(lang)
-}
-
 const activeMenu = computed(() => {
   const { path } = route
   return path
@@ -351,25 +326,6 @@ onMounted(() => {
     align-items: center;
     gap: 16px;
 
-    .lang-dropdown {
-      .lang-trigger {
-        display: flex;
-        align-items: center;
-        gap: 4px;
-        padding: 6px 10px;
-        border-radius: 6px;
-        color: #5a5e66;
-        font-size: 13px;
-        cursor: pointer;
-        transition: background-color 0.2s;
-
-        &:hover {
-          background-color: #f5f7fa;
-          color: #409eff;
-        }
-      }
-    }
-
     .user-info {
       display: flex;
       align-items: center;

+ 15 - 56
src/views/login/index.vue

@@ -2,21 +2,7 @@
   <div class="login">
     <!-- 语言切换 -->
     <div class="login__lang">
-      <el-dropdown @command="handleLanguage">
-        <span class="lang-trigger">
-          <Icon icon="mdi:translate" width="18" height="18" />
-          {{ currentLangLabel }}
-          <el-icon><ArrowDown /></el-icon>
-        </span>
-        <template #dropdown>
-          <el-dropdown-menu>
-            <el-dropdown-item command="zh-cn" :class="{ active: appStore.language === 'zh-cn' }">
-              简体中文
-            </el-dropdown-item>
-            <el-dropdown-item command="en" :class="{ active: appStore.language === 'en' }">English</el-dropdown-item>
-          </el-dropdown-menu>
-        </template>
-      </el-dropdown>
+      <LangDropdown />
     </div>
 
     <div class="login__card">
@@ -66,29 +52,19 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted, watch, computed } from 'vue'
+import { ref, reactive, onMounted, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
-import { User, Lock, ArrowDown } from '@element-plus/icons-vue'
-import { Icon } from '@iconify/vue'
+import { User, Lock } from '@element-plus/icons-vue'
+import LangDropdown from '@/components/LangDropdown.vue'
 import { useUserStore } from '@/store/user'
-import { useAppStore } from '@/store/app'
 import type { LoginParams } from '@/types'
 import { useI18n } from 'vue-i18n'
 
 const router = useRouter()
 const route = useRoute()
 const userStore = useUserStore()
-const appStore = useAppStore()
 const { t } = useI18n()
-// 语言切换
-const currentLangLabel = computed(() => {
-  return appStore.language === 'zh-cn' ? '简体中文' : 'English'
-})
-
-function handleLanguage(lang: string) {
-  appStore.changeLanguage(lang)
-}
 
 const version = __APP_VERSION__
 
@@ -131,17 +107,12 @@ async function handleLogin() {
   })
 }
 
-// 记住我 - 保存/读取登录信息
+// 记住我 - 只保存用户名
 const REMEMBER_KEY = 'login_remember'
 
 function saveLoginInfo() {
   if (rememberMe.value) {
-    const data = {
-      username: loginForm.username,
-      password: btoa(loginForm.password),
-      remember: true
-    }
-    localStorage.setItem(REMEMBER_KEY, JSON.stringify(data))
+    localStorage.setItem(REMEMBER_KEY, loginForm.username)
   } else {
     localStorage.removeItem(REMEMBER_KEY)
   }
@@ -150,14 +121,8 @@ function saveLoginInfo() {
 function loadLoginInfo() {
   const saved = localStorage.getItem(REMEMBER_KEY)
   if (saved) {
-    try {
-      const data = JSON.parse(saved)
-      loginForm.username = data.username || ''
-      loginForm.password = data.password ? atob(data.password) : ''
-      rememberMe.value = data.remember ?? true
-    } catch {
-      localStorage.removeItem(REMEMBER_KEY)
-    }
+    loginForm.username = saved
+    rememberMe.value = true
   }
 }
 
@@ -195,21 +160,15 @@ function goHelp() {
     right: 24px;
     z-index: 10;
 
-    .lang-trigger {
-      display: flex;
-      align-items: center;
-      gap: 6px;
+    // 覆盖 LangDropdown 组件的 CSS 变量 (深色主题)
+    --lang-color: #e5e7eb;
+    --lang-hover-bg: rgba(255, 255, 255, 0.18);
+    --lang-hover-color: #fff;
+
+    :deep(.lang-trigger) {
+      background: rgba(255, 255, 255, 0.1);
       padding: 8px 12px;
       border-radius: 8px;
-      background: rgba(255, 255, 255, 0.1);
-      color: #e5e7eb;
-      font-size: 13px;
-      cursor: pointer;
-      transition: background 0.2s;
-
-      &:hover {
-        background: rgba(255, 255, 255, 0.18);
-      }
     }
   }
 }