bfe39e45a9
5. 修改统一认证登录和管理员登录是通过接口形式进行,存储返回的accessToken。 6. 修改交叉评查的部分样式
7.0 KiB
7.0 KiB
前端快速开始 - 5分钟集成RBAC系统
版本: v1.0 日期: 2025-11-17 目标: 最快速度集成登录、动态路由和数据访问
⚡ 5分钟集成步骤
步骤1: 安装依赖(1分钟)
npm install axios pinia vue-router@4 element-plus
步骤2: 配置环境变量(30秒)
创建 .env.development:
VITE_API_BASE_URL=http://172.16.0.55:8073
步骤3: 创建请求工具(1分钟)
文件: src/utils/request.ts
import axios from 'axios';
import { ElMessage } from 'element-plus';
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://172.16.0.55:8073',
timeout: 30000
});
// 请求拦截器:自动添加Token
service.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
});
// 响应拦截器:统一错误处理
service.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
ElMessage.error('登录已过期');
localStorage.clear();
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default service;
步骤4: 创建用户Store(2分钟)
文件: src/stores/user.ts
import { defineStore } from 'pinia';
import { ref } from 'vue';
import request from '@/utils/request';
import router from '@/router';
export const useUserStore = defineStore('user', () => {
const token = ref('');
const userInfo = ref<any>(null);
const routes = ref<any[]>([]);
// 登录
const login = async (username: string, password: string) => {
const res = await request.post('/auth/login', { username, password });
if (res.data.success) {
token.value = res.data.data.access_token;
userInfo.value = res.data.data.user_info;
localStorage.setItem('token', token.value);
localStorage.setItem('userInfo', JSON.stringify(userInfo.value));
return true;
}
return false;
};
// 获取路由
const fetchRoutes = async () => {
const res = await request.get('/user/routes');
if (res.data.code === 200) {
routes.value = res.data.data.routes;
// 动态注册路由
routes.value.forEach(route => {
router.addRoute({
path: route.route_path,
name: route.route_name,
component: () => import(`@/views/${route.component}.vue`),
meta: { title: route.route_title }
});
});
return routes.value;
}
};
// 登出
const logout = () => {
token.value = '';
userInfo.value = null;
routes.value = [];
localStorage.clear();
router.push('/login');
};
return { token, userInfo, routes, login, fetchRoutes, logout };
});
步骤5: 配置路由守卫(30秒)
文件: src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import { useUserStore } from '@/stores/user';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/login', component: () => import('@/views/Login.vue') }
]
});
router.beforeEach(async (to, from, next) => {
const userStore = useUserStore();
const token = localStorage.getItem('token');
if (token && !userStore.token) {
userStore.token = token;
userStore.userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
}
if (to.path !== '/login' && !token) {
next('/login');
} else if (token && !userStore.routes.length) {
await userStore.fetchRoutes();
next({ ...to, replace: true });
} else {
next();
}
});
export default router;
步骤6: 创建登录页(30秒)
文件: src/views/Login.vue
<template>
<el-form @submit.prevent="handleLogin">
<el-form-item>
<el-input v-model="username" placeholder="用户名" />
</el-form-item>
<el-form-item>
<el-input v-model="password" type="password" placeholder="密码" />
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit" :loading="loading">
登录
</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { useUserStore } from '@/stores/user';
import { ElMessage } from 'element-plus';
const router = useRouter();
const userStore = useUserStore();
const username = ref('');
const password = ref('');
const loading = ref(false);
const handleLogin = async () => {
loading.value = true;
try {
const success = await userStore.login(username.value, password.value);
if (success) {
await userStore.fetchRoutes();
router.push('/home');
} else {
ElMessage.error('登录失败');
}
} catch (error) {
ElMessage.error('登录失败');
} finally {
loading.value = false;
}
};
</script>
✅ 完成!
现在你已经完成了基础集成,可以:
- ✅ 用户登录
- ✅ 动态加载路由
- ✅ 访问受保护的页面
🚀 下一步:数据访问
查询数据
import request from '@/utils/request';
// 查询文档列表
const fetchDocuments = async () => {
const res = await request.get('/documents', {
params: {
user_id: `eq.${userInfo.user_id}`, // 只查自己的数据
limit: 20,
offset: 0
}
});
return res.data;
};
创建数据
const createDocument = async (data: any) => {
const res = await request.post('/documents', data);
return res.data;
};
更新数据
const updateDocument = async (id: number, data: any) => {
const res = await request.patch(`/documents?id=eq.${id}`, data);
return res.data;
};
删除数据
const deleteDocument = async (id: number) => {
const res = await request.delete(`/documents?id=eq.${id}`);
return res.data;
};
📚 测试账号
| 用户名 | 密码 | 角色 | 说明 |
|---|---|---|---|
| 000 | admin06111 | 超级管理员 | 所有权限(29个路由) |
| 001 | gdyc06111 | 普通用户 | 有限权限(19个路由) |
| jy001 | jyyc0814 | 系统管理员 | 所有权限(29个路由) |
🔧 常见问题
1. Token过期怎么办?
后端返回401时,自动清除Token并跳转登录页。
2. 路由404怎么办?
检查组件路径是否正确:
// 后端返回: component: "views/Home"
// 前端import: @/views/views/Home.vue ❌
// 修改为:
component: () => import(`@/views/Home.vue`)
3. 普通用户能看到所有数据?
前端必须手动添加 user_id 过滤:
// ❌ 错误
const docs = await request.get('/documents');
// ✅ 正确
const docs = await request.get('/documents', {
params: { user_id: `eq.${userInfo.user_id}` }
});
📖 详细文档
完整API说明请查看:
- 完整对接文档:
docs/RBAC/前端完整对接文档_RBAC与PostgREST.md
创建时间: 2025-11-17 维护者: Claude Code