Переглянути джерело

feat(login): 实现短信登录功能并完善多语言支持

- 在登录对话框中新增短信登录选项卡,包含手机号/邮箱输入、验证码发送与倒计时功能
- 调整登录方式标签页顺序,默认激活短信登录
- 完善登录表单的国际化文案,补充中英文翻译字段
- 支持根据当前语言动态设置请求头 Accept-Language 字段
- 修复 pinia 语言状态初始化时未保存到 localStorage 的问题
- 移除部分冗余代码和注释,优化模板结构
zhangningning 1 тиждень тому
батько
коміт
ee71bf6db2
7 змінених файлів з 103 додано та 70 видалено
  1. 1 1
      src/App.vue
  2. 3 1
      src/api/request.js
  3. 67 65
      src/components/LoginDialog.vue
  4. 13 0
      src/locales/en.js
  5. 13 0
      src/locales/zh-CN.js
  6. 4 2
      src/main.js
  7. 2 1
      src/pinia/langStore.js

+ 1 - 1
src/App.vue

@@ -11,7 +11,7 @@
             <el-menu-item index="2" @click="goMyLearning">{{ $t('common.gongzuoliu_trade') }}</el-menu-item>
             <el-menu-item index="3" @click="$router.push('/my-learning')">学习教程系统</el-menu-item>
             <el-menu-item index="4" @click="$router.push('/my-learning')">学习笔记</el-menu-item>
-            <el-menu-item index="5" @click="$router.push('/my-learning')">积分商城</el-menu-item>
+            <el-menu-item index="5" @click="$router.push('/my-learning')">米币商城</el-menu-item>
           </el-menu>
           <div class="header-right">
             <el-avatar :size="32" :src="appStore.userInfo?.avatar || 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'" />

+ 3 - 1
src/api/request.js

@@ -1,6 +1,6 @@
 import axios from 'axios'
 import { ElMessage } from 'element-plus'
