Django + Vue3 前后端分离技术实现自动化测试平台从零到有系列 <第一章> 之 注册登录实现

发布于:2025-09-15 ⋅ 阅读:(20) ⋅ 点赞:(0)

1、简介

自动化框架到平台,需要扩展开发的内容太多,首要的便是用户登录相关的内容;

从自动化框架走向平台,需要扩展开发的内容非常多,其中首要的便是用户登录相关功能。
笔者自 2015 年开始接触自动化测试,一开始使用类似 RF 的框架,编写简单脚本;后来逐步实现自己的自动化框架;再到搭建自动化测试平台和集群性能测试平台。在这一过程中所吸收的技术与思想,以及问题的处理方法,都将在本系列文章中逐步分享。

测试工程师在学习自动化框架时通常会接触到的技术名词有:Python、Selenium、Requests、Unittest、ytest 等等。
但当实现到平台时,它便不同于单纯写脚本与框架,而是会涉及后端与前端;所以除了上述名词之外,还需要掌握 Python 的一些后端框架,如 Flask、FastAPI、Django。
本系列使用 Django 框架实现,而本文主要描述前后端登录相关功能逻辑的实现。

在实现过程中,将会涉及以下关键点:

后端(Django):如何实现前后端分离、基于原生登录机制扩展 token 访问、处理跨域请求等。
前端(Vue3):如何配置路由、封装接口调用、保存登录态、以及编写登录与主页页面。

需要说明的是,本文只是《Django + Vue3 前后端分离技术实现自动化测试平台从零到有系列》的第一章,更多深入内容会在后续章节逐步展开,本文结尾也会预告下一章的内容。

2、开发前准备

2.1、本教程需要的基础:

Python代码编写精通
Django 会写Demo
Vue3 + Element 会写Demo

基础欠些也没有关系,本文会细致的讲解用到的代码,按照一步一步来,也能实现;不懂的可以留言与私聊。

2.2、开发环境准备:

一台安装了下面东西电脑
Mysql 8
Python 3.6 +
Nodejs
Django 4 +
Pycharm
Vscode (开发前端用)

2.3、数据库准备

mysql8新增一个数据库apiauto,备用
字符集:utf8mb4
排序规则:utf8mb4_general_ci

在这里插入图片描述

3、后端实现

3.1、django项目创建与环境搭建

3.1.1 项目创建

一般都用windows开发,找到一个自己专门放python项目的目录,cmd运行ms-dos窗口,执行命令

django-admin startproject [项目名称]

本文后端取名的apiauto,所以命令是

django-admin startproject apiauto

在这里插入图片描述

3.1.2、虚拟环境

1、用Pycharm查看项目初始信息
在这里插入图片描述
2、打开在命令输出框在这里插入图片描述
3、输入下面命令,配置项目虚拟环境

virtualenv atvenv

如下图,运行成功
在这里插入图片描述
4、Pycharm将项目默认虚拟环境运行项目
File ---- Settings ---- Project ---- 下拉框Show all
在这里插入图片描述
点击上图中的 + 号跳转到下图,按照箭头操作,可以配置到当前项目虚拟环境
在这里插入图片描述
6、配置完成后,Pycharm重新加载项目,Terminal命令框会出现虚拟环境标识。
在这里插入图片描述

3.1.3、安装依赖

1、在项目目录下新增一个文件:requirements.txt,添加下列相关内容

asgiref==3.9.1         # ASGI (Asynchronous Server Gateway Interface) 的参考实现,  Django 需要它来支持异步特性(如 async 视图、Channels)
Django==4.2.24      # Web 框架核心库,负责路由、ORM、模板、认证、管理后台等, 这是整个项目的核心框架
django-cors-headers==4.8.0        # 处理 CORS (跨域资源共享),允许前后端分离时跨域请求, 比如前端 Vue/React 在 localhost:3000 请求 Django API 时需要用到;前后端分离的核心
djangorestframework==3.16.1       # Django REST Framework,Django 的 REST API 扩展框架, 提供序列化器、视图集、路由、权限控制等功能;前后端分离的核心
djangorestframework_simplejwt==5.5.1       # JWT (JSON Web Token) 的 DRF 插件, 实现登录认证、刷新 token、访问控制等;前后端分离的核心
mysqlclient==2.2.7         # MySQL 数据库驱动(C 语言实现,性能更高), Django ORM 通过它与 MySQL 通信
PyJWT==2.10.1         # Python 的 JWT 编码/解码库, simplejwt 底层依赖它来生成和校验 token
PyMySQL==1.1.2        # 纯 Python 实现的 MySQL 驱动, 一般在 Windows 或没有 mysqlclient 环境时作为替代
sqlparse==0.5.3            # SQL 解析工具,Django ORM 用它来美化或分析 SQL 查询语句
typing_extensions==4.15.0          # Python 类型注解扩展(向后兼容老版本 Python),一些新语法或新类型在老版本 Python 中需要这个库
tzdata==2025.2             # 时区数据库,用于 Django 的时区支持。在没有系统时区数据库的环境(比如 Windows、容器)中特别有用

