diff --git a/package.json b/package.json
index f345c54..d8bb71c 100644
--- a/package.json
+++ b/package.json
@@ -16,9 +16,11 @@
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"axios": "^1.13.2",
+ "crypto-js": "^4.2.0",
"element-plus": "^2.13.1",
"nprogress": "^0.2.0",
"pinia": "^3.0.4",
+ "pinia-plugin-persistedstate": "^4.7.1",
"vue": "^3.5.26",
"vue-i18n": "^11.2.8",
"vue-router": "^4.6.4"
diff --git a/src/App.vue b/src/App.vue
index ffd3b71..8d74a5c 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,4 +1,14 @@
-
+
diff --git a/src/config/index.js b/src/config/index.js
index 9af8d9d..b2e352e 100644
--- a/src/config/index.js
+++ b/src/config/index.js
@@ -1,7 +1,51 @@
export default {
- app_title: 'vueadmin',
+ APP_NAME: 'vueadmin',
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",
}
diff --git a/src/main.js b/src/main.js
index c2ddecf..bf3e91f 100644
--- a/src/main.js
+++ b/src/main.js
@@ -6,7 +6,6 @@ import App from './App.vue'
import router from './router'
import pinia from './stores'
import i18n from './i18n'
-import { useI18nStore } from './stores/modules/i18n'
const app = createApp(App)
@@ -15,8 +14,4 @@ app.use(router)
app.use(pinia)
app.use(i18n)
-// 初始化 i18n store,从 localStorage 读取保存的语言设置
-const i18nStore = useI18nStore()
-i18nStore.initLocale()
-
app.mount('#app')
diff --git a/src/stores/index.js b/src/stores/index.js
index 8a05980..bb44b70 100644
--- a/src/stores/index.js
+++ b/src/stores/index.js
@@ -1,5 +1,9 @@
import { createPinia } from 'pinia'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
+// 注册持久化插件
+pinia.use(piniaPluginPersistedstate)
+
export default pinia
diff --git a/src/stores/modules/i18n.js b/src/stores/modules/i18n.js
index 41e2a8d..1ec7546 100644
--- a/src/stores/modules/i18n.js
+++ b/src/stores/modules/i18n.js
@@ -1,34 +1,35 @@
import { defineStore } from 'pinia'
import i18n from '@/i18n'
-export const useI18nStore = defineStore('i18n', {
- state: () => ({
- currentLocale: 'zh-CN',
- availableLocales: [
- { label: '简体中文', value: 'zh-CN' },
- { label: 'English', value: 'en-US' }
- ]
- }),
+export const useI18nStore = defineStore(
+ 'i18n',
+ {
+ state: () => ({
+ currentLocale: 'zh-CN',
+ availableLocales: [
+ { label: '简体中文', value: 'zh-CN' },
+ { label: 'English', value: 'en-US' }
+ ]
+ }),
- getters: {
- localeLabel: (state) => {
- const locale = state.availableLocales.find((item) => item.value === state.currentLocale)
- return locale ? locale.label : ''
- }
- },
-
- actions: {
- setLocale(locale) {
- this.currentLocale = locale
- i18n.global.locale.value = locale
- localStorage.setItem('locale', locale)
+ getters: {
+ localeLabel: (state) => {
+ const locale = state.availableLocales.find((item) => item.value === state.currentLocale)
+ return locale ? locale.label : ''
+ }
},
- initLocale() {
- const savedLocale = localStorage.getItem('locale')
- if (savedLocale && this.availableLocales.some((item) => item.value === savedLocale)) {
- this.setLocale(savedLocale)
+ actions: {
+ setLocale(locale) {
+ this.currentLocale = locale
+ i18n.global.locale.value = locale
}
+ },
+
+ persist: {
+ key: 'i18n-store',
+ storage: localStorage,
+ pick: ['currentLocale']
}
}
-})
+)
diff --git a/src/stores/modules/layout.js b/src/stores/modules/layout.js
index 43f2d5a..23d25ad 100644
--- a/src/stores/modules/layout.js
+++ b/src/stores/modules/layout.js
@@ -1,57 +1,67 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
-export const useLayoutStore = defineStore('layout', () => {
- // 布局模式:'sidebar', 'top-nav', 'sidebar-top', 'classic'
- const layoutMode = ref('sidebar')
+export const useLayoutStore = defineStore(
+ 'layout',
+ () => {
+ // 布局模式:'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 = () => {
- sidebarCollapsed.value = !sidebarCollapsed.value
- }
+ // 切换侧边栏折叠
+ const toggleSidebar = () => {
+ sidebarCollapsed.value = !sidebarCollapsed.value
+ }
- // 设置布局模式
- const setLayoutMode = (mode) => {
- layoutMode.value = mode
- }
+ // 设置布局模式
+ const setLayoutMode = (mode) => {
+ layoutMode.value = mode
+ }
- // 更新视图标签
- const updateViewTags = (tag) => {
- const index = viewTags.value.findIndex((item) => item.fullPath === tag.fullPath)
- if (index !== -1) {
- viewTags.value[index] = tag
- } else {
- viewTags.value.push(tag)
+ // 更新视图标签
+ const updateViewTags = (tag) => {
+ const index = viewTags.value.findIndex((item) => item.fullPath === tag.fullPath)
+ if (index !== -1) {
+ viewTags.value[index] = tag
+ } else {
+ 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,
- }
-})
+)
diff --git a/src/stores/modules/user.js b/src/stores/modules/user.js
index 5685e03..08ddcfc 100644
--- a/src/stores/modules/user.js
+++ b/src/stores/modules/user.js
@@ -2,79 +2,80 @@ import { ref } from 'vue'
import { defineStore } from 'pinia'
import { resetRouter } from '../../router'
-export const useUserStore = defineStore('user', () => {
- const token = ref(localStorage.getItem('token') || '')
- const refreshToken = ref(localStorage.getItem('refreshToken') || '')
- const userInfo = ref(JSON.parse(localStorage.getItem('userInfo') || 'null'))
- const menu = ref(JSON.parse(localStorage.getItem('MENU') || '[]'))
+export const useUserStore = defineStore(
+ 'user',
+ () => {
+ const token = ref('')
+ const refreshToken = ref('')
+ const userInfo = ref(null)
+ const menu = ref([])
- // 设置 token
- function setToken(newToken) {
- token.value = newToken
- localStorage.setItem('token', newToken)
+ // 设置 token
+ function setToken(newToken) {
+ token.value = 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,
- }
-})
+)
diff --git a/yarn.lock b/yarn.lock
index ebde940..f473b73 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1116,6 +1116,11 @@ cross-spawn@^7.0.6:
shebang-command "^2.0.0"
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:
version "3.0.0"
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"
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:
version "1.0.0"
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"
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:
version "3.0.4"
resolved "https://registry.yarnpkg.com/pinia/-/pinia-3.0.4.tgz#75dde12784a61e34c1fa6abcd13c1a1061c360c0"