优化pinia持久化
This commit is contained in:
@@ -16,9 +16,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
"element-plus": "^2.13.1",
|
"element-plus": "^2.13.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
|
"pinia-plugin-persistedstate": "^4.7.1",
|
||||||
"vue": "^3.5.26",
|
"vue": "^3.5.26",
|
||||||
"vue-i18n": "^11.2.8",
|
"vue-i18n": "^11.2.8",
|
||||||
"vue-router": "^4.6.4"
|
"vue-router": "^4.6.4"
|
||||||
|
|||||||
12
src/App.vue
12
src/App.vue
@@ -1,4 +1,14 @@
|
|||||||
<script setup></script>
|
<script setup>
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useI18nStore } from './stores/modules/i18n'
|
||||||
|
import i18n from './i18n'
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 从持久化的 store 中读取语言设置并同步到 i18n
|
||||||
|
const i18nStore = useI18nStore()
|
||||||
|
i18n.global.locale.value = i18nStore.currentLocale
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<router-view />
|
<router-view />
|
||||||
|
|||||||
@@ -1,7 +1,51 @@
|
|||||||
export default {
|
export default {
|
||||||
app_title: 'vueadmin',
|
APP_NAME: 'vueadmin',
|
||||||
DASHBOARD_URL: '/home',
|
DASHBOARD_URL: '/home',
|
||||||
baseURL: '',
|
|
||||||
// 白名单路由(不需要登录即可访问)
|
// 白名单路由(不需要登录即可访问)
|
||||||
whiteList: ['/login', '/register', '/reset-password']
|
whiteList: ['/login', '/register', '/reset-password'],
|
||||||
|
//版本号
|
||||||
|
APP_VER: "1.6.6",
|
||||||
|
|
||||||
|
//内核版本号
|
||||||
|
CORE_VER: "1.6.6",
|
||||||
|
|
||||||
|
//接口地址
|
||||||
|
API_URL: "http://localhost:8000/admin/",
|
||||||
|
|
||||||
|
//请求超时
|
||||||
|
TIMEOUT: 50000,
|
||||||
|
|
||||||
|
//TokenName
|
||||||
|
TOKEN_NAME: "authorization",
|
||||||
|
|
||||||
|
//Token前缀,注意最后有个空格,如不需要需设置空字符串
|
||||||
|
TOKEN_PREFIX: "Bearer ",
|
||||||
|
|
||||||
|
//追加其他头
|
||||||
|
HEADERS: {},
|
||||||
|
|
||||||
|
//请求是否开启缓存
|
||||||
|
REQUEST_CACHE: false,
|
||||||
|
//语言
|
||||||
|
LANG: "zh-cn",
|
||||||
|
|
||||||
|
//是否加密localStorage, 为空不加密
|
||||||
|
//支持多种加密方式: 'AES', 'BASE64', 'DES'
|
||||||
|
LS_ENCRYPTION: "",
|
||||||
|
|
||||||
|
//localStorage加密秘钥,位数建议填写8的倍数
|
||||||
|
LS_ENCRYPTION_key: "2XNN4K8LC0ELVWN4",
|
||||||
|
|
||||||
|
//localStorage加密模式,AES支持: 'ECB', 'CBC', 'CTR', 'OFB', 'CFB'
|
||||||
|
LS_ENCRYPTION_mode: "ECB",
|
||||||
|
|
||||||
|
//localStorage加密填充方式,AES支持: 'Pkcs7', 'ZeroPadding', 'Iso10126', 'Iso97971'
|
||||||
|
LS_ENCRYPTION_padding: "Pkcs7",
|
||||||
|
|
||||||
|
//localStorage默认过期时间(单位:小时),0表示永不过期
|
||||||
|
LS_DEFAULT_EXPIRE: 720, // 30天
|
||||||
|
|
||||||
|
//DES加密秘钥,必须是8字节
|
||||||
|
LS_DES_key: "12345678",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import App from './App.vue'
|
|||||||
import router from './router'
|
import router from './router'
|
||||||
import pinia from './stores'
|
import pinia from './stores'
|
||||||
import i18n from './i18n'
|
import i18n from './i18n'
|
||||||
import { useI18nStore } from './stores/modules/i18n'
|
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
@@ -15,8 +14,4 @@ app.use(router)
|
|||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.use(i18n)
|
app.use(i18n)
|
||||||
|
|
||||||
// 初始化 i18n store,从 localStorage 读取保存的语言设置
|
|
||||||
const i18nStore = useI18nStore()
|
|
||||||
i18nStore.initLocale()
|
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||||
|
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|
||||||
|
// 注册持久化插件
|
||||||
|
pinia.use(piniaPluginPersistedstate)
|
||||||
|
|
||||||
export default pinia
|
export default pinia
|
||||||
|
|||||||
@@ -1,34 +1,35 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import i18n from '@/i18n'
|
import i18n from '@/i18n'
|
||||||
|
|
||||||
export const useI18nStore = defineStore('i18n', {
|
export const useI18nStore = defineStore(
|
||||||
state: () => ({
|
'i18n',
|
||||||
currentLocale: 'zh-CN',
|
{
|
||||||
availableLocales: [
|
state: () => ({
|
||||||
{ label: '简体中文', value: 'zh-CN' },
|
currentLocale: 'zh-CN',
|
||||||
{ label: 'English', value: 'en-US' }
|
availableLocales: [
|
||||||
]
|
{ label: '简体中文', value: 'zh-CN' },
|
||||||
}),
|
{ label: 'English', value: 'en-US' }
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
localeLabel: (state) => {
|
localeLabel: (state) => {
|
||||||
const locale = state.availableLocales.find((item) => item.value === state.currentLocale)
|
const locale = state.availableLocales.find((item) => item.value === state.currentLocale)
|
||||||
return locale ? locale.label : ''
|
return locale ? locale.label : ''
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
setLocale(locale) {
|
|
||||||
this.currentLocale = locale
|
|
||||||
i18n.global.locale.value = locale
|
|
||||||
localStorage.setItem('locale', locale)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
initLocale() {
|
actions: {
|
||||||
const savedLocale = localStorage.getItem('locale')
|
setLocale(locale) {
|
||||||
if (savedLocale && this.availableLocales.some((item) => item.value === savedLocale)) {
|
this.currentLocale = locale
|
||||||
this.setLocale(savedLocale)
|
i18n.global.locale.value = locale
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
persist: {
|
||||||
|
key: 'i18n-store',
|
||||||
|
storage: localStorage,
|
||||||
|
pick: ['currentLocale']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|||||||
@@ -1,57 +1,67 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
export const useLayoutStore = defineStore('layout', () => {
|
export const useLayoutStore = defineStore(
|
||||||
// 布局模式:'sidebar', 'top-nav', 'sidebar-top', 'classic'
|
'layout',
|
||||||
const layoutMode = ref('sidebar')
|
() => {
|
||||||
|
// 布局模式:'sidebar', 'top-nav', 'sidebar-top', 'classic'
|
||||||
|
const layoutMode = ref('sidebar')
|
||||||
|
|
||||||
// 侧边栏折叠状态
|
// 侧边栏折叠状态
|
||||||
const sidebarCollapsed = ref(false)
|
const sidebarCollapsed = ref(false)
|
||||||
|
|
||||||
// 视图标签页(用于记录页面滚动位置)
|
// 视图标签页(用于记录页面滚动位置)
|
||||||
const viewTags = ref([])
|
const viewTags = ref([])
|
||||||
|
|
||||||
// 切换侧边栏折叠
|
// 切换侧边栏折叠
|
||||||
const toggleSidebar = () => {
|
const toggleSidebar = () => {
|
||||||
sidebarCollapsed.value = !sidebarCollapsed.value
|
sidebarCollapsed.value = !sidebarCollapsed.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置布局模式
|
// 设置布局模式
|
||||||
const setLayoutMode = (mode) => {
|
const setLayoutMode = (mode) => {
|
||||||
layoutMode.value = mode
|
layoutMode.value = mode
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新视图标签
|
// 更新视图标签
|
||||||
const updateViewTags = (tag) => {
|
const updateViewTags = (tag) => {
|
||||||
const index = viewTags.value.findIndex((item) => item.fullPath === tag.fullPath)
|
const index = viewTags.value.findIndex((item) => item.fullPath === tag.fullPath)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
viewTags.value[index] = tag
|
viewTags.value[index] = tag
|
||||||
} else {
|
} else {
|
||||||
viewTags.value.push(tag)
|
viewTags.value.push(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除视图标签
|
||||||
|
const removeViewTags = (fullPath) => {
|
||||||
|
const index = viewTags.value.findIndex((item) => item.fullPath === fullPath)
|
||||||
|
if (index !== -1) {
|
||||||
|
viewTags.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空视图标签
|
||||||
|
const clearViewTags = () => {
|
||||||
|
viewTags.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
layoutMode,
|
||||||
|
sidebarCollapsed,
|
||||||
|
viewTags,
|
||||||
|
toggleSidebar,
|
||||||
|
setLayoutMode,
|
||||||
|
updateViewTags,
|
||||||
|
removeViewTags,
|
||||||
|
clearViewTags,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
persist: {
|
||||||
|
key: 'layout-store',
|
||||||
|
storage: localStorage,
|
||||||
|
pick: ['layoutMode', 'sidebarCollapsed']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
// 移除视图标签
|
|
||||||
const removeViewTags = (fullPath) => {
|
|
||||||
const index = viewTags.value.findIndex((item) => item.fullPath === fullPath)
|
|
||||||
if (index !== -1) {
|
|
||||||
viewTags.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空视图标签
|
|
||||||
const clearViewTags = () => {
|
|
||||||
viewTags.value = []
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
layoutMode,
|
|
||||||
sidebarCollapsed,
|
|
||||||
viewTags,
|
|
||||||
toggleSidebar,
|
|
||||||
setLayoutMode,
|
|
||||||
updateViewTags,
|
|
||||||
removeViewTags,
|
|
||||||
clearViewTags,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -2,79 +2,80 @@ import { ref } from 'vue'
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { resetRouter } from '../../router'
|
import { resetRouter } from '../../router'
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', () => {
|
export const useUserStore = defineStore(
|
||||||
const token = ref(localStorage.getItem('token') || '')
|
'user',
|
||||||
const refreshToken = ref(localStorage.getItem('refreshToken') || '')
|
() => {
|
||||||
const userInfo = ref(JSON.parse(localStorage.getItem('userInfo') || 'null'))
|
const token = ref('')
|
||||||
const menu = ref(JSON.parse(localStorage.getItem('MENU') || '[]'))
|
const refreshToken = ref('')
|
||||||
|
const userInfo = ref(null)
|
||||||
|
const menu = ref([])
|
||||||
|
|
||||||
// 设置 token
|
// 设置 token
|
||||||
function setToken(newToken) {
|
function setToken(newToken) {
|
||||||
token.value = newToken
|
token.value = newToken
|
||||||
localStorage.setItem('token', newToken)
|
}
|
||||||
|
|
||||||
|
// 设置 refresh token
|
||||||
|
function setRefreshToken(newRefreshToken) {
|
||||||
|
refreshToken.value = newRefreshToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置用户信息
|
||||||
|
function setUserInfo(info) {
|
||||||
|
userInfo.value = info
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置菜单
|
||||||
|
function setMenu(newMenu) {
|
||||||
|
menu.value = newMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取菜单
|
||||||
|
function getMenu() {
|
||||||
|
return menu.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除菜单
|
||||||
|
function clearMenu() {
|
||||||
|
menu.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登出
|
||||||
|
function logout() {
|
||||||
|
token.value = ''
|
||||||
|
refreshToken.value = ''
|
||||||
|
userInfo.value = null
|
||||||
|
menu.value = []
|
||||||
|
|
||||||
|
// 重置路由
|
||||||
|
resetRouter()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已登录
|
||||||
|
function isLoggedIn() {
|
||||||
|
return !!token.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
token,
|
||||||
|
refreshToken,
|
||||||
|
userInfo,
|
||||||
|
menu,
|
||||||
|
setToken,
|
||||||
|
setRefreshToken,
|
||||||
|
setUserInfo,
|
||||||
|
setMenu,
|
||||||
|
getMenu,
|
||||||
|
clearMenu,
|
||||||
|
logout,
|
||||||
|
isLoggedIn,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
persist: {
|
||||||
|
key: 'user-store',
|
||||||
|
storage: localStorage,
|
||||||
|
pick: ['token', 'refreshToken', 'userInfo', 'menu']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
// 设置 refresh token
|
|
||||||
function setRefreshToken(newRefreshToken) {
|
|
||||||
refreshToken.value = newRefreshToken
|
|
||||||
localStorage.setItem('refreshToken', newRefreshToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置用户信息
|
|
||||||
function setUserInfo(info) {
|
|
||||||
userInfo.value = info
|
|
||||||
localStorage.setItem('userInfo', JSON.stringify(info))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置菜单
|
|
||||||
function setMenu(newMenu) {
|
|
||||||
menu.value = newMenu
|
|
||||||
localStorage.setItem('MENU', JSON.stringify(newMenu))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取菜单
|
|
||||||
function getMenu() {
|
|
||||||
return menu.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清除菜单
|
|
||||||
function clearMenu() {
|
|
||||||
menu.value = []
|
|
||||||
localStorage.removeItem('MENU')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 登出
|
|
||||||
function logout() {
|
|
||||||
token.value = ''
|
|
||||||
refreshToken.value = ''
|
|
||||||
userInfo.value = null
|
|
||||||
menu.value = []
|
|
||||||
localStorage.removeItem('token')
|
|
||||||
localStorage.removeItem('refreshToken')
|
|
||||||
localStorage.removeItem('userInfo')
|
|
||||||
localStorage.removeItem('MENU')
|
|
||||||
|
|
||||||
// 重置路由
|
|
||||||
resetRouter()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否已登录
|
|
||||||
function isLoggedIn() {
|
|
||||||
return !!token.value
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
token,
|
|
||||||
refreshToken,
|
|
||||||
userInfo,
|
|
||||||
menu,
|
|
||||||
setToken,
|
|
||||||
setRefreshToken,
|
|
||||||
setUserInfo,
|
|
||||||
setMenu,
|
|
||||||
getMenu,
|
|
||||||
clearMenu,
|
|
||||||
logout,
|
|
||||||
isLoggedIn,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|||||||
17
yarn.lock
17
yarn.lock
@@ -1116,6 +1116,11 @@ cross-spawn@^7.0.6:
|
|||||||
shebang-command "^2.0.0"
|
shebang-command "^2.0.0"
|
||||||
which "^2.0.1"
|
which "^2.0.1"
|
||||||
|
|
||||||
|
crypto-js@^4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
|
||||||
|
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
|
||||||
|
|
||||||
cssesc@^3.0.0:
|
cssesc@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||||
@@ -1161,6 +1166,11 @@ define-lazy-prop@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f"
|
resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f"
|
||||||
integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==
|
integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==
|
||||||
|
|
||||||
|
defu@^6.1.4:
|
||||||
|
version "6.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479"
|
||||||
|
integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==
|
||||||
|
|
||||||
delayed-stream@~1.0.0:
|
delayed-stream@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||||
@@ -1880,6 +1890,13 @@ picomatch@^4.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
|
||||||
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
|
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
|
||||||
|
|
||||||
|
pinia-plugin-persistedstate@^4.7.1:
|
||||||
|
version "4.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.7.1.tgz#d13880e0b7efdafd1b73fca3d73cd64ae34bde84"
|
||||||
|
integrity sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==
|
||||||
|
dependencies:
|
||||||
|
defu "^6.1.4"
|
||||||
|
|
||||||
pinia@^3.0.4:
|
pinia@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/pinia/-/pinia-3.0.4.tgz#75dde12784a61e34c1fa6abcd13c1a1061c360c0"
|
resolved "https://registry.yarnpkg.com/pinia/-/pinia-3.0.4.tgz#75dde12784a61e34c1fa6abcd13c1a1061c360c0"
|
||||||
|
|||||||
Reference in New Issue
Block a user