在这里插入图片描述
2、运行命令

pip install -r requirements.txt -i https://pypi.mirrors.ustc.edu.cn/simple/

开始安装
在这里插入图片描述等待所有下载完成。
注意下载时不能开代理。

3.1.4、新增用户模块App

1、新增App命令

python manage.py startapp [模块名称]

这里将用户App名定义为users,所以命令便是:

python manage.py startapp users

如下图,users 模块创建好了,可以开始编写代码了
在这里插入图片描述

3.2、编写代码

3.2.1、代码框架

代码目录users里面除了serializers.py,urls.py文件是自己创建用来编写序列化用户数据信息的内容,其他的文件都是新增project与app时自动生成的,具体相关作用见下面目录列表

apiauto/                        # 项目根目录(包含 manage.py 和应用目录)
├─ manage.py                    # Django 管理脚本,项目入口,可运行迁移、启动服务等
│
├─ apiauto/                     # 项目配置目录(和项目同名)
│   ├─ __init__.py              # Python 包标识(空文件即可)
│   ├─ settings.py              # 全局配置文件(数据库、应用、REST、JWT 等)
│   ├─ urls.py                  # 项目 URL 路由入口,分发到各个 app 的 urls.py
│   ├─ wsgi.py                  # WSGI 启动文件,传统部署(Gunicorn/Uwsgi 用)
│   └─ asgi.py                  # ASGI 启动文件,异步部署(Daphne/Uvicorn 用,支持 WebSocket)
│
└─ users/                       # 自定义应用(用户模块)
    ├─ __init__.py              # Python 包标识
    ├─ admin.py                 # Django Admin 后台配置(可注册用户模型)
    ├─ apps.py                  # 应用配置,Django 自动识别 app
    ├─ models.py                # 数据模型定义(ORM 对应数据库表)
    ├─ serializers.py           # DRF 序列化器(定义注册、用户信息、修改密码等数据结构)
    ├─ views.py                 # 视图(API 逻辑处理,例如注册、登录、获取用户信息)
    ├─ urls.py                  # 当前 app 的路由(只负责本 app 的接口)
    └─ test.py                  # 单元测试文件(编写测试用例验证功能是否正确)  

所以自己编写项目在users目录下创建好serializers.py,urls.py文件,然后再开始编写代码。
在这里插入图片描述

3.2.2、Settings.py文件维护

一个新的项目,先需要进行相关配置,才能运行起来;
Settings.py有默认相关配置,本项目增加的配置在下列标识了 “✅ 新增” ,请注意;如果对模糊的,先将下面的内容替换到Settings.py文件中即可,以后再慢慢了解;

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-upf+a6tatnrn9*e2wrj)n7vp!cbxwro=ae267ji*pofr=r^+ns'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # 第三方 ✅ 新增,前后端分离项目必须
    "rest_framework",
    "rest_framework_simplejwt",
    "corsheaders",

    # 自定义 ✅ 新增,增加用户框App
    "users",

]

MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",     # ✅ 新增,兼容跨域
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'apiauto.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'apiauto.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases

DATABASES = {
	# ✅ 新增,本地mysql8数据库连信息配置,apiauto是前面新增加的一个库
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'apiauto',
        'USER': 'root',
        'PASSWORD': 'qwer1234',
        'HOST': '192.168.1.122',
        'PORT': 3307,
        'OPTIONS': {'charset': 'utf8mb4'},     #设置字符类型,防止特殊字符报错,特殊字符是:表情包呀,下载文档转码数据等...
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'


# ✅ 新增,DRF + JWT 配置,
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ),
    "DEFAULT_PERMISSION_CLASSES": (
        "rest_framework.permissions.IsAuthenticated",
    ),
}

# ✅ 新增,允许跨域
CORS_ALLOW_ALL_ORIGINS = True

