Code前端首页关于Code前端联系我们

flask vue3 admin 怎么搭建前后端分离的后台管理项目?

terry 2小时前 阅读数 27 #Vue
文章标签 flask vue3

为啥选 Flask + Vue3 组合做后台管理系统?

做后台管理系统时,技术选型得兼顾开发效率可维护性,选 Flask 做后端,是因为它轻量灵活,没太多冗余配置,想加功能就装扩展(比如用 Flask-RESTful 快速写接口、Flask-SQLAlchemy 连数据库),适合快速迭代。

前端选 Vue3 呢,Composition API 让逻辑复用更方便,写复杂页面时代码结构更清晰;而且生态里像 Element Plus 这种 UI 库组件丰富,表格、表单、弹窗这些后台常用组件直接拿过来改,省不少时间。

前后端分离还有个隐形优势:团队能分工,前端专注交互和页面逻辑,后端专心搞数据和业务规则,开发时还能并行推进——前端用 mock 数据先做页面,后端单独测接口,联调时换真实地址就行,效率拉满。

搭建前得准备哪些环境和工具?

环境和工具准备到位,后面少踩坑。

后端环境:得装 Python(建议 3.8+,稳定还兼容新库),然后搞个虚拟环境(用 venv 或者 conda,隔离项目依赖),再装核心库:Flask(框架本身)、Flask-SQLAlchemy(操作数据库)、PyJWT(做 token 鉴权)、flask-cors(处理跨域)这些。

前端环境:Node.js 得 16+ 版本,用 Vite 或者 Vue CLI 初始化项目(推荐 Vite,打包和热更新更快),然后装 Element Plus(UI 库)、Pinia(状态管理,替代 Vuex 更轻量)、axios(发请求)这些依赖。

工具辅助:VSCode 装 Python Vue Language Features 插件,写代码时自动补全、查错;Postman 用来测后端接口,确保返回符合预期;数据库工具选 Navicat 或者 DBeaver,方便可视化操作表结构。

Flask 后端怎么快速写出接口和鉴权?

后端核心是提供接口控制权限,按步骤来:

项目结构分层

建个 app 文件夹,里面分模块:

  • routes:放路由和视图函数,比如用户登录、增删改查的接口;
  • models:定义数据库模型(用户、角色、权限这些表结构);
  • utils:工具函数,比如生成 JWT、密码加密;
  • config.py:存配置(数据库地址、JWT 密钥)。

数据库模型示例

Flask-SQLAlchemy 定义用户表:

from app import db, bcrypt  # bcrypt 是密码加密工具
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password = db.Column(db.String(100), nullable=False)
    role = db.Column(db.String(20), default='user')  # 简单角色控制,复杂场景用关联表
    def set_password(self, password):
        self.password = bcrypt.generate_password_hash(password).decode('utf-8')
    def check_password(self, password):
        return bcrypt.check_password_hash(self.password, password)

路由与鉴权

写登录接口,验证成功后发 token:

from flask import request, jsonify
from app.routes import bp
from app.models import User
import jwt
from datetime import datetime, timedelta
@bp.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    user = User.query.filter_by(username=data['username']).first()
    if not user or not user.check_password(data['password']):
        return jsonify({'msg': '账号或密码错误'}), 401
    # 生成 token,过期时间设为2小时
    token = jwt.encode(
        {'id': user.id, 'exp': datetime.utcnow() + timedelta(hours=2)},
        app.config['SECRET_KEY'],
        algorithm='HS256'
    )
    return jsonify({'token': token}), 200

鉴权用装饰器,保护需要登录的接口:

from functools import wraps
def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token:
            return jsonify({'msg': '缺少 token'}), 401
        try:
            data = jwt.decode(token.split(' ')[1], app.config['SECRET_KEY'], algorithms=['HS256'])
            current_user = User.query.get(data['id'])
        except:
            return jsonify({'msg': 'token 无效'}), 401
        return f(current_user, *args, **kwargs)
    return decorated
