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

7.0 KiB
Raw Blame History

前端快速开始 - 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: 创建用户Store2分钟)

文件: 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>

完成!

现在你已经完成了基础集成,可以:

  1. 用户登录
  2. 动态加载路由
  3. 访问受保护的页面

🚀 下一步:数据访问

查询数据

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