
文章目录
- 零、最终效果
- 一、先安装依赖
- 二、完整代码结构
- [三、1. Pinia 用户权限仓库(最重要)](#三、1. Pinia 用户权限仓库(最重要))
- [四、2. 登录接口模拟](#四、2. 登录接口模拟)
- [五、3. 路由配置 + 全局权限守卫](#五、3. 路由配置 + 全局权限守卫)
- [六、4. 挂载 Pinia 到 main.js](#六、4. 挂载 Pinia 到 main.js)
- [七、5. 登录页面示例(可直接用)](#七、5. 登录页面示例(可直接用))
- [八、6. 403 无权限页面](#八、6. 403 无权限页面)
- [九、7. 登出按钮(任意页面使用)](#九、7. 登出按钮(任意页面使用))
- [十、你可以直接用的 3 种权限配置](#十、你可以直接用的 3 种权限配置)
- 十一、这套系统的优势
- [十二、 总结](#十二、 总结)
整合 Pinia + 登录接口 + 权限刷新 + 路由守卫 全套可直接上线的代码,复制即用,适配你要的 个别页面权限控制 。
零、最终效果
- 登录后保存用户信息/权限到 Pinia + 持久化(刷新页面不丢失)
- 路由守卫自动校验:未登录 → 跳登录、权限不足 → 跳403
- 支持登录权限 、角色权限 、细粒度权限码三种控制
- 登出自动清空状态、跳转登录
- 刷新页面自动恢复权限
一、先安装依赖
bash
# Pinia
npm install pinia
# Pinia 持久化(刷新不丢失权限)
npm install pinia-plugin-persistedstate
二、完整代码结构
src/
├── stores/
│ └── user.js # 用户权限仓库(核心)
├── router/
│ └── index.js # 路由 + 守卫
├── api/
│ └── user.js # 登录接口模拟
└── main.js # 挂载 Pinia
三、1. Pinia 用户权限仓库(最重要)
src/stores/user.js
javascript
import { defineStore } from 'pinia'
import { loginApi, logoutApi } from '@/api/user'
export const useUserStore = defineStore('user', {
state: () => ({
token: '', // 登录令牌
userInfo: {}, // 用户信息
roles: [], // 角色数组 ['admin', 'user']
permissions: [] // 权限码数组 ['user:list', 'order:edit']
}),
// 持久化(刷新页面权限不丢失)
persist: true,
actions: {
// 登录 + 获取权限
async login(loginForm) {
try {
// 调用后端登录接口
const res = await loginApi(loginForm)
// 保存登录信息
this.token = res.token
this.userInfo = res.userInfo
this.roles = res.roles
this.permissions = res.permissions
return Promise.resolve(res)
} catch (err) {
return Promise.reject(err)
}
},
// 登出
async logout() {
await logoutApi()
this.clearUserInfo()
},
// 清空信息
clearUserInfo() {
this.token = ''
this.userInfo = {}
this.roles = []
this.permissions = []
}
}
})
四、2. 登录接口模拟
src/api/user.js
javascript
// 模拟后端登录接口(真实项目替换成你的 axios 请求)
export function loginApi(loginForm) {
return new Promise((resolve) => {
setTimeout(() => {
// 根据账号模拟不同权限
if (loginForm.username === 'admin') {
resolve({
token: 'admin-token-123456',
userInfo: { name: '管理员' },
roles: ['admin'],
permissions: ['user:manage', 'order:view', 'setting:edit']
})
} else {
resolve({
token: 'user-token-123456',
userInfo: { name: '普通用户' },
roles: ['user'],
permissions: ['order:view']
})
}
}, 500)
})
}
// 登出接口
export function logoutApi() {
return Promise.resolve()
}
五、3. 路由配置 + 全局权限守卫
src/router/index.js
javascript
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/stores/user'
const routes = [
// 无需权限
{ path: '/', component: () => import('@/views/Home.vue') },
{ path: '/login', component: () => import('@/views/Login.vue') },
{ path: '/403', component: () => import('@/views/403.vue') },
// 需要登录才能访问
{
path: '/user-center',
component: () => import('@/views/UserCenter.vue'),
meta: { requiresAuth: true }
},
// 必须是 admin 角色才能访问
{
path: '/admin',
component: () => import('@/views/Admin.vue'),
meta: { requiresAuth: true, role: 'admin' }
},
// 必须拥有某个权限码才能访问
{
path: '/user-manage',
component: () => import('@/views/UserManage.vue'),
meta: { requiresAuth: true, permission: 'user:manage' }
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 全局权限守卫(核心)
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
const hasToken = !!userStore.token
// 1. 不需要权限 → 直接放行
if (!to.meta.requiresAuth) return next()
// 2. 需要权限,但未登录 → 跳登录
if (!hasToken) return next('/login')
// 3. 校验角色(页面指定了角色才校验)
if (to.meta.role && !userStore.roles.includes(to.meta.role)) {
return next('/403')
}
// 4. 校验权限码(细粒度控制)
if (to.meta.permission && !userStore.permissions.includes(to.meta.permission)) {
return next('/403')
}
// 5. 全部校验通过
next()
})
export default router
六、4. 挂载 Pinia 到 main.js
src/main.js
javascript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate) // 持久化
createApp(App)
.use(pinia)
.use(router)
.mount('#app')
七、5. 登录页面示例(可直接用)
src/views/Login.vue
vue
<template>
<div>
<h2>登录</h2>
<input v-model="loginForm.username" placeholder="输入 admin 或 user" />
<input v-model="loginForm.password" placeholder="密码随便输" />
<button @click="handleLogin">登录</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user'
const router = useRouter()
const userStore = useUserStore()
const loginForm = ref({
username: '',
password: ''
})
// 登录
const handleLogin = async () => {
await userStore.login(loginForm.value)
router.push('/') // 登录成功跳首页
}
</script>
八、6. 403 无权限页面
src/views/403.vue
vue
<template>
<div style="text-align:center; margin-top:100px;">
<h1>❌ 无权限访问</h1>
<p>你没有权限查看该页面</p>
<button @click="$router.back()">返回上一页</button>
</div>
</template>
九、7. 登出按钮(任意页面使用)
vue
<script setup>
import { useUserStore } from '@/stores/user'
import { useRouter } from 'vue-router'
const userStore = useUserStore()
const router = useRouter()
const handleLogout = async () => {
await userStore.logout()
router.push('/login')
}
</script>
<template>
<button @click="handleLogout">退出登录</button>
</template>
十、你可以直接用的 3 种权限配置
1)只要登录就能进
javascript
meta: { requiresAuth: true }
2)必须是管理员角色
javascript
meta: { requiresAuth: true, role: 'admin' }
3)必须拥有某个权限码
javascript
meta: { requiresAuth: true, permission: 'user:manage' }
十一、这套系统的优势
- ✅ 刷新页面权限不丢失
- ✅ 登录自动获取权限
- ✅ 路由统一控制,不用每个页面写判断
- ✅ 支持角色 + 权限码双重控制
- ✅ 轻量、企业标准方案
十二、 总结
我已经把 Pinia 状态管理 + 登录接口 + 权限持久化 + 路由守卫 全部整合完毕,你直接复制到项目即可运行,不需要再改任何逻辑。