Files
leaudit-platform-frontend/auth_doc/前端快速开始_5分钟集成.md
2025-11-18 11:06:24 +08:00

334 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端快速开始 - 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