-
+import { useLangStore } from '@/pinia/langStore'
 // 创建 axios 实例
 const request = axios.create({
   baseURL: import.meta.env.VITE_API_BASE_URL,
@@ -18,6 +18,8 @@ request.interceptors.request.use(
     if (token) {
       config.headers.Authorization = `Bearer ${token}`
     }
+    const langStore = useLangStore();
+    config.headers['Accept-Language'] = langStore.currentLang==='en'?'en':'zh';
     
     // 处理请求参数
     if (config.method === 'get' && config.params) {

+ 67 - 65
src/components/LoginDialog.vue

@@ -10,17 +10,63 @@
   >
     <!-- 登录标题 -->
     <div class="login-header">
-      <h2 class="login-title">欢迎回来</h2>
-      <p class="login-subtitle">请登录您的账号</p>
+      <h2 class="login-title">{{ $t('common.login') }}</h2>
+      <!-- <p class="login-subtitle">请登录您的账号</p> -->
     </div>
     
     <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="login-tabs">
-      <el-tab-pane label="账号密码登录" name="password">
+      <el-tab-pane :label="$t('login.smsLogin')" name="sms">
+        <el-form ref="smsFormRef" :model="smsForm" :rules="smsRules" label-width="0">
+          <el-form-item prop="account" class="login-form-item">
+            <el-input
+              v-model="smsForm.account"
+              :placeholder="$t('login.placeholderPhoneOrEmail')"
+              prefix-icon="User"
+              clearable
+              class="login-input"
+            />
+          </el-form-item>
+          <el-form-item prop="smsCode" class="login-form-item">
+            <el-input
+              v-model="smsForm.verifyCode"
+              :placeholder="$t('login.placeholderCaptcha')"
+              prefix-icon="Message"
+              clearable
+              class="login-input"
+            >
+              <template #append>
+                <el-button
+                  :disabled="smsCountdown > 0"
+                  @click="sendSmsCode"
+                  style="width:140px"
+                  :class="{'countdown-btn': smsCountdown > 0}"
+                  size="small"
+                >
+                  {{ smsCountdown > 0 ? `${smsCountdown}s` : $t('login.sendCaptcha') }}
+                </el-button>
+              </template>
+            </el-input>
+          </el-form-item>
+          <el-form-item class="login-form-item">
+            <el-button 
+              type="primary" 
+              @click="handleSmsLogin" 
+              :loading="loading" 
+              class="login-button"
+              size="large"
+            >
+              {{ $t('common.login') }}
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </el-tab-pane>
+
+      <el-tab-pane :label="$t('login.passwordLogin')" name="password">
         <el-form ref="passwordFormRef" :model="passwordForm" :rules="passwordRules" label-width="0">
           <el-form-item prop="account" class="login-form-item">
             <el-input
               v-model="passwordForm.account"
-              placeholder="请输入账号"
+              :placeholder="$t('login.placeholderAccount')"
               prefix-icon="User"
               clearable
               class="login-input"
@@ -30,7 +76,7 @@
             <el-input
               v-model="passwordForm.password"
               type="password"
-              placeholder="请输入密码"
+              :placeholder="$t('login.placeholderPassword')"
               prefix-icon="Lock"
               show-password
               class="login-input"
@@ -41,7 +87,7 @@
             <div class="gap10">
               <el-input
                 v-model="passwordForm.captcha"
-                placeholder="请输入验证码"
+                :placeholder="$t('login.placeholderCaptcha')"
                 prefix-icon="Lock"
                 class="login-input"
               />
@@ -53,7 +99,7 @@
             <!-- <el-checkbox v-model="passwordForm.remember" class="remember-checkbox">
               <span class="remember-text">记住密码</span>
             </el-checkbox> -->
-            <el-link type="primary" href="#" :underline="false" class="forgot-link" @click="activeTab = 'reset'">忘记密码?</el-link>
+            <el-link type="primary" href="#" :underline="false" class="forgot-link" @click="activeTab = 'reset'">{{ $t('login.forgetPassword') }} ?</el-link>
           </el-form-item>
           <el-form-item class="login-form-item">
             <el-button 
@@ -63,63 +109,19 @@
               class="login-button"
               size="large"
             >
-              登录
+              {{ $t('common.login') }}
             </el-button>
           </el-form-item>
         </el-form>
       </el-tab-pane>
       
-      <el-tab-pane label="验证码登录" name="sms">
-        <el-form ref="smsFormRef" :model="smsForm" :rules="smsRules" label-width="0">
-          <el-form-item prop="account" class="login-form-item">
-            <el-input
-              v-model="smsForm.account"
-              placeholder="请输入手机号或邮箱"
-              prefix-icon="User"
-              clearable
-              class="login-input"
-            />
-          </el-form-item>
-          <el-form-item prop="smsCode" class="login-form-item">
-            <el-input
-              v-model="smsForm.verifyCode"
-              placeholder="请输入验证码"
-              prefix-icon="Message"
-              clearable
-              class="login-input"
-            >
-              <template #append>
-                <el-button
-                  :disabled="smsCountdown > 0"
-                  @click="sendSmsCode"
-                  style="width:100px"
-                  :class="{'countdown-btn': smsCountdown > 0}"
-                  size="small"
-                >
-                  {{ smsCountdown > 0 ? `${smsCountdown}s` : '发送验证码' }}
-                </el-button>
-              </template>
-            </el-input>
-          </el-form-item>
-          <el-form-item class="login-form-item">
-            <el-button 
-              type="primary" 
-              @click="handleSmsLogin" 
-              :loading="loading" 
-              class="login-button"
-              size="large"
-            >
-              登录
-            </el-button>
-          </el-form-item>
-        </el-form>
-      </el-tab-pane>
-      <el-tab-pane label="重置密码" name="reset">
+      
+      <el-tab-pane :label="$t('login.resetPassword')" name="reset">
         <el-form ref="resetFormRef" :model="resetForm" :rules="resetRules" label-width="0">
           <el-form-item prop="account" class="login-form-item">
             <el-input
               v-model="resetForm.account"
-              placeholder="请输入手机号或邮箱"
+              :placeholder="$t('login.placeholderPhoneOrEmail')"
               prefix-icon="User"
               clearable
               class="login-input"
@@ -128,7 +130,7 @@
           <el-form-item prop="smsCode" class="login-form-item">
             <el-input
               v-model="resetForm.verifyCode"
-              placeholder="请输入验证码"
+              :placeholder="$t('login.placeholderCaptcha')"
               prefix-icon="Message"
               clearable
               class="login-input"
@@ -137,11 +139,11 @@
                 <el-button
                   :disabled="passwordresetCountdown > 0"
                   @click="sendPasswordresetCode"
-                  style="width:100px"
+                  style="width:140px"
                   :class="{'countdown-btn': passwordresetCountdown > 0}"
                   size="small"
                 >
-                  {{ passwordresetCountdown > 0 ? `${passwordresetCountdown}s` : '发送验证码' }}
+                  {{ passwordresetCountdown > 0 ? `${passwordresetCountdown}s` : $t('login.sendCaptcha') }}
                 </el-button>
               </template>
             </el-input>
@@ -149,7 +151,7 @@
           <el-form-item prop="newPassword" class="login-form-item">
             <el-input
               v-model="resetForm.newPassword"
-              placeholder="请输入新密码"
+              :placeholder="$t('login.placeholderNewPassword')"
               prefix-icon="Lock"
               show-password
               class="login-input"
@@ -158,7 +160,7 @@
           <el-form-item prop="confirmPassword" class="login-form-item">
             <el-input
               v-model="resetForm.confirmPassword"
-              placeholder="请确认新密码"
+              :placeholder="$t('login.placeholderConfirmPassword')"
               prefix-icon="Lock"
               show-password
               class="login-input"
@@ -172,7 +174,7 @@
               class="login-button"
               size="large"
             >
-              登录
+              {{ $t('common.login') }}
             </el-button>
           </el-form-item>
         </el-form>
@@ -182,7 +184,7 @@
     <!-- 其他登录方式 -->
     <div class="other-login">
       <div class="divider">
-        <span class="divider-text">其他登录方式</span>
+        <span class="divider-text">{{ $t('login.otherLogin') }}</span>
       </div>
       <div class="social-login">
         <el-button
@@ -203,10 +205,10 @@
     </div>
     
     <!-- 注册链接 -->
-    <div class="register-link">
+    <!-- <div class="register-link">
       <span>还没有账号?</span>
       <el-link type="primary" href="#" :underline="false">立即注册</el-link>
-    </div>
+    </div> -->
   </el-dialog>
 </template>
 
@@ -259,7 +261,7 @@ const getCaptchaFn = async (type) => {
 }
 
 // 当前激活的标签页
-const activeTab = ref('password')
+const activeTab = ref('sms')
 
 // 密码登录表单
 const passwordForm = reactive({

+ 13 - 0
src/locales/en.js

@@ -12,6 +12,19 @@ export default {
     gongzuoliu: 'Workflow',
     gongzuoliu_trade: 'Workflow Trade',
   },
+  login: {
+    smsLogin: 'SMS Login',
+    passwordLogin: 'Password Login',
+    resetPassword: 'Reset Password',
+    placeholderPhoneOrEmail: 'Please input phone or email',
+    placeholderCaptcha: 'Please input captcha',
+    sendCaptcha: 'Send Captcha',
+    otherLogin: 'Other Login',
+    placeholderPassword: 'Please input password',
+    placeholderNewPassword: 'Please input new password',
+    placeholderConfirmPassword: 'Please confirm new password',
+    forgetPassword: 'Forget Password',
+  },
   // 添加路由标题翻译
   route: {
     courseDetail: 'Course Detail',

+ 13 - 0
src/locales/zh-CN.js

@@ -12,6 +12,19 @@ export default {
     gongzuoliu: '工作流',
     gongzuoliu_trade: '工作流交易',
   },
+  login: {
+    smsLogin: '短信登录',
+    passwordLogin: '账号密码登录',
+    resetPassword: '重置密码',
+    placeholderPhoneOrEmail: '请输入手机号或邮箱',
+    placeholderCaptcha: '请输入验证码',
+    sendCaptcha: '发送验证码',
+    otherLogin: '其他登录方式',
+    placeholderPassword: '请输入密码',
+    placeholderNewPassword: '请输入新密码',
+    placeholderConfirmPassword: '请确认新密码',
+    forgetPassword: '忘记密码',
+  },
   // 添加路由标题翻译
   route: {
 

+ 4 - 2
src/main.js

@@ -9,13 +9,15 @@ import i18n from './i18n' // 导入i18n配置
 // 导入自定义消息工具
 import message from './utils/message'
 
+const app = createApp(App)
+app.use(createPinia())
+
 
 // 如果您正在使用CDN引入,请删除下面一行。
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 import Breadcrumb from '@/components/Breadcrumb.vue'
 
-const app = createApp(App)
-app.use(createPinia())
+
 
 // 注册i18n
 app.use(i18n)

+ 2 - 1
src/pinia/langStore.js

@@ -12,7 +12,8 @@ const elLocaleMap = {
 }
 
 // 默认语言(优先从localStorage读取,没有则用浏览器默认)
-const defaultLang = localStorage.getItem(LANG_KEY) || (navigator.language || 'zh-CN').toLowerCase()
+const defaultLang = localStorage.getItem(LANG_KEY) || (navigator.language || 'zh-CN').toLowerCase();
+localStorage.setItem(LANG_KEY, defaultLang)
 
 export const useLangStore = defineStore('lang', {
   state: () => ({