3.2.3、序列化器代码实现

users目录下serializers.py实现
serializers.py的作用的绑定django框架自带的用户表,然后进行用户管理的一个数据序列化代码。
在这里插入图片描述

序列化是什么意思呢?

它其实分为序列化与反序列化
序列化是将有规则的数据转换成特定格式(如Json、xml等)
反序列化是将特定格式转换是数据形式
一般用到的地方是前数据存储时或前端引用渲染时需要用到这个技术。

实现用户注册,用户信息管理,用户密码修改的一些数据序列化操作。

from django.contrib.auth.models import User
from rest_framework import serializers
from django.contrib.auth.password_validation import validate_password


class RegisterSerializer(serializers.ModelSerializer):
    # 用户注册用的序列化器

    # 密码字段(只写,不返回),并启用 Django 内置密码验证
    password = serializers.CharField(
        write_only=True,
        required=True,
        validators=[validate_password]
    )

    # 第二次确认密码(只写,不返回)
    password2 = serializers.CharField(write_only=True, required=True)

    class Meta:
        # 绑定到 Django 自带的 User 模型
        model = User
        # 需要的字段(序列化/反序列化时使用)
        fields = ("username", "password", "password2", "email", "first_name", "last_name")

    def validate(self, attrs):
        # 自定义验证:检查两次输入的密码是否一致
        if attrs["password"] != attrs["password2"]:
            raise serializers.ValidationError({"password": "两次输入的密码不一致"})
        return attrs

    def create(self, validated_data):
        # 创建用户时,从 validated_data 中取值,创建 User 实例
        user = User.objects.create(
            username=validated_data["username"],
            email=validated_data["email"],
            first_name=validated_data.get("first_name", ""),
            last_name=validated_data.get("last_name", "")
        )
        # 使用 set_password 来保存密码(加密存储,而不是明文)
        user.set_password(validated_data["password"])
        user.save()
        return user


class UserSerializer(serializers.ModelSerializer):
    # 用户信息序列化器(用于返回用户基本信息)
    class Meta:
        model = User
        fields = ("id", "username", "email", "first_name", "last_name")


class UpdatePasswordSerializer(serializers.Serializer):
    # 修改密码序列化器
    old_password = serializers.CharField(required=True)  # 旧密码(用户验证身份)
    new_password = serializers.CharField(required=True)  # 新密码

3.2.4、注册等相关接口实现

users目录下views.py实现

django定义views.py为接口文件,前端调用第一时间会进入到这里。我们一般按规矩办事就好,如果喜欢造自己的轮子可能会出很多异常情况。
在这里插入图片描述

views.py实现接口,如注册接口、用户信息接口、修改密码接口等等
这里为什么没有登录接口呢,因为登录接口使用JWT的,已经被封装好的,待会编写urls.py时直接引入即可。

from django.shortcuts import render

# Create your views here.
from rest_framework.decorators import api_view, permission_classes
from rest_framework import generics, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from django.contrib.auth.models import User

#引入序列化好相关的
from .serializers import RegisterSerializer, UserSerializer, UpdatePasswordSerializer


''' 
注册接口视图:
- 继承 CreateAPIView,自动处理 POST 请求
- 使用 RegisterSerializer 进行用户注册
- 允许任何人访问(AllowAny)
'''
class RegisterView(generics.CreateAPIView):
    queryset = User.objects.all()              # 注册后会在 User 表中创建新用户
    serializer_class = RegisterSerializer      # 使用注册序列化器
    permission_classes = (permissions.AllowAny,)  # 不需要登录即可注册


''' 
用户信息接口视图:
- 继承 RetrieveUpdateAPIView,支持 GET(获取用户信息)、PUT/PATCH(更新用户信息)
- 仅允许已认证用户访问
- get_object 返回当前登录用户(request.user)
'''
class UserProfileView(generics.RetrieveUpdateAPIView):
    serializer_class = UserSerializer
    permission_classes = [permissions.IsAuthenticated]  # 需要登录

    def get_object(self):
        return self.request.user   # 返回当前登录的用户对象


''' 
修改密码接口视图:
- 继承 APIView,自定义 post 方法
- 仅允许已认证用户访问
- 校验旧密码是否正确,再设置新密码
'''
class UpdatePasswordView(APIView):
    permission_classes = [permissions.IsAuthenticated]  # 需要登录

    def post(self, request):
        serializer = UpdatePasswordSerializer(data=request.data)  # 反序列化输入数据
        serializer.is_valid(raise_exception=True)  # 验证输入合法性

        user = request.user  # 当前登录用户
        if not user.check_password(serializer.data.get("old_password")):
            return Response({"error": "旧密码错误"}, status=400)  # 旧密码错误

        # 设置新密码(加密存储)
        user.set_password(serializer.data.get("new_password"))
        user.save()
        return Response({"message": "密码更新成功"})


