From 12697c57500f22c49df5e844dceb84a8a37abdea Mon Sep 17 00:00:00 2001 From: TianyangZhang Date: Wed, 25 Mar 2026 15:59:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=99=BB=E5=85=A5=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=B1=82=E8=81=8C=E8=80=85=E5=92=8C=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=91=98=E4=B8=A4=E7=A7=8D=E8=A7=92=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 前端改动: - LoginView.vue 添加角色选择(求职者/管理员) - 求职者使用邮箱验证码登入 - 管理员使用用户名密码登入 - 两种方式在同一页面,通过角色选项卡切换 后端改动: - CustomTokenObtainPairView 改为支持两种登入方式 - 若提供 email+code 则使用邮箱验证码登入 - 若提供 username+password 则使用用户名密码登入 设计: - 求职者可自助注册和邮箱验证码登入 - 管理员由 superadmin 创建,使用用户名密码登入 - 两种登入都返回同样的 JWT token Co-Authored-By: Claude Haiku 4.5 --- offer_backend/apps/accounts/views.py | 34 +++-- offer_frontend/src/views/auth/LoginView.vue | 154 +++++++++++++++++--- 2 files changed, 156 insertions(+), 32 deletions(-) diff --git a/offer_backend/apps/accounts/views.py b/offer_backend/apps/accounts/views.py index 73cfccd..1d552f5 100644 --- a/offer_backend/apps/accounts/views.py +++ b/offer_backend/apps/accounts/views.py @@ -50,11 +50,25 @@ class SendCodeView(APIView): class CustomTokenObtainPairView(TokenObtainPairView): - """自定义邮箱验证码登入视图""" - serializer_class = LoginSerializer + """自定义登入视图,支持邮箱验证码和用户名密码两种方式""" def post(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) + # 判断是否是邮箱验证码登入(有 email 和 code)还是用户名密码登入(有 username 和 password) + if 'email' in request.data and 'code' in request.data: + # 邮箱验证码登入 + return self._login_with_code(request) + elif 'username' in request.data and 'password' in request.data: + # 用户名密码登入 + return self._login_with_password(request) + else: + return Response( + {'error': '请提供正确的登入方式'}, + status=status.HTTP_400_BAD_REQUEST + ) + + def _login_with_code(self, request): + """邮箱验证码登入""" + serializer = LoginSerializer(data=request.data) serializer.is_valid(raise_exception=True) user = serializer.validated_data['user'] @@ -64,17 +78,19 @@ class CustomTokenObtainPairView(TokenObtainPairView): vc.mark_as_verified() # 生成 JWT token - refresh = self.get_token(user) + from rest_framework_simplejwt.tokens import RefreshToken + refresh = RefreshToken.for_user(user) return Response({ 'refresh': str(refresh), 'access': str(refresh.access_token), }, status=status.HTTP_200_OK) - @classmethod - def get_token(cls, user): - """使用 TokenObtainPairSerializer 生成 token""" - token = super().get_token(user) - return token + def _login_with_password(self, request): + """用户名密码登入""" + from rest_framework_simplejwt.serializers import TokenObtainPairSerializer + serializer = TokenObtainPairSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + return Response(serializer.validated_data, status=status.HTTP_200_OK) class RegisterView(APIView): diff --git a/offer_frontend/src/views/auth/LoginView.vue b/offer_frontend/src/views/auth/LoginView.vue index a7a4bac..d60bc9a 100644 --- a/offer_frontend/src/views/auth/LoginView.vue +++ b/offer_frontend/src/views/auth/LoginView.vue @@ -1,12 +1,30 @@