feat: 1. 添加axios全局路由拦截进行自动添加请求jwt。 2.重新整理路由表。 3. 文档列表新增版本差异对比。 4.菜单路由可访问列表通过对接接口返回,添加全局路由检测。
5. 修改统一认证登录和管理员登录是通过接口形式进行,存储返回的accessToken。 6. 修改交叉评查的部分样式
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
# 前端快速开始 - 5分钟集成RBAC系统
|
||||
|
||||
**版本**: v1.0
|
||||
**日期**: 2025-11-17
|
||||
**目标**: 最快速度集成登录、动态路由和数据访问
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 5分钟集成步骤
|
||||
|
||||
### 步骤1: 安装依赖(1分钟)
|
||||
|
||||
```bash
|
||||
npm install axios pinia vue-router@4 element-plus
|
||||
```
|
||||
|
||||
### 步骤2: 配置环境变量(30秒)
|
||||
|
||||
创建 `.env.development`:
|
||||
|
||||
```env
|
||||
VITE_API_BASE_URL=http://172.16.0.55:8073
|
||||
```
|
||||
|
||||
### 步骤3: 创建请求工具(1分钟)
|
||||
|
||||
**文件**: `src/utils/request.ts`
|
||||
|
||||
```typescript
|
||||
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`
|
||||
|
||||
```typescript
|
||||
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`
|
||||
|
||||
```typescript
|
||||
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`
|
||||
|
||||
```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>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 完成!
|
||||
|
||||
现在你已经完成了基础集成,可以:
|
||||
|
||||
1. ✅ 用户登录
|
||||
2. ✅ 动态加载路由
|
||||
3. ✅ 访问受保护的页面
|
||||
|
||||
---
|
||||
|
||||
## 🚀 下一步:数据访问
|
||||
|
||||
### 查询数据
|
||||
|
||||
```typescript
|
||||
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;
|
||||
};
|
||||
```
|
||||
|
||||
### 创建数据
|
||||
|
||||
```typescript
|
||||
const createDocument = async (data: any) => {
|
||||
const res = await request.post('/documents', data);
|
||||
return res.data;
|
||||
};
|
||||
```
|
||||
|
||||
### 更新数据
|
||||
|
||||
```typescript
|
||||
const updateDocument = async (id: number, data: any) => {
|
||||
const res = await request.patch(`/documents?id=eq.${id}`, data);
|
||||
return res.data;
|
||||
};
|
||||
```
|
||||
|
||||
### 删除数据
|
||||
|
||||
```typescript
|
||||
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怎么办?
|
||||
|
||||
检查组件路径是否正确:
|
||||
|
||||
```typescript
|
||||
// 后端返回: component: "views/Home"
|
||||
// 前端import: @/views/views/Home.vue ❌
|
||||
|
||||
// 修改为:
|
||||
component: () => import(`@/views/Home.vue`)
|
||||
```
|
||||
|
||||
### 3. 普通用户能看到所有数据?
|
||||
|
||||
前端必须手动添加 `user_id` 过滤:
|
||||
|
||||
```typescript
|
||||
// ❌ 错误
|
||||
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
|
||||
Reference in New Issue
Block a user