''' 
获取用户简要信息的函数式视图:
- 使用装饰器 api_view(["GET"]) 定义只支持 GET
- 需要登录才能访问
- 返回用户名和邮箱
'''
@api_view(["GET"])
@permission_classes([permissions.IsAuthenticated])  # 需要登录
def user_profile(request):
    return Response({
        "username": request.user.username,
        "email": request.user.email
    })

3.2.5、接口对外路由

users目录下urls.py实现
urls.py被django定义为路由规则文件。前端所要访问的url路径在这里实现。
在这里插入图片描述

具体实现可看代码注释

#users目录下的urls.py代码
from django.urls import path
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView   #这里导入了jwt实现的登录与刷token接口,不用自己实现
from .views import RegisterView, UserProfileView, UpdatePasswordView    # 导入我们自己实现的注册、信息、密码相关的接口

urlpatterns = [
    # ================= JWT 认证相关 =================

    # 登录接口:
    # - 请求方式:POST
    # - 入参:username, password
    # - 返回:access(短期访问 token)、refresh(长期刷新 token)
    path("login/", TokenObtainPairView.as_view(), name="token_obtain_pair"),

    # 刷新 token 接口:
    # - 请求方式:POST
    # - 入参:refresh(刷新 token)
    # - 返回:新的 access token
    path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),

    # ================= 用户相关 =================

    # 注册接口:
    # - 请求方式:POST
    # - 入参:username, password, password2, email, first_name, last_name
    # - 返回:新建用户的基本信息(不含密码)
    path("register/", RegisterView.as_view(), name="register"),

    # 获取 / 更新用户信息接口:
    # - 请求方式:GET(获取用户信息)、PUT/PATCH(更新用户信息)
    # - 权限:需要登录(access token)
    # - 返回:用户基本信息(id, username, email, first_name, last_name)
    path("user/", UserProfileView.as_view(), name="user_profile"),

    # 修改密码接口:
    # - 请求方式:POST
    # - 入参:old_password, new_password
    # - 权限:需要登录
    # - 返回:{"message": "密码更新成功"} 或 {"error": "旧密码错误"}
    path("user/password/", UpdatePasswordView.as_view(), name="update_password"),
]

注意
users目录urls.py属于users这个模块App的分路由;它的实现不能直接被前端调用到;
所以当我们实现完成users目录下的urls.py后,还得将其引用到apiauto项目目录下的urls.py文件里面
在这里插入图片描述

具体细节看下列代码与注释

#apiauto项目目录下的urls.py文件
from django.contrib import admin
from django.urls import path, include

# 定义整个项目的 URL 路由表(入口)
urlpatterns = [
    # 管理后台的路由,默认提供 Django 的后台管理系统功能
    # 访问 http://127.0.0.1:8000/admin/ 即进入后台
    path('admin/', admin.site.urls),

    # 引入 users 应用下的路由配置
    # 即把 "users/urls.py" 中定义的路由挂载到 "http://127.0.0.1:8000/user/" 路径下
    # 比如 users/urls.py 里有 "register/",那么访问路径就是 "http://127.0.0.1:8000/user/register/"
    path("user/", include("users.urls")),
]

至此,用户注册与登录功能已经完成,可以开始运行调试了。

3.3、调试

3.3.1、初始化数据库

运行之前需要将Django的admin用户管理的models初始化到数据库去,这是django默认自带的用户数据表。这里理解透需要一些基础,大家可以自行去学习一下
命令:

python manage.py makemigrations  #根据在 models.py 中定义或修改的模型(Model),初始化用户表,没有新增models,这里运行不会改为任何模型,默认生成在应用目录下的 migrations/ 文件夹里
python manage.py migrate    #Django 会读取 migrations 文件,然后执行对应的 SQL 语句。将数据更新到数据库;

在这里插入图片描述
等待执行完成,可以去查看对就mysql8的apiauto(这是前面创建的数据库)里的表,可以发现django默认的表都生成了。
在这里插入图片描述

3.3.2、命令运行

python manage.py runserver

在这里插入图片描述