# 用装饰器保护接口
@bp.route('/user', methods=['GET'])
@token_required
def get_user(current_user):
    return jsonify({'username': current_user.username}), 200

处理跨域

flask-cors 后,在 app.py 里配置:

from flask_cors import CORS
app = Flask(__name__)
CORS(app, resources={r"/api/*": {"origins": "http://localhost:5173"}})  # 前端开发地址

Vue3 前端怎么构建页面和处理请求?

前端核心是页面交互请求管理,步骤如下:

初始化项目与 UI 库

用 Vite 建项目:npm create vite@latest my-admin --template vue,然后装 Element Plus

npm install element-plus @element-plus/icons-vue

main.js 全局注册:

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as Icons from '@element-plus/icons-vue'
const app = createApp(App)
app.use(ElementPlus)
// 全局注册图标
for (const [key, component] of Object.entries(Icons)) {
  app.component(key, component)
}
app.mount('#app')

路由与状态管理

vue-router 配页面路由(登录、仪表盘、用户管理):

import { createRouter, createWebHistory } from 'vue-router'
import Login from './views/Login.vue'
import Dashboard from './views/Dashboard.vue'
import UserManage from './views/UserManage.vue'
const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/login', name: 'Login', component: Login },
    { path: '/', name: 'Dashboard', component: Dashboard, meta: { requiresAuth: true } },
    { path: '/user', name: 'UserManage', component: UserManage, meta: { requiresAuth: true } }
  ]
})

Pinia 存用户信息和 token:

import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
  state: () => ({
    token: localStorage.getItem('token') || '',
    username: '',
    role: ''
  }),
  actions: {
    setUserInfo(info) {
      this.token = info.token
      this.username = info.username
      this.role = info.role
      localStorage.setItem('token', info.token)
    },
    logout() {
      this.$reset()
      localStorage.removeItem('token')
    }
  }
})

请求封装(axios)

封装 axios 统一加 token、处理错误:

import axios from 'axios'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'
const request = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL, // 从环境变量拿后端地址
  timeout: 5000
})
// 请求拦截器:加 token 到请求头
request.interceptors.request.use(config => {
  const userStore = useUserStore()
  if (userStore.token) {
    config.headers.Authorization = `Bearer ${userStore.token}`
  }
  return config
}, error => {
  return Promise.reject(error)
})
// 响应拦截器:处理错误
request.interceptors.response.use(response => {
  return response.data
}, error => {
  if (error.response.status === 401) {
    // token 过期,跳登录页
    const userStore = useUserStore()
    userStore.logout()
    window.location.href = '/login'
    ElMessage.error('登录状态失效,请重新登录')
  } else {
    ElMessage.error(error.response.data.msg || '请求失败')
  }
  return Promise.reject(error)
})
export default request

前后端联调时容易踩哪些坑?

联调是“发现问题 - 改问题”的过程,这些坑要注意:

跨域配置不对

后端 flask-cors 配置的 origins 要和前端实际地址一致,开发时前端是 http://localhost:5173,生产是线上域名,别偷懒用 (生产环境有安全风险)。

接口地址不统一

前端用 .env.development.env.production 区分开发/生产环境的后端地址:

# 开发环境
VITE_API_BASE_URL = 'http://localhost:5000'
# 生产环境
VITE_API_BASE_URL = 'https://your-domain.com/api'

token 传递丢了

前端要确保所有请求的请求头都带 Authorization,axios 拦截器里加了逻辑,但如果手动发请求(比如用 fetch),得自己加头,不然后端鉴权失败。

响应格式不统一

后端接口返回要固定格式,

{
  "code": 200,
  "data": {},
  "msg": "操作成功"
}

前端拦截器统一处理 codecode !== 200 就弹错误提示,别让不同接口返回格式五花八门,前端处理逻辑乱套。

后台管理系统的权限该怎么设计?

权限要从后端控制接口前端控制页面/按钮两方面做:

后端:RBAC 模型

简单场景用“用户 - 角色 - 权限”关联,比如给角色分配权限(如 user:add user:edit),用户关联角色,后端接口加权限装饰器:

