feat: 1. 添加axios全局路由拦截进行自动添加请求jwt。 2.重新整理路由表。 3. 文档列表新增版本差异对比。 4.菜单路由可访问列表通过对接接口返回,添加全局路由检测。

5. 修改统一认证登录和管理员登录是通过接口形式进行,存储返回的accessToken。    6. 修改交叉评查的部分样式
This commit is contained in:
2025-11-18 11:06:24 +08:00
parent 8a50671c39
commit bfe39e45a9
53 changed files with 9503 additions and 2796 deletions
@@ -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: 创建用户Store2分钟)
**文件**: `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