3.3.3、Pycharm编译Debug调试运行

1、工具栏点击三角边的下拉框,选择Edit Configurations
在这里插入图片描述
2、在弹出框中点击+号,选择python
在这里插入图片描述

3、脚本配置
script 字段选择项目manage.py 文件目录
脚本框输入:runserver
如下图,输入好了后,点击Apply,再点击OK保存
在这里插入图片描述

4、保存完成后,工具栏出现了manage的一个运行项,直接点击三角运行,或点击小虫来调试
在这里插入图片描述

5、点击小虫调试开始后,项目调试运行日志是在Console输出,不是在Terminal中输出了,这里要注意;(写到这里,感觉自己好啰嗦,这些谁不会呀,作者在臭显摆)
在这里插入图片描述

3.3.4、注册与登录接口调试

上面已经运行起来了,但不代表逻辑正常了,我们可以来注册一个账号,然后使用这个账号登录一下。这里直接调试接口,Django输出的接口Url支持在浏览器调试

3.3.4.1、浏览器调试Django接口

1、查看接口
在浏览器输入http://127.0.0.1:8000/ 可以看到项目级urls.py的路径
在这里插入图片描述
浏览器输入http://127.0.0.1:8000/user/ 回车后可以看到users App下的路径
在这里插入图片描述
在浏览器里输入http://127.0.0.1:8000/user/login/ 回车后可以打开登录接口的调试页面,这是DRF自带的一个Browsable API renderer接口调试功能,默认开启,方便调试接口,这样无须再下载Postmen与Apifox。
可以在settings.py中关掉,一般在项目正式上线会关掉。
在这里插入图片描述

3.3.4.1、调试注册接口

在浏览器里输入http://127.0.0.1:8000/user/register/,打开注册接口调试页面,输入相关参数

{
    "username": "hunwei",   #登录用户名
    "password": "hunwei2025",   #密码
    "password2": "hunwei2025",  #确认密码
    "email": "hunwei@163.com",
    "first_name": "魂",    #姓魂
    "last_name": "尾"   #名尾
}

输入完成后,点击POST按钮
在这里插入图片描述

注册成功,接口会返回201,并将用户相关信息返回,除了密码,如下图
在这里插入图片描述
这时我们去数据库apiauto查看auth_user表中是否有对应数据,成功数据库一定会有数据的

在这里插入图片描述
至此,平台第一个用户注册成功了

3.3.4.2、调试登录接口

在浏览器里输入http://127.0.0.1:8000/user/login/ 回车后可以打开登录接口的调试页面
输入请求数据,点击POST

{
    "username": "hunwei",
    "password": "hunwei2025"
}

在这里插入图片描述

登录成功返回token
在这里插入图片描述

其中access是正常token,供其他接口使用;refresh是刷新token,供token过期,刷新用户token使用。JWT这么设计的,我们按规则来,无须造轮子。
注意JWT这个技术不是Python Django独有的,在Java等其他语种里都有用这个来实现前后端分离,很稳定。

我们完成了注册,登录,其他接口各位看官可自行去尝试。复刻上面操作便可,问题不大;

下面开始实现前端代码了。

4、前端实现

4.1、项目开始

4.1.1、项目创建

进入到项目apiauto同级,运行CMD(ms-dos),执行创建命令:

npm init vue@latest

静待小会
然后输入前端项目名称,apiauto-views,
在这里插入图片描述
回车,后续选项均回车,遇到是否创建示例代码,都随意,不重要
在这里插入图片描述
直到看到初始化完成的提示,项目创建好了
在这里插入图片描述
可以看到apiauto同级目录下多了一个apiauto-views的项目
在这里插入图片描述
至此,项目创建完成,可以开始下载依赖了

4.1.2、安装依赖

本文使用vscode打开前端项目,然后使用pnpm安装依赖;电脑没有这两个环境的,先处理好环境,再继续看下去

vscode打开项目后,在Terminal框里输入命令

pnpm install

在这里插入图片描述
等待安装完成

4.1.2、首次运行

依赖安装完成后,输入命令运行

pnpm dev

在这里插入图片描述
在浏览器里输入上图中的地址,如下图,哈,对,你做到了!
在这里插入图片描述

4.2、代码框架

前端代码主要是实现对后端登录接口的请求,会封装axios,编写两个页面,Login、Home,实现页面的路由。整个代码的架构如下

