Browse Source

feature: 登陆成功,路由跳转,引入工具类

ljc 7 months ago
parent
commit
b3cbc71e68

+ 8 - 1
src/App.vue

@@ -5,8 +5,10 @@
 </template>
 
 <script lang="ts" setup>
-  import { computed, provide, nextTick, onMounted, ref } from 'vue'
+  import { computed, provide, nextTick, onMounted, ref, getCurrentInstance } from 'vue'
   import { useSettingStore } from '@/store/modules/setting'
+  import Pigeon from './lib/pigeon.js'
+  import Session from './lib/session.js'
 
   let isRouterAlive = ref(true)
   const SettingStore = useSettingStore()
@@ -18,7 +20,12 @@
       isRouterAlive.value = true
     })
   }
+  const { proxy } = getCurrentInstance()
+  // 注册全局弹窗组件
+  const $pigeon = new Pigeon(proxy)
   provide('reload', reload)
+  provide('pigeon', $pigeon)
+  provide('session', Session)
 
   onMounted(() => {
     window.document.documentElement.setAttribute('data-theme', 'theme1')

+ 10 - 11
src/layout/components/Header/components/Avatar.vue

@@ -1,26 +1,20 @@
 <template>
   <el-dropdown>
     <span class="el-dropdown-link">
-      {{ userInfo.username }}
+      {{ user.name || 'UserName' }}
       <el-icon class="header-icon el-icon--right">
         <arrow-down />
       </el-icon>
     </span>
     <template #dropdown>
       <el-dropdown-menu>
-        <el-dropdown-item :command="0" @click="switchRolesAction('admin')">
-          {{ currentRoles === 'admin' ? '当前角色' : '切换角色' }}:管理员
-        </el-dropdown-item>
-        <el-dropdown-item :command="0" divided @click="switchRolesAction('other')">
-          {{ currentRoles === 'other' ? '当前角色' : '切换角色' }}:普通用户
-        </el-dropdown-item>
         <el-dropdown-item :command="3" divided @click="modifyPassword">
           <el-icon><Edit /></el-icon>
-          修改密码
+          {{ $t('Label.UpdatePwd') }}
         </el-dropdown-item>
         <el-dropdown-item :command="4" divided @click="logOut">
           <el-icon><SwitchButton /></el-icon>
-          退出登录
+          Logout
         </el-dropdown-item>
       </el-dropdown-menu>
     </template>
@@ -32,18 +26,23 @@
 <script lang="ts" setup>
   import { useRouter } from 'vue-router'
   import { ElMessage, ElMessageBox } from 'element-plus'
-  import { computed, ref } from 'vue'
+  import { computed, ref,getCurrentInstance  } from 'vue'
 
-  import AvatarLogo from '@/assets/image/avatar.png'
   import { useUserStore } from '@/store/modules/user'
   import { useTagsViewStore } from '@/store/modules/tagsView'
   import { usePermissionStore } from '@/store/modules/permission'
   import PersonalDialog from './PersonalDialog.vue'
 
+  import { safeGetUser, safeGetDisplay } from '@/utils/safeJson'
   const router = useRouter()
   const UserStore = useUserStore()
   const TagsViewStore = useTagsViewStore()
   const PermissionStore = usePermissionStore()
+  const v3This = getCurrentInstance()
+
+  const user = computed(() => {
+    return safeGetUser(v3This..Session)
+  })
 
   const currentRoles = computed({
     get() {

+ 9 - 6
src/layout/components/Header/components/Language.vue

@@ -20,9 +20,11 @@
 <script lang="ts" setup>
   import { ArrowDown } from '@element-plus/icons-vue'
   import axios from 'axios'
-  import { getCurrentInstance, onMounted, inject } from 'vue'
+  import { getCurrentInstance, onMounted, inject, ref } from 'vue'
   import { useI18n } from 'vue-i18n'
   const reload = inject('reload')
+  // 引入session
+  const session = inject('session')
   const { locale } = useI18n()
   const langs = {
     cn: '中文简体',
@@ -35,7 +37,7 @@
 
   // 切换显示语言
   const SwitchLanguage = (lang) => {
-    if (lang != v3This.ctx.Session.Get('lang')) {
+    if (lang != session.Get('lang')) {
       switchLang(lang)
     }
   }
@@ -43,7 +45,7 @@
   const switchLang = async (lang) => {
     locale.value = lang
     langName.value = langs[lang]
-    v3This.ctx.Session.Set('lang', lang)
+    session.Set('lang', lang)
     axios.defaults.headers.common['Language'] = sessionStorage.getItem('lang')
     setTimeout(() => {
       reload()
@@ -52,10 +54,11 @@
 
   // 初始化显示语言
   const initLanguage = () => {
-    if (!v3This.ctx.Session.Get('lang')) {
-      v3This.ctx.Session.Set('lang', 'cn')
+    console.log(v3This, 'this')
+    if (!session.Get('lang')) {
+      session.Set('lang', 'cn')
     }
-    let lang = v3This.ctx.Session.Get('lang') || 'cn'
+    let lang = session.Get('lang') || 'cn'
     locale.value = lang
     langName.value = langs[lang]
   }

+ 3 - 7
src/lib/pigeon.js

@@ -5,13 +5,9 @@
  * @LastEditTime: 2018-11-30 11:25:43
  */
 
-import { getCurrentInstance } from 'vue'
-
-const { proxy } = getCurrentInstance()
-
 class Pigeon {
-  constructor() {
-    this.vue = proxy
+  constructor(vue) {
+    this.vue = vue
   }
 
   /**
@@ -157,4 +153,4 @@ class Pigeon {
   }
 }
 
-export default new Pigeon()
+export default Pigeon

+ 2 - 1
src/lib/service.js

@@ -24,7 +24,8 @@ class Service {
   SessionExpire() {
     // 状态管理去处理过期事件
     const store = useStore()
-    console.log(store)
+    console.log(store, 'store')
+    store.commit('InfoExpire', true)
     // this.vue.$store.commit('InfoExpire', true)
   }
 

+ 2 - 2
src/lib/session.js

@@ -13,7 +13,7 @@ class SessionStorage {
     let res = null
     try {
       if (flag) {
-        res = CryptoJS(this.Data[key])
+        res = crypt.Decrypt(this.Data[key])
       } else {
         res = this.Data[key]
       }
@@ -57,4 +57,4 @@ class SessionStorage {
   }
 }
 
-export default new SessionStorage
+export default new SessionStorage()

+ 6 - 5
src/main.ts

@@ -1,13 +1,14 @@
 import { createApp } from 'vue'
 import App from './App.vue'
 import router from './routers'
-import vuex from './store/index'
+import vuex from './vuexStore'
+import pinia from './store'
 
 import { registerElIcons } from '@/plugins/ElIcons'
 // 引入全局组件布局
 import PageWrapLayout from '@/components/PageWrapLayout/index.vue'
 // 权限路由
-import './permission'
+// import './permission'
 // svg-icons注册导入
 // @ts-ignore: virtual module injected by svg-icons plugin
 import 'virtual:svg-icons-register'
@@ -27,14 +28,13 @@ import '@/assets/iconfont/iconfont.js'
 import i18n from './i18n'
 
 import Config from './config'
-import SessionStorage from './lib/session.js'
-import Pigeon from './lib/pigeon.js'
+
+// import Pigeon from './lib/pigeon.js'
 //定义mixin
 const mixins = {
   created() {
     this.Session = SessionStorage
     this.Config = Config
-    this.$pigeon = Pigeon
   },
 }
 
@@ -45,6 +45,7 @@ app.component('SvgIcon', SvgIcon)
 app.component('PageWrapLayout', PageWrapLayout)
 
 app.use(vuex)
+app.use(pinia)
 app.use(router)
 app.use(i18n)
 app.use(ElementPlus).mount('#app')

+ 9 - 1
src/permission.ts

@@ -9,6 +9,14 @@ NProgress.configure({ showSpinner: false }) // NProgress Configuration
 const whiteList = ['/signin', '/forget', '/auth-redirect'] // 设置白名单
 // 记录路由
 let hasRoles = true
+const loggedIn = () => {
+  // return true
+  if (sessionStorage.getItem('user')) {
+    return true
+  } else {
+    return false
+  }
+}
 
 router.beforeEach(async (to, from, next) => {
   // 开启进度条
@@ -20,7 +28,7 @@ router.beforeEach(async (to, from, next) => {
 
   const UserStore = useUserStore()
   // 确定用户是否已登录过,存在Token
-  const hasToken = UserStore.token
+  const hasToken = loggedIn()
 
   if (hasToken) {
     if (to.path === '/signin') {

+ 32 - 0
src/routers/index.ts

@@ -98,4 +98,36 @@ const router = createRouter({
   routes: constantRoutes,
 })
 
+function loggedIn() {
+  // return true
+  if (sessionStorage.getItem('user')) {
+    return true
+  } else {
+    return false
+  }
+}
+
+router.beforeEach((to, from, next) => {
+  if (to.matched.some((record) => record.meta.requiresAuth)) {
+    // this route requires auth, check if logged in
+    // if not, redirect to login page.
+    if (!loggedIn()) {
+      next({
+        path: '/signin',
+        // query: { redirect: to.fullPath }
+      })
+    } else {
+      if (to.path == '/') {
+        next({
+          path: '/',
+        })
+      } else {
+        next()
+      }
+    }
+  } else {
+    next()
+  }
+})
+
 export default router

+ 18 - 10
src/store/index.ts

@@ -1,15 +1,23 @@
-import { createStore } from 'vuex'
-import home from './modules/home'
-import navMenu from './modules/navMenu'
+import { defineStore, createPinia } from 'pinia'
+// 引入持久化插件
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
 
-const store = createStore({
-  state: {},
-  mutations: {},
+export const Store = defineStore({
+  // id: 必须的,在所有 Store 中唯一
+  id: 'globalState',
+  // state: 返回对象的函数
+  state: () => ({}),
+  getters: {},
   actions: {},
-  modules: {
-    home,
-    navMenu,
+  persist: {
+    // 本地存储的名称
+    key: 'globalState',
+    //保存的位置
+    storage: window.sessionStorage, //localstorage
   },
 })
 
-export default store
+const pinia = createPinia()
+//pinia使用
+pinia.use(piniaPluginPersistedstate)
+export default pinia

+ 0 - 39
src/store/modules/navMenu.ts

@@ -1,39 +0,0 @@
-import session from '@/lib/session'
-
-export default {
-  state: {
-    index: "0",
-    isCollapse: false,
-    menus:[]
-  },
-  mutations: {
-    // 初始化菜单
-    InitNavMenu (state, payload) {
-      if (payload != null) {
-        session.Set("menus", JSON.stringify(payload), true)
-        session.Set("isCollapse", "false", false)
-      }
-      const menus = JSON.parse(session.Get("menus", true));
-      menus.forEach(item => {
-        if(item.name == 'R-User'){
-          const visibleChildren = item.children.filter(el => el.show);
-          if (visibleChildren.length === 1 && visibleChildren[0].name === 'R-UserList') {
-            console.log(visibleChildren[0].btns['R-UserList-Search'].show)
-            if(visibleChildren[0].btns['R-UserList-Search'].show === false) {
-              item.show = false
-            }
-          }
-        }
-      });
-      state.menus = JSON.parse(session.Get("menus", true));
-      console.log(state.menus, '---')
-      state.isCollapse = session.Get("isCollapse", false) == "true" ? true: false
-    },
-
-    // 菜单切换
-    SetCollapse (state, payload) {
-      state.isCollapse = payload;
-      session.Set("isCollapse", payload)
-    }
-  }
-}

+ 54 - 0
src/store/modules/permission.ts

@@ -0,0 +1,54 @@
+import { defineStore } from 'pinia'
+import { asyncRoutes, constantRoutes, notFoundRouter } from '@/routers/index'
+import { hasPermission, filterAsyncRoutes } from '@/utils/routers'
+import { filterKeepAlive } from '@/utils/routers'
+export const usePermissionStore = defineStore({
+  // id: 必须的,在所有 Store 中唯一
+  id: 'permissionState',
+  // state: 返回对象的函数
+  state: () => ({
+    // 路由
+    routes: [],
+    // 动态路由
+    addRoutes: [],
+    // 缓存路由
+    cacheRoutes: {},
+  }),
+  getters: {
+    permission_routes: (state) => {
+      return state.routes
+    },
+    keepAliveRoutes: (state) => {
+      return filterKeepAlive(asyncRoutes)
+    },
+  },
+  // 可以同步 也可以异步
+  actions: {
+    // 生成路由
+    generateRoutes(roles) {
+      return new Promise((resolve) => {
+        // 在这判断是否有权限,哪些角色拥有哪些权限
+        let accessedRoutes
+        if (roles && roles.length && !roles.includes('admin')) {
+          accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
+        } else {
+          accessedRoutes = asyncRoutes || []
+        }
+        accessedRoutes = accessedRoutes.concat(notFoundRouter)
+        this.routes = constantRoutes.concat(accessedRoutes)
+        this.addRoutes = accessedRoutes
+        resolve(accessedRoutes)
+      })
+    },
+    // 清楚路由
+    clearRoutes() {
+      this.routes = []
+      this.addRoutes = []
+      this.cacheRoutes = []
+    },
+    getCacheRoutes() {
+      this.cacheRoutes = filterKeepAlive(asyncRoutes)
+      return this.cacheRoutes
+    },
+  },
+})

+ 79 - 0
src/store/modules/setting.ts

@@ -0,0 +1,79 @@
+import { defineStore } from 'pinia'
+import { PRIMARY_COLOR } from '../../config'
+
+export const useSettingStore = defineStore({
+  // id: 必须的,在所有 Store 中唯一
+  id: 'settingState',
+  // state: 返回对象的函数
+  state: () => ({
+    // menu 是否收缩
+    isCollapse: true,
+    //
+    withoutAnimation: false,
+    device: 'desktop',
+    // 刷新当前页
+    isReload: true,
+    // 主题设置
+    themeConfig: {
+      // 显示设置
+      showSetting: false,
+      // 菜单展示模式 默认 vertical   horizontal / vertical /columns
+      mode: 'vertical',
+      // tagsView 是否展示 默认展示
+      showTag: true,
+      // 页脚
+      footer: true,
+      // 深色模式 切换暗黑模式
+      isDark: false,
+      // 显示侧边栏Logo
+      showLogo: true,
+      // 主题颜色
+      primary: PRIMARY_COLOR,
+      // element组件大小
+      globalComSize: 'default',
+      // 是否只保持一个子菜单的展开
+      uniqueOpened: true,
+      // 固定header
+      fixedHeader: true,
+      // 灰色模式
+      gray: false,
+      // 色弱模式
+      weak: false,
+    },
+  }),
+  getters: {},
+  // 可以同步 也可以异步
+  actions: {
+    // 设置主题
+    setThemeConfig({ key, val }) {
+      this.themeConfig[key] = val
+    },
+    // 切换 Collapse
+    setCollapse(value) {
+      this.isCollapse = value
+      this.withoutAnimation = false
+    },
+    // 关闭侧边栏
+    closeSideBar({ withoutAnimation }) {
+      this.isCollapse = false
+      this.withoutAnimation = withoutAnimation
+    },
+    toggleDevice(device) {
+      this.device = device
+    },
+    // 刷新
+    setReload() {
+      this.isReload = false
+      setTimeout(() => {
+        this.isReload = true
+      }, 50)
+    },
+  },
+  // 这部分数据不需要存储
+  // persist: {
+  //     // 本地存储的名称
+  //     key: "settingState",
+  //     //保存的位置
+  //     storage: window.localStorage,//localstorage
+  // },
+})

+ 107 - 0
src/store/modules/tagsView.ts

@@ -0,0 +1,107 @@
+import { defineStore } from 'pinia'
+import router from '@/routers/index'
+
+export const useTagsViewStore = defineStore({
+  // id: 必须的,在所有 Store 中唯一
+  id: 'tagsViewState',
+  // state: 返回对象的函数
+  state: () => ({
+    activeTabsValue: '/home',
+    visitedViews: [],
+    cachedViews: [],
+  }),
+  getters: {},
+  // 可以同步 也可以异步
+  actions: {
+    setTabsMenuValue(val) {
+      this.activeTabsValue = val
+    },
+    addView(view) {
+      this.addVisitedView(view)
+    },
+    removeView(routes) {
+      return new Promise((resolve, reject) => {
+        this.visitedViews = this.visitedViews.filter((item) => !routes.includes(item.path))
+        resolve(null)
+      })
+    },
+    addVisitedView(view) {
+      this.setTabsMenuValue(view.path)
+      if (this.visitedViews.some((v) => v.path === view.path)) return
+
+      this.visitedViews.push(
+        Object.assign({}, view, {
+          title: view.meta.title || 'no-name',
+        })
+      )
+      if (view.meta.keepAlive) {
+        this.cachedViews.push(view.name)
+      }
+    },
+    delView(activeTabPath) {
+      return new Promise((resolve) => {
+        this.delVisitedView(activeTabPath)
+        this.delCachedView(activeTabPath)
+        resolve({
+          visitedViews: [...this.visitedViews],
+          cachedViews: [...this.cachedViews],
+        })
+      })
+    },
+    toLastView(activeTabPath) {
+      const index = this.visitedViews.findIndex((item) => item.path === activeTabPath)
+      const nextTab = this.visitedViews[index + 1] || this.visitedViews[index - 1]
+      if (!nextTab) return
+      router.push(nextTab.path)
+      this.addVisitedView(nextTab)
+    },
+    delVisitedView(path) {
+      return new Promise((resolve) => {
+        this.visitedViews = this.visitedViews.filter((v) => {
+          return v.path !== path || v.meta.affix
+        })
+        this.cachedViews = this.cachedViews.filter((v) => {
+          return v.path !== path || v.meta.affix
+        })
+        resolve([...this.visitedViews])
+      })
+    },
+    delCachedView(view) {
+      return new Promise((resolve) => {
+        const index = this.cachedViews.indexOf(view.name)
+        index > -1 && this.cachedViews.splice(index, 1)
+        resolve([...this.cachedViews])
+      })
+    },
+    clearVisitedView() {
+      this.delAllViews()
+    },
+    delAllViews() {
+      return new Promise((resolve) => {
+        this.visitedViews = this.visitedViews.filter((v) => v.meta.affix)
+        this.cachedViews = this.visitedViews.filter((v) => v.meta.affix)
+        resolve([...this.visitedViews])
+      })
+    },
+    delOtherViews(path) {
+      this.visitedViews = this.visitedViews.filter((item) => {
+        return item.path === path || item.meta.affix
+      })
+      this.cachedViews = this.visitedViews.filter((item) => {
+        return item.path === path || item.meta.affix
+      })
+    },
+    goHome() {
+      this.activeTabsValue = '/home'
+      router.push({ path: '/home' })
+    },
+    updateVisitedView(view) {
+      for (let v of this.visitedViews) {
+        if (v.path === view.path) {
+          v = Object.assign(v, view)
+          break
+        }
+      }
+    },
+  },
+})

+ 61 - 0
src/store/modules/user.ts

@@ -0,0 +1,61 @@
+import { defineStore } from 'pinia'
+
+export const useUserStore = defineStore({
+  // id: 必须的,在所有 Store 中唯一
+  id: 'userState',
+  // state: 返回对象的函数
+  state: () => ({
+    // 登录token
+    token: null,
+    // 登录用户信息
+    userInfo: {},
+    // 角色
+    roles: localStorage.roles ? JSON.parse(localStorage.roles) : [],
+  }),
+  getters: {},
+  // 可以同步 也可以异步
+  actions: {
+    // 登录
+    login(userInfo) {
+      const { username, password } = userInfo
+      return new Promise(async (resolve, reject) => {
+        this.token = username
+        this.userInfo = userInfo
+        await this.getRoles()
+        resolve(username)
+      })
+    },
+    // 获取用户授权角色信息,实际应用中 可以通过token通过请求接口在这里获取用户信息
+    getRoles() {
+      return new Promise((resolve, reject) => {
+        // 获取权限列表 默认就是超级管理员,因为没有进行接口请求 写死
+        this.roles = ['admin']
+        localStorage.roles = JSON.stringify(this.roles)
+        resolve(this.roles)
+      })
+    },
+    // 获取用户信息 ,如实际应用中 可以通过token通过请求接口在这里获取用户信息
+    getInfo(roles) {
+      return new Promise((resolve, reject) => {
+        this.roles = roles
+        resolve(roles)
+      })
+    },
+    // 退出
+    logout() {
+      return new Promise((resolve, reject) => {
+        this.token = null
+        this.userInfo = {}
+        this.roles = []
+        resolve(null)
+      })
+    },
+  },
+  // 进行持久化存储
+  persist: {
+    // 本地存储的名称
+    key: 'userState',
+    //保存的位置
+    storage: window.localStorage, //localstorage
+  },
+})

+ 68 - 0
src/utils/safeJson.js

@@ -0,0 +1,68 @@
+/**
+ * 安全的 JSON 解析工具
+ * 用于处理 sessionStorage 中的数据解析,避免 "Unexpected end of JSON input" 错误
+ */
+
+/**
+ * 安全地解析 JSON 字符串
+ * @param {string} jsonString - 要解析的 JSON 字符串
+ * @param {any} defaultValue - 解析失败时的默认值
+ * @returns {any} 解析结果或默认值
+ */
+export function safeJsonParse(jsonString, defaultValue = null) {
+  if (!jsonString) {
+    return defaultValue
+  }
+
+  try {
+    return JSON.parse(jsonString)
+  } catch (error) {
+    console.error('Error parsing JSON:', error, 'Input:', jsonString)
+    return defaultValue
+  }
+}
+
+/**
+ * 安全地从 Session 获取并解析数据
+ * @param {Object} session - Session 实例
+ * @param {string} key - 数据键名
+ * @param {boolean} flag - 是否加密
+ * @param {any} defaultValue - 解析失败时的默认值
+ * @returns {any} 解析结果或默认值
+ */
+export function safeSessionGet(session, key, flag = false, defaultValue = null) {
+  try {
+    const data = session.Get(key, flag)
+    return safeJsonParse(data, defaultValue)
+  } catch (error) {
+    console.error(`Error getting session data for key "${key}":`, error)
+    return defaultValue
+  }
+}
+
+/**
+ * 安全地获取用户信息
+ * @param {Object} session - Session 实例
+ * @returns {Object|null} 用户信息对象或 null
+ */
+export function safeGetUser(session) {
+  return safeSessionGet(session, 'user', true, null)
+}
+
+/**
+ * 安全地获取权限信息
+ * @param {Object} session - Session 实例
+ * @returns {Object} 权限信息对象或空对象
+ */
+export function safeGetDisplay(session) {
+  return safeSessionGet(session, 'display', true, {})
+}
+
+/**
+ * 安全地获取菜单信息
+ * @param {Object} session - Session 实例
+ * @returns {Array} 菜单信息数组或空数组
+ */
+export function safeGetMenus(session) {
+  return safeSessionGet(session, 'menus', true, [])
+}

+ 71 - 54
src/views/login/Forget.vue

@@ -4,80 +4,96 @@
       <i class="el-icon-lock"></i>
       <span v-t="'forget.title'"></span>
     </div>
-    <el-form :model="params" :rules="rules" ref="params" label-width="0" class="form">
+    <el-form :model="params" :rules="rules" ref="paramsFormRef" label-width="0" class="form">
       <el-form-item prop="email">
         <el-input
           class="m-input"
-          prefix-icon="iconfont iconyouxiang1"
+          :prefix-icon="Message"
           v-model.trim="params.email"
           :placeholder="$t('forget.form')"
         ></el-input>
       </el-form-item>
       <el-form-item>
-        <el-button type="danger" class="s-btn" @click="send" v-t="'forget.forget'"></el-button>
+        <el-button type="danger" class="s-btn" @click="send">
+          {{ $t('forget.forget') }}
+        </el-button>
       </el-form-item>
       <el-form-item>
-        <el-button class="s-btn" @click="cancel" v-t="'forget.cancel'"></el-button>
+        <el-button class="s-btn" @click="cancel">
+          {{ $t('forget.cancel') }}
+        </el-button>
       </el-form-item>
     </el-form>
   </div>
 </template>
 
-<script>
+<script lang="ts" setup>
+  import { ref, reactive, computed, getCurrentInstance, inject } from 'vue'
+  import { useRouter } from 'vue-router'
+  import { useI18n } from 'vue-i18n'
+  import { Message } from '@element-plus/icons-vue'
   import Service from '@/service/login'
   import Config from '@/config'
 
-  let { Code } = Config
-  export default {
-    data() {
-      return {
-        loading: false,
-        params: {
-          email: '',
-        },
-        // 验证规则
-        rules: {
-          email: [
-            {
-              validator: (rule, value, callback) => {
-                if (this.Config.Pattern.Email.test(value)) {
-                  callback()
-                } else {
-                  callback(new Error(this.$i18n.t('vaildate.email.format')))
-                }
-              },
-              trigger: 'blur',
-            },
-          ],
-        },
-      }
-    },
-    methods: {
-      // 发送
-      send: async function () {
-        this.$refs['params'].validate(async (valid) => {
-          if (valid) {
-            let res = await Service.forgetPwd(this.params)
-            if (res.code == Code.StatusOK) {
-              this.loading = true
-              this.$pigeon.MessageOK(res.msg)
-              setTimeout(() => {
-                this.loading = false
-                this.$router.push({ path: '/signin' }).catch((arr) => arr)
-              }, 1000)
-            } else {
-              this.$pigeon.MessageError(res.msg)
-            }
+  const { Code } = Config
+  const router = useRouter()
+  const { t } = useI18n()
+  const { proxy } = getCurrentInstance()
+
+  // 引入全局弹窗
+  const pigeon = inject('pigeon')
+
+  // 响应式数据
+  const loading = ref(false)
+  const params = reactive({
+    email: '',
+  })
+
+  const paramsFormRef = ref()
+
+  // 验证规则
+  const rules = reactive({
+    email: [
+      {
+        validator: (rule, value, callback) => {
+          if (Config.Pattern.Email.test(value)) {
+            callback()
           } else {
-            return false
+            callback(new Error(t('vaildate.email.format')))
           }
-        })
-      },
-      cancel() {
-        this.$router.push({ path: '/signin' }).catch((arr) => arr)
+        },
+        trigger: 'blur',
       },
-    },
-    mounted() {},
+    ],
+  })
+
+  // 方法
+  const send = async () => {
+    if (!paramsFormRef.value) return
+
+    try {
+      const valid = await paramsFormRef.value.validate()
+      if (!valid) return
+
+      const res = await Service.forgetPwd(params)
+      if (res.code === Code.StatusOK) {
+        loading.value = true
+        // 使用自定义消息组件
+        pigeon.MessageOK(res.msg)
+        setTimeout(() => {
+          loading.value = false
+          router.push({ path: '/signin' }).catch((err) => console.log(err))
+        }, 1000)
+      } else {
+        pigeon.MessageError(res.msg)
+      }
+    } catch (error) {
+      console.error('发送失败:', error)
+    }
+  }
+
+  const cancel = () => {
+    router.push({ path: '/signin' }).catch((err) => console.log(err))
   }
 </script>
 
@@ -128,10 +144,11 @@
 
 <style lang="scss">
   #signin {
-    .el-input__inner {
+    .el-input__wrapper {
       border: none;
       border-bottom: 1px solid;
       border-radius: 0;
+      box-shadow: none;
       @include border_gray_1();
     }
     .el-form-item {

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

@@ -99,7 +99,15 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive, onMounted, computed, getCurrentInstance } from 'vue'
+  import {
+    ref,
+    reactive,
+    onMounted,
+    computed,
+    getCurrentInstance,
+    onDeactivated,
+    inject,
+  } from 'vue'
   import { useRouter } from 'vue-router'
   import { useStore } from 'vuex'
   import Service from '@/service/login'
@@ -112,6 +120,11 @@
   const { Code } = Config
   const router = useRouter()
   const store = useStore()
+  const v3This = getCurrentInstance()
+  // 引入弹窗方法
+  const pigeon = inject('pigeon')
+  // 引入session
+  const session = inject('session')
 
   const { t } = useI18n()
   // 类型定义
@@ -266,7 +279,8 @@
         axios.defaults.headers.common['Access-Token'] = res.data
         await getLoginInfo()
       } else {
-        console.error(res.msg)
+        pigeon.MessageError(res.msg)
+        loading.value = false
       }
     } catch (error) {
       console.error('登录失败:', error)
@@ -284,8 +298,9 @@
         store.commit('InfoExpire', false)
         sessionStorage.setItem('info', JSON.stringify(res.data))
         console.log('登录成功')
-
+        pigeon.MessageOK(t('Msg.LoginSuccess'))
         setTimeout(() => {
+          console.log('跳转')
           router.push({ path: '/' }).catch((err) => console.log(err))
         }, 1000)
       } else {
@@ -339,16 +354,16 @@
     if (!val) return
 
     try {
-      // const res = await Service.sendCode({
-      //   loginName: params.loginName,
-      //   lang: sessionStorage.getItem('lang') || 'en',
-      // })
-      //
-      // if (res.code === Code.StatusOK) {
-      //   console.log('验证码发送成功')
-      // } else {
-      //   console.error(res.msg)
-      // }
+      const res = await Service.sendCode({
+        loginName: params.loginName,
+        lang: session.Get('lang') || 'en',
+      })
+
+      if (res.code === Code.StatusOK) {
+        console.log('验证码发送成功')
+      } else {
+        console.error(res.msg)
+      }
     } catch (error) {
       console.error('发送验证码失败:', error)
     }
@@ -356,13 +371,13 @@
 
   // 初始化定时器
   const initTimer = () => {
-    const storedTimer = sessionStorage.getItem('timer')
+    const storedTimer = session.Get('timer') || timer.value
     const st = storedTimer ? parseInt(storedTimer) : 59
 
     if (st === 59) {
       getCodeString.value = t('signup.form.getCode')
     } else {
-      timer.value = st
+      timer.value = session.Get('timer')
       getCode(0)
     }
   }
@@ -370,6 +385,9 @@
   onMounted(() => {
     initTimer()
   })
+  onDeactivated(() => {
+    clearInterval(interval.value)
+  })
 </script>
 <style lang="scss" scoped>
   @import './index.scss';

+ 15 - 0
src/vuexStore/index.ts

@@ -0,0 +1,15 @@
+import { createStore } from 'vuex'
+import home from './modules/home'
+import navMenu from './modules/navMenu'
+
+const store = createStore({
+  state: {},
+  mutations: {},
+  actions: {},
+  modules: {
+    home,
+    navMenu,
+  },
+})
+
+export default store

+ 1 - 0
src/store/modules/home.ts → src/vuexStore/modules/home.ts

@@ -9,6 +9,7 @@ export default {
   mutations: {
     //个人信息/登录信息
     InitInfo(state, payload) {
+      console.log(payload, '设置个人信息')
       if (payload != null) {
         session.Set('user', JSON.stringify(payload), true)
       }

+ 39 - 0
src/vuexStore/modules/navMenu.ts

@@ -0,0 +1,39 @@
+import session from '@/lib/session'
+
+export default {
+  state: {
+    index: '0',
+    isCollapse: false,
+    menus: [],
+  },
+  mutations: {
+    // 初始化菜单
+    InitNavMenu(state, payload) {
+      if (payload != null) {
+        session.Set('menus', JSON.stringify(payload), true)
+        session.Set('isCollapse', 'false', false)
+      }
+      const menus = JSON.parse(session.Get('menus', true))
+      menus.forEach((item) => {
+        if (item.name == 'R-User') {
+          const visibleChildren = item.children.filter((el) => el.show)
+          if (visibleChildren.length === 1 && visibleChildren[0].name === 'R-UserList') {
+            console.log(visibleChildren[0].btns['R-UserList-Search'].show)
+            if (visibleChildren[0].btns['R-UserList-Search'].show === false) {
+              item.show = false
+            }
+          }
+        }
+      })
+      state.menus = JSON.parse(session.Get('menus', true))
+      console.log(state.menus, '---')
+      state.isCollapse = session.Get('isCollapse', false) == 'true' ? true : false
+    },
+
+    // 菜单切换
+    SetCollapse(state, payload) {
+      state.isCollapse = payload
+      session.Set('isCollapse', payload)
+    },
+  },
+}