From 8d812dd2d3c880e9af79f15794086b904a0eb721 Mon Sep 17 00:00:00 2001
From: pany <939630029@qq.com>
Date: Wed, 24 Aug 2022 16:52:01 +0800
Subject: [PATCH] refactor: Setup Stores
---
src/App.vue | 4 +-
src/api/login.ts | 4 +-
src/components/ThemeSwitch/index.vue | 7 +-
src/config/theme.ts | 11 +-
src/layout/components/Sidebar/index.vue | 9 +-
src/store/modules/app.ts | 99 ++++++++---------
src/store/modules/permission.ts | 38 +++----
src/store/modules/settings.ts | 29 ++---
src/store/modules/tags-view.ts | 77 ++++++-------
src/store/modules/user.ts | 140 +++++++++++-------------
src/utils/cache/cookies.ts | 12 +-
src/utils/cache/localStorage.ts | 17 ++-
12 files changed, 215 insertions(+), 232 deletions(-)
diff --git a/src/App.vue b/src/App.vue
index 3f0bedd..9a7afdb 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -2,8 +2,10 @@
import { useAppStore } from "@/store/modules/app"
import zhCn from "element-plus/lib/locale/lang/zh-cn"
+const appStore = useAppStore()
+
/** 初始化主题 */
-useAppStore().initTheme()
+appStore.initTheme()
/** 将 Element-Plus 的语言设置为中文 */
const locale = zhCn
diff --git a/src/api/login.ts b/src/api/login.ts
index 8bf8002..8d8def2 100644
--- a/src/api/login.ts
+++ b/src/api/login.ts
@@ -6,7 +6,7 @@ interface ILoginData {
}
/** 登录并返回 Token */
-export function login(data: ILoginData) {
+export function loginApi(data: ILoginData) {
return request({
url: "users/login",
method: "post",
@@ -14,7 +14,7 @@ export function login(data: ILoginData) {
})
}
/** 获取用户详情 */
-export function getUserInfo() {
+export function getUserInfoApi() {
return request({
url: "users/info",
method: "post"
diff --git a/src/components/ThemeSwitch/index.vue b/src/components/ThemeSwitch/index.vue
index ca9f210..fd3e1cc 100644
--- a/src/components/ThemeSwitch/index.vue
+++ b/src/components/ThemeSwitch/index.vue
@@ -1,17 +1,16 @@
diff --git a/src/config/theme.ts b/src/config/theme.ts
index f0949c2..8705017 100644
--- a/src/config/theme.ts
+++ b/src/config/theme.ts
@@ -1,5 +1,12 @@
-/** 注册的主题 */
-const themeList = [
+/** 注册的主题, 其中 normal 是必须的, dark 是内置的, 如需更多主题,可自行注册 */
+export type ThemeName = "normal" | "dark"
+
+interface IThemeList {
+ title: string
+ name: ThemeName
+}
+
+const themeList: IThemeList[] = [
{
title: "默认",
name: "normal"
diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue
index 7417e46..bb1df12 100644
--- a/src/layout/components/Sidebar/index.vue
+++ b/src/layout/components/Sidebar/index.vue
@@ -13,15 +13,18 @@ const v3SidebarMenuTextColor = getCssVariableValue("--v3-sidebar-menu-text-color
const v3SidebarMenuActiveTextColor = getCssVariableValue("--v3-sidebar-menu-active-text-color")
const route = useRoute()
+const appStore = useAppStore()
+const permissionStore = usePermissionStore()
+const settingsStore = useSettingsStore()
const sidebar = computed(() => {
- return useAppStore().sidebar
+ return appStore.sidebar
})
const routes = computed(() => {
- return usePermissionStore().routes
+ return permissionStore.routes
})
const showLogo = computed(() => {
- return useSettingsStore().showSidebarLogo
+ return settingsStore.showSidebarLogo
})
const activeMenu = computed(() => {
const { meta, path } = route
diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts
index c9835c6..417e86e 100644
--- a/src/store/modules/app.ts
+++ b/src/store/modules/app.ts
@@ -1,68 +1,59 @@
+import { reactive, ref } from "vue"
import { defineStore } from "pinia"
import { getSidebarStatus, getActiveThemeName, setSidebarStatus, setActiveThemeName } from "@/utils/cache/localStorage"
-import themeList from "@/config/theme"
+import type { ThemeName } from "@/config/theme"
export enum DeviceType {
Mobile,
Desktop
}
-interface IAppState {
- device: DeviceType
- sidebar: {
- opened: boolean
- withoutAnimation: boolean
- }
- /** 主题列表 */
- themeList: { title: string; name: string }[]
- /** 正在应用的主题的名字 */
- activeThemeName: string
+interface ISidebar {
+ opened: boolean
+ withoutAnimation: boolean
}
-export const useAppStore = defineStore({
- id: "app",
- state: (): IAppState => {
- return {
- device: DeviceType.Desktop,
- sidebar: {
- opened: getSidebarStatus() !== "closed",
- withoutAnimation: false
- },
- themeList: themeList,
- activeThemeName: getActiveThemeName() || "normal"
- }
- },
- actions: {
- toggleSidebar(withoutAnimation: boolean) {
- this.sidebar.opened = !this.sidebar.opened
- this.sidebar.withoutAnimation = withoutAnimation
- if (this.sidebar.opened) {
- setSidebarStatus("opened")
- } else {
- setSidebarStatus("closed")
- }
- },
- closeSidebar(withoutAnimation: boolean) {
- this.sidebar.opened = false
- this.sidebar.withoutAnimation = withoutAnimation
+const setClassName = (value: ThemeName) => {
+ document.documentElement.className = value
+}
+
+export const useAppStore = defineStore("app", () => {
+ const sidebar: ISidebar = reactive({
+ opened: getSidebarStatus() !== "closed",
+ withoutAnimation: false
+ })
+ const device = ref(DeviceType.Desktop)
+ /** 正在应用的主题的名字 */
+ const activeThemeName = ref(getActiveThemeName() || "normal")
+
+ const toggleSidebar = (withoutAnimation: boolean) => {
+ sidebar.opened = !sidebar.opened
+ sidebar.withoutAnimation = withoutAnimation
+ if (sidebar.opened) {
+ setSidebarStatus("opened")
+ } else {
setSidebarStatus("closed")
- },
- toggleDevice(device: DeviceType) {
- this.device = device
- },
- setTheme(activeThemeName: string) {
- // 检查这个主题在主题列表里是否存在
- this.activeThemeName = this.themeList.find((theme) => theme.name === activeThemeName)
- ? activeThemeName
- : this.themeList[0].name
- // 应用到 Dom
- document.documentElement.className = this.activeThemeName
- // 持久化
- setActiveThemeName(this.activeThemeName)
- },
- initTheme() {
- // 初始化
- document.documentElement.className = this.activeThemeName
}
}
+ const closeSidebar = (withoutAnimation: boolean) => {
+ sidebar.opened = false
+ sidebar.withoutAnimation = withoutAnimation
+ setSidebarStatus("closed")
+ }
+ const toggleDevice = (value: DeviceType) => {
+ device.value = value
+ }
+ const setTheme = (value: ThemeName) => {
+ activeThemeName.value = value
+ // 应用到 Dom
+ setClassName(activeThemeName.value)
+ // 持久化
+ setActiveThemeName(activeThemeName.value)
+ }
+ const initTheme = () => {
+ // 初始化
+ setClassName(activeThemeName.value)
+ }
+
+ return { device, sidebar, activeThemeName, toggleSidebar, closeSidebar, toggleDevice, setTheme, initTheme }
})
diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts
index a095fc8..d0687e2 100644
--- a/src/store/modules/permission.ts
+++ b/src/store/modules/permission.ts
@@ -1,13 +1,9 @@
+import { ref } from "vue"
import store from "@/store"
import { defineStore } from "pinia"
import { RouteRecordRaw } from "vue-router"
import { constantRoutes, asyncRoutes } from "@/router"
-interface IPermissionState {
- routes: RouteRecordRaw[]
- dynamicRoutes: RouteRecordRaw[]
-}
-
const hasPermission = (roles: string[], route: RouteRecordRaw) => {
if (route.meta && route.meta.roles) {
return roles.some((role) => {
@@ -36,26 +32,22 @@ const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => {
return res
}
-export const usePermissionStore = defineStore({
- id: "permission",
- state: (): IPermissionState => {
- return {
- routes: [],
- dynamicRoutes: []
- }
- },
- actions: {
- setRoutes(roles: string[]) {
- let accessedRoutes
- if (roles.includes("admin")) {
- accessedRoutes = asyncRoutes
- } else {
- accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
- }
- this.routes = constantRoutes.concat(accessedRoutes)
- this.dynamicRoutes = accessedRoutes
+export const usePermissionStore = defineStore("permission", () => {
+ const routes = ref([])
+ const dynamicRoutes = ref([])
+
+ const setRoutes = (roles: string[]) => {
+ let accessedRoutes
+ if (roles.includes("admin")) {
+ accessedRoutes = asyncRoutes
+ } else {
+ accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
+ routes.value = constantRoutes.concat(accessedRoutes)
+ dynamicRoutes.value = accessedRoutes
}
+
+ return { routes, dynamicRoutes, setRoutes }
})
/** 在 setup 外使用 */
diff --git a/src/store/modules/settings.ts b/src/store/modules/settings.ts
index 85abce4..d3769bc 100644
--- a/src/store/modules/settings.ts
+++ b/src/store/modules/settings.ts
@@ -1,25 +1,14 @@
+import { ref } from "vue"
import { defineStore } from "pinia"
import layoutSettings from "@/config/layout"
-interface ISettingsState {
- fixedHeader: boolean
- showSettings: boolean
- showTagsView: boolean
- showSidebarLogo: boolean
- showThemeSwitch: boolean
- showScreenfull: boolean
-}
+export const useSettingsStore = defineStore("settings", () => {
+ const fixedHeader = ref(layoutSettings.fixedHeader)
+ const showSettings = ref(layoutSettings.showSettings)
+ const showTagsView = ref(layoutSettings.showTagsView)
+ const showSidebarLogo = ref(layoutSettings.showSidebarLogo)
+ const showThemeSwitch = ref(layoutSettings.showThemeSwitch)
+ const showScreenfull = ref(layoutSettings.showScreenfull)
-export const useSettingsStore = defineStore({
- id: "settings",
- state: (): ISettingsState => {
- return {
- fixedHeader: layoutSettings.fixedHeader,
- showSettings: layoutSettings.showSettings,
- showTagsView: layoutSettings.showTagsView,
- showSidebarLogo: layoutSettings.showSidebarLogo,
- showThemeSwitch: layoutSettings.showThemeSwitch,
- showScreenfull: layoutSettings.showScreenfull
- }
- }
+ return { fixedHeader, showSettings, showTagsView, showSidebarLogo, showThemeSwitch, showScreenfull }
})
diff --git a/src/store/modules/tags-view.ts b/src/store/modules/tags-view.ts
index eb1d419..b4d57c8 100644
--- a/src/store/modules/tags-view.ts
+++ b/src/store/modules/tags-view.ts
@@ -1,3 +1,4 @@
+import { ref } from "vue"
import { defineStore } from "pinia"
import { _RouteLocationBase, RouteLocationNormalized } from "vue-router"
@@ -6,51 +7,43 @@ export interface ITagView extends Partial {
to?: _RouteLocationBase
}
-interface ITagsViewState {
- visitedViews: ITagView[]
-}
+export const useTagsViewStore = defineStore("tags-view", () => {
+ const visitedViews = ref([])
-export const useTagsViewStore = defineStore({
- id: "tags-view",
- state: (): ITagsViewState => {
- return {
- visitedViews: []
- }
- },
- actions: {
- addVisitedView(view: ITagView) {
- if (this.visitedViews.some((v) => v.path === view.path)) return
- this.visitedViews.push(
- Object.assign({}, view, {
- title: view.meta?.title || "no-name"
- })
- )
- },
- delVisitedView(view: ITagView) {
- for (const [i, v] of this.visitedViews.entries()) {
- if (v.path === view.path) {
- this.visitedViews.splice(i, 1)
- break
- }
- }
- },
- delOthersVisitedViews(view: ITagView) {
- this.visitedViews = this.visitedViews.filter((v) => {
- return v.meta?.affix || v.path === view.path
+ const addVisitedView = (view: ITagView) => {
+ if (visitedViews.value.some((v) => v.path === view.path)) return
+ visitedViews.value.push(
+ Object.assign({}, view, {
+ title: view.meta?.title || "no-name"
})
- },
- delAllVisitedViews() {
- // keep affix tags
- const affixTags = this.visitedViews.filter((tag) => tag.meta?.affix)
- this.visitedViews = affixTags
- },
- updateVisitedView(view: ITagView) {
- for (let v of this.visitedViews) {
- if (v.path === view.path) {
- v = Object.assign(v, view)
- break
- }
+ )
+ }
+ const delVisitedView = (view: ITagView) => {
+ for (const [i, v] of visitedViews.value.entries()) {
+ if (v.path === view.path) {
+ visitedViews.value.splice(i, 1)
+ break
}
}
}
+ const delOthersVisitedViews = (view: ITagView) => {
+ visitedViews.value = visitedViews.value.filter((v) => {
+ return v.meta?.affix || v.path === view.path
+ })
+ }
+ const delAllVisitedViews = () => {
+ // keep affix tags
+ const affixTags = visitedViews.value.filter((tag) => tag.meta?.affix)
+ visitedViews.value = affixTags
+ }
+ const updateVisitedView = (view: ITagView) => {
+ for (let v of visitedViews.value) {
+ if (v.path === view.path) {
+ v = Object.assign(v, view)
+ break
+ }
+ }
+ }
+
+ return { visitedViews, addVisitedView, delVisitedView, delOthersVisitedViews, delAllVisitedViews, updateVisitedView }
})
diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts
index 53f4acc..2d73792 100644
--- a/src/store/modules/user.ts
+++ b/src/store/modules/user.ts
@@ -1,86 +1,78 @@
+import { ref } from "vue"
import store from "@/store"
import { defineStore } from "pinia"
import { usePermissionStore } from "./permission"
import { getToken, removeToken, setToken } from "@/utils/cache/cookies"
import router, { resetRouter } from "@/router"
-import { login, getUserInfo } from "@/api/login"
+import { loginApi, getUserInfoApi } from "@/api/login"
import { RouteRecordRaw } from "vue-router"
-interface IUserState {
- token: string
- roles: string[]
-}
+export const useUserStore = defineStore("user", () => {
+ const token = ref(getToken() || "")
+ const roles = ref([])
-export const useUserStore = defineStore({
- id: "user",
- state: (): IUserState => {
- return {
- token: getToken() || "",
- roles: []
- }
- },
- actions: {
- /** 设置角色数组 */
- setRoles(roles: string[]) {
- this.roles = roles
- },
- /** 登录 */
- login(userInfo: { username: string; password: string }) {
- return new Promise((resolve, reject) => {
- login({
- username: userInfo.username.trim(),
- password: userInfo.password
- })
- .then((res: any) => {
- setToken(res.data.accessToken)
- this.token = res.data.accessToken
- resolve(true)
- })
- .catch((error) => {
- reject(error)
- })
- })
- },
- /** 获取用户详情 */
- getInfo() {
- return new Promise((resolve, reject) => {
- getUserInfo()
- .then((res: any) => {
- this.roles = res.data.user.roles
- resolve(res)
- })
- .catch((error) => {
- reject(error)
- })
- })
- },
- /** 切换角色 */
- async changeRoles(role: string) {
- const token = role + "-token"
- this.token = token
- setToken(token)
- await this.getInfo()
- const permissionStore = usePermissionStore()
- permissionStore.setRoutes(this.roles)
- resetRouter()
- permissionStore.dynamicRoutes.forEach((item: RouteRecordRaw) => {
- router.addRoute(item)
- })
- },
- /** 登出 */
- logout() {
- removeToken()
- this.token = ""
- this.roles = []
- resetRouter()
- },
- /** 重置 Token */
- resetToken() {
- removeToken()
- this.token = ""
- this.roles = []
- }
+ /** 设置角色数组 */
+ const setRoles = (value: string[]) => {
+ roles.value = value
}
+ /** 登录 */
+ const login = (userInfo: { username: string; password: string }) => {
+ return new Promise((resolve, reject) => {
+ loginApi({
+ username: userInfo.username,
+ password: userInfo.password
+ })
+ .then((res: any) => {
+ setToken(res.data.accessToken)
+ token.value = res.data.accessToken
+ resolve(true)
+ })
+ .catch((error) => {
+ reject(error)
+ })
+ })
+ }
+ /** 获取用户详情 */
+ const getInfo = () => {
+ return new Promise((resolve, reject) => {
+ getUserInfoApi()
+ .then((res: any) => {
+ roles.value = res.data.user.roles
+ resolve(res)
+ })
+ .catch((error) => {
+ reject(error)
+ })
+ })
+ }
+ /** 切换角色 */
+ const changeRoles = async (role: string) => {
+ const newToken = role + "-token"
+ token.value = newToken
+ setToken(newToken)
+ await getInfo()
+ const permissionStore = usePermissionStore()
+ permissionStore.setRoutes(roles.value)
+ resetRouter()
+ permissionStore.dynamicRoutes.forEach((item: RouteRecordRaw) => {
+ router.addRoute(item)
+ })
+ }
+ /** 登出 */
+ const logout = () => {
+ removeToken()
+ token.value = ""
+ roles.value = []
+ resetRouter()
+ }
+ /** 重置 Token */
+ const resetToken = () => {
+ removeToken()
+ token.value = ""
+ roles.value = []
+ }
+
+ return { token, roles, setRoles, login, getInfo, changeRoles, logout, resetToken }
})
/** 在 setup 外使用 */
diff --git a/src/utils/cache/cookies.ts b/src/utils/cache/cookies.ts
index b13f84b..537d6c6 100644
--- a/src/utils/cache/cookies.ts
+++ b/src/utils/cache/cookies.ts
@@ -3,6 +3,12 @@
import CacheKey from "@/constants/cacheKey"
import Cookies from "js-cookie"
-export const getToken = () => Cookies.get(CacheKey.TOKEN)
-export const setToken = (token: string) => Cookies.set(CacheKey.TOKEN, token)
-export const removeToken = () => Cookies.remove(CacheKey.TOKEN)
+export const getToken = () => {
+ return Cookies.get(CacheKey.TOKEN)
+}
+export const setToken = (token: string) => {
+ Cookies.set(CacheKey.TOKEN, token)
+}
+export const removeToken = () => {
+ Cookies.remove(CacheKey.TOKEN)
+}
diff --git a/src/utils/cache/localStorage.ts b/src/utils/cache/localStorage.ts
index 5bcedd4..36af5aa 100644
--- a/src/utils/cache/localStorage.ts
+++ b/src/utils/cache/localStorage.ts
@@ -1,9 +1,18 @@
/** 统一处理 localStorage */
import CacheKey from "@/constants/cacheKey"
+import type { ThemeName } from "@/config/theme"
-export const getSidebarStatus = () => localStorage.getItem(CacheKey.SIDEBAR_STATUS)
-export const setSidebarStatus = (sidebarStatus: string) => localStorage.setItem(CacheKey.SIDEBAR_STATUS, sidebarStatus)
+export const getSidebarStatus = () => {
+ return localStorage.getItem(CacheKey.SIDEBAR_STATUS)
+}
+export const setSidebarStatus = (sidebarStatus: "opened" | "closed") => {
+ localStorage.setItem(CacheKey.SIDEBAR_STATUS, sidebarStatus)
+}
-export const getActiveThemeName = () => localStorage.getItem(CacheKey.ACTIVE_THEME_NAME)
-export const setActiveThemeName = (themeName: string) => localStorage.setItem(CacheKey.ACTIVE_THEME_NAME, themeName)
+export const getActiveThemeName = () => {
+ return localStorage.getItem(CacheKey.ACTIVE_THEME_NAME) as ThemeName
+}
+export const setActiveThemeName = (themeName: ThemeName) => {
+ localStorage.setItem(CacheKey.ACTIVE_THEME_NAME, themeName)
+}