apiauto-views/
├─ src/
│  ├─ main.js
│  ├─ App.vue
│  ├─ router/index.js   #页面路由实现
│  ├─ stores/user.js    #登录态处理,token
│  ├─ views/Login.vue
│  ├─ views/Home.vue
│  └─ utils/request.js   #封装请求
├─ package.json

项目创建成功后,没有这些目录
在这里插入图片描述
一个一个自行新增
在这里插入图片描述
目录与文档增加完了后,可以开始写前端代码了

4.3、代码实现

4.3.1、路由、状态管理、请求依赖安装

项目初建时,一般要先安装路由、请求、鉴权相关的内容,所以需要安装这些依赖,命令如下

pnpm install vue-router@4 pinia axios

在这里插入图片描述

4.3.2、main.js代码实现

main.js是Vue项目的运行入口
画出本项目Vue代码执行流程如下:

	index.html (挂载点: <div id="app"></div>)
          │
          ▼
      main.js
   ┌───────────────┐
   │ createApp(App)│  ← 创建 Vue 应用实例
   └───────┬───────┘
           │
           ▼
 ┌─────────┴─────────┐
 │   app.use(Pinia)  │  ← 全局状态管理
 │   app.use(Router) │  ← 路由系统
 └─────────┬─────────┘
           │
           ▼
       App.vue (根组件)
           │
           ▼
   <router-view /> 渲染对应页面
           │
    ┌──────┴──────┐
    ▼             ▼
 Login.vue      Home.vue
   ...            ...

main.js是项目执行的运行入口,所以我们第一时间需要将main.js维护好,使其能调起其他的内容
具体代码如下

import { createApp } from 'vue'
import App from './App.vue'

import router from './router'
import { createPinia } from 'pinia'

const app = createApp(App)

// 使用 Pinia 插件,把全局状态管理能力挂载到应用上
app.use(createPinia())
// 使用 Router 插件,把前端路由功能挂载到应用上
app.use(router)
// 将应用挂载到 public/index.html 中 id="app" 的 DOM 节点上,应用开始运行
app.mount('#app')

4.3.3、App.vue代码修改

改为只使用router路径的视图。路由跳到哪个页面,前端就展示哪个页面。

<template>
  <router-view />
</template>

4.3.4、路由代码实现

前端的路由掌握页面跳转与展示,现在实现的Login与Home页面的流转;
代码文件是:router/index.js
依赖的库是:router

代码相关逻辑,关注每一行的注释,仍有不懂的,可以留言或私聊。

// 0. 从 vue-router 包导入创建路由所需的函数
import { createRouter, createWebHashHistory } from "vue-router"

// 1. 导入页面组件(路由要渲染的视图组件)
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'

// 这个是 Pinia 的 store 工厂函数(不是调用 useUserStore())
// 注意:不要在文件顶层直接调用 useUserStore(),因为 Pinia 必须先被 app.use(createPinia()) 安装
import { useUserStore } from '../stores/user'

// 2. 定义路由表 —— 每一项对应一个“路径 => 组件”的映射
const routes = [
    { path: '/login', name: 'Login', component: Login }, // 登录页
    { path: '/', redirect: '/home' },                    // 根路径重定向到 /home
    { path: '/home', name: 'Home', component: Home }     // 首页
]

// 3. 创建路由实例
const router = createRouter({
    // history 模式:这里使用 hash 模式(URL 带 #),兼容性强,后端无需特殊配置
    history: createWebHashHistory(),
    routes, // 刚定义的路由表
})


// 全局前置守卫:在每次路由跳转前触发(导航被确认前)
// 参数说明:to = 要去的路由,from = 来自哪个路由,next = 继续/中断 导航的回调
router.beforeEach((to, from, next) => {
  const userStore = useUserStore() // 在守卫内再调用 useUserStore()(此时 Pinia 应该已激活)
  // 如果目标不是 /login 且 没有 token(未登录),就跳到登录页
  if (to.path !== '/login' && !userStore.token) {
    next('/login') // 跳转到登录页(可以用 next({ path: '/login' }) 或带 query)
  } else {
    next() // 放行:继续当前导航
  }
})

// 4. 导出路由实例,供 main.js 使用 app.use(router)
export default router

路由编辑好可以,就可以开始正式页面请求与状态管理相关的开发了。

4.3.5、状态管理实现

这段代码是将token状态管理起来,方便后续接口获取使用
对应文件是: stores/user.js
使用的依赖是:Pinia

代码如下,具体细节可看注释;如果仍有不懂的,可以留言或私聊。