def require_permission(perm):
    def decorator(f):
        @wraps(f)
        def wrapped(current_user, *args, **kwargs):
            if perm not in current_user.role.permissions:  # 假设角色表存了权限字符串
                return jsonify({'msg': '没有权限'}), 403
            return f(current_user, *args, **kwargs)
        return wrapped
    return decorator
# 保护需要权限的接口
@bp.route('/user/add', methods=['POST'])
@token_required
@require_permission('user:add')
def add_user(current_user):
    # ... 逻辑

前端:动态控制页面和按钮

  • 页面权限:路由守卫判断,比如某个页面需要 user:edit 权限,在路由 meta 里加 requiresPermission: 'user:edit',然后在 beforeEach 里检查:

    router.beforeEach((to, from, next) => {
      const userStore = useUserStore()
      if (to.meta.requiresPermission) {
        if (userStore.permissions.includes(to.meta.requiresPermission)) {
          next()
        } else {
          next('/403') // 无权限页面
        }
      } else {
        next()
      }
    })
  • 按钮权限:用自定义指令,比如控制“删除”按钮只给有权限的人看:

    // 自定义指令 permission.js
    import { useUserStore } from '@/stores/user'
    export const permissionDirective = {
      mounted(el, binding) {
        const userStore = useUserStore()
        const perm = binding.value
        if (!userStore.permissions.includes(perm)) {
          el.parentNode?.removeChild(el) // 移除按钮
        }
      }
    }
    // 全局注册指令
    app.directive('permission', permissionDirective)
    // 页面中使用
    <el-button v-permission="'user:delete'">删除</el-button>

项目部署到线上要注意什么?

部署要让前后端稳定跑起来,这些细节别漏:

后端部署:Gunicorn + Nginx

  • Gunicorn 启动 Flask(性能比内置服务器好):

    gunicorn -w 4 -b 127.0.0.1:8000 app:app  # 4 个 worker,绑定 8000 端口
  • Nginx 反向代理,把 /api 开头的请求转发给 Gunicorn,静态资源给前端:

    server {
        listen 80;
        server_name your-domain.com;
        location /api {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
        location / {
            root /var/www/frontend/dist;  # 前端打包后的 dist 目录
            try_files $uri $uri/ /index.html;  # 处理前端 history 模式路由
        }
    }

前端部署:打包与静态资源

执行 npm run build 生成 dist 文件夹,传到服务器的 Nginx 静态目录,注意生产环境要把 .env.production 里的后端地址改成线上域名。

数据库与安全

  • 数据库用 MySQL 或 PostgreSQL,部署时配好字符集(如 utf8mb4)和用户权限,避免注入风险。
  • Flask-Migrate 做数据库迁移,改模型后执行 flask db migrateflask db upgrade,保证表结构同步。
  • 开启 HTTPS:用 Let’s Encrypt 申请免费证书,Nginx 配置 443 端口,强制 HTTP 跳 HTTPS。

实际开发中还有哪些细节要注意?

这些“小细节”能避免后期返工:

  • 密码安全:后端存密码别明文,用 werkzeug.security 里的 generate_password_hashcheck_password_hash 加密。
  • JWT 配置:token 过期时间别太长(2 小时),同时搞个 refresh token 续期,避免用户频繁登录。
  • 路由守卫兜底:前端路由加 requiresAuth 元信息,没登录就跳登录页,防止直接输 URL 绕开权限。
  • 错误提示友好:后端返回错误码时,前端用 ElMessage 弹提示(用户名已存在”“token 过期”),别让用户看空白页懵圈。
  • 代码规范:后端用 flake8 检查语法,前端用 ESLint 格式化代码,团队协作时风格统一,维护成本低。

搭建 flask + vue3 admin 项目,核心是前后端各司其职+细节落地,从环境准备到权限设计,每一步都要结合业务场景调整——比如简单项目用角色判断权限,复杂项目用 RBAC 细粒度控制,多测多调,遇到问题拆分成“前端请求→后端接口→数据库操作”环节排查,慢慢就理顺了~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门