// 从 pinia 导入定义 store 的函数
import { defineStore } from 'pinia'

// 导入路由实例,用于在 logout 后跳转页面
import router from '../router'

// 定义并导出一个名为 'user' 的 store(Pinia 的写法)
export const useUserStore = defineStore('user', {
  // state:存放响应式状态(相当于 Vuex 的 state)
  state: () => ({
    // token:优先从 localStorage 读取(保证刷新后仍然有 token),没有则为空字符串
    token: localStorage.getItem('token') || '',
    // refreshToken:同上,用于刷新 access token(如果后端支持)
    refreshToken: localStorage.getItem('refreshToken') || ''
  }),

  // actions:定义修改 state 的方法(可以是同步也可以是异步)
  actions: {
    // 保存 token(登录成功后调用)
    setToken(access, refresh) {
      // 把 access token 存到 store 的 state 中(响应式)
      this.token = access
      // 把 refresh token 存到 state 中
      this.refreshToken = refresh
      // 把 token 持久化到 localStorage,以便刷新页面仍然有效
      localStorage.setItem('token', access)
      localStorage.setItem('refreshToken', refresh)
    },

    // 退出登录(登出)方法
    logout() {
      // 清空 store 中的 token(立即生效)
      this.token = ''
      this.refreshToken = ''
      // 从 localStorage 中移除持久化的 token
      localStorage.removeItem('token')
      localStorage.removeItem('refreshToken')
      // 跳转到登录页(依赖 router 实例)
      router.push('/login')
    }
  }
})

浏览器开发者模式下(F12)的Tap页 Application 下的Storage栏可以看到各网站留存的一些信息。如token相关的,上面代码中的localStorage.setItem就是将token存放到这里
在这里插入图片描述
这个写完便可以开始编写登录请求相关的封装了,它依赖状态管理,所以先实现状态管理

4.3.6、axios接口请求封装

接口请求封装,一些共同特性可以封装起,如token携带,常规异常(300、400等)可以装起来
代码优雅也是一种能力的体现(虽然笔者不见得有多优雅,哈哈哈,但是要有追求嘛~)
代码文件是:utils/request.js
使用的依赖是:axios

代码如下,具体细节可看注释;如果仍有不懂的,可以留言或私聊。

import axios from 'axios'
import { useUserStore } from '../stores/user'

// 创建 axios 实例,统一配置 baseURL
const request = axios.create({
  baseURL: 'http://127.0.0.1:8000', // 后端 Django 项目地址
  timeout: 5000
})

// 标记是否正在刷新 token
let isRefreshing = false
// 存放等待中请求的回调(解决多个请求同时 401 的问题)
let requestsQueue = []

// 请求拦截器:在请求头里加 token
request.interceptors.request.use(config => {
  const userStore = useUserStore()  //这里用了上一节的代码
  if (userStore.token) {
    config.headers.Authorization = `Bearer ${userStore.token}`
  }
  return config
})

// 响应拦截器:处理错误(重点:401 token 失效时刷新)
request.interceptors.response.use(
  res => res.data, // 直接返回 data,这里新手注意,后面的接口请求,res返回结果即是data。
  async err => {
    const userStore = useUserStore()
    const status = err.response?.status

    if (status === 401) {
      const refreshToken = userStore.refreshToken

      if (refreshToken) {
        // 如果没有正在刷新,则发起刷新
        if (!isRefreshing) {
          isRefreshing = true
          try {
            // ✅ 使用 request 实例(会自动带上 baseURL)
            const resp = await request.post('/api/token/refresh/', {
              refresh: refreshToken
            })
            const newToken = resp.access

            // 更新 store & localStorage
            userStore.token = newToken
            localStorage.setItem('token', newToken)

            // 已经拿到新的 token,执行队列里的请求
            requestsQueue.forEach(cb => cb(newToken))
            requestsQueue = []
          } catch (e) {
            // refresh 失败 → 退出登录
            userStore.logout()
            const router = (await import('../router')).default
            router.push('/login')
          } finally {
            isRefreshing = false
          }
        }

        // 返回一个 promise,等刷新完之后再继续原来的请求
        return new Promise(resolve => {
          requestsQueue.push(token => {
            err.config.headers.Authorization = `Bearer ${token}`
            resolve(request(err.config)) // 重新发请求
          })
        })
      } else {
        // 没有 refresh token → 直接退出
        userStore.logout()
        const router = (await import('../router')).default
        router.push('/login')
      }
    }

    return Promise.reject(err)
  }
)

export default request

接口封装已初步完成,现在已经可以使用了,以后写其他章节再慢慢填坑,嘿嘿~

4.3.7、登录页实现

登录页面使用html画出来个登录框,然后使用requests调用登录接口
代码文件是:views/Login.vue
使用的依赖是:/utils/requests

代码如下,具体可见注释,记住:HTML现阶段软件测试必须掌握一门标记语言(别想偷懒~,不然前端自动化都磕磕绊绊);有什么疑问可以留言与私聊。

//template 是vue标记的Html块
<template>
  <div class="login-container">
    <h2 class="title">用户登录</h2>
    <form @submit.prevent="handleLogin" class="login-form">
      <div class="form-item">
        <label for="username">用户名</label>
        <input v-model="username" id="username" type="text" placeholder="请输入用户名" />
      </div>
      <div class="form-item">
        <label for="password">密码</label>
        <input v-model="password" id="password" type="password" placeholder="请输入密码" />
      </div>
      <button type="submit" class="login-btn">登录</button>
    </form>
  </div>
</template>

// script是脚本块
<script setup>
import { ref } from 'vue'
import { useUserStore } from '../stores/user'
import { useRouter } from 'vue-router'
import request from '../utils/requests'

const username = ref('')
const password = ref('')
const userStore = useUserStore()
const router = useRouter()

// 登录事件
const handleLogin = async () => {
  if (!username.value || !password.value) {
    alert('请输入用户名和密码')
    return
  }

  try {
    // 调用 Django JWT 登录接口
    const resp = await request.post('/user/login/', {
      username: username.value,
      password: password.value
    })
    console.log('登录返回:', resp)
    // Django JWT 返回的数据结构是:
    // {
    //   "refresh": "xxxxx",
    //   "access": "xxxxx"
    // }
    const { access, refresh } = resp   //这里使用resp而不使用resp.data,因为requests里面已经封装了res = res.data了

    // 存储到 Pinia(并可持久化到 localStorage)
    userStore.setToken(access, refresh)

    // 登录成功后跳转首页
    router.push('/')
  } catch (err) {
    alert('登录失败,请检查用户名或密码')
    console.error(err)
  }
}
</script>

// 这是样式块
<style scoped>
.login-container {
  max-width: 400px;
  margin: 100px auto;
  padding: 20px;
  border-radius: 10px;
  background: #fff;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.title {
  text-align: center;
  margin-bottom: 20px;
}
.login-form {
  display: flex;
  flex-direction: column;
}
.form-item {
  margin-bottom: 15px;
}
.form-item label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}
.form-item input {
  width: 100%;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 6px;
}
.login-btn {
  padding: 10px;
  background-color: #42b983;
  color: #fff;
  font-weight: bold;
  border: none;
  border-radius: 6px;
  cursor: pointer;
}
.login-btn:hover {
  background-color: #36976a;
}
</style>

登录页写完了,登录成功要跳转到Home页,接下来就开始写Home页了

4.3.8、首页Demo实现

Home是整个项目最常跳转的页面,且其他页面会是它的子页面,以后的章节大家可以看到
代码文件是:views/Home.vue

代码如下,具体可见注释;有什么疑问可以留言与私聊。

<template>
  <div style="padding: 40px;">
    <h2>Home Page</h2>
    <p>欢迎,你已经登录成功 🎉</p>
    <button @click="logout">退出登录</button>
  </div>
</template>

<script setup>
import { useUserStore } from '@/stores/user'
import { useRouter } from 'vue-router'

const userStore = useUserStore()
const router = useRouter()

const logout = () => {
  userStore.logout()
  router.push('/login')
}
</script>

好,前端的代码也编写完毕了。可以调试了

4.4、调试

1、在vscode的Terminal输入命令:

pnpm dev

在这里插入图片描述
2、在浏览器里输入:http://localhost:5173/
出现登录页面
输入账号密码(后端调试时注册的)
hunwei
hunwei2025
在这里插入图片描述
3、点击登录,跳转到Home页了,提示登录成功
在这里插入图片描述
调试完成

5、总结预告

至此自动化平台的注册登录模块已经编写完成。通读本文与实际操作后,可能初步学会Django 与Vue3基础,可以开始慢慢扩展了;还是那句话,有不懂的可以留言与私聊,我看到了便会回复。

预告:下一章节会将自动化平台框架图,数据模型设计出来,预计在下周5之前输出。

再会