第八篇: 登录注册功能实现

目 录

一、引言

二、登录功能

[2.1 后端登录功能](#2.1 后端登录功能)

[2.2 前端实现](#2.2 前端实现)

[2.3 页面展示](#2.3 页面展示)

三、注册功能

[3.1 后端注册功能](#3.1 后端注册功能)

[3.2 前端实现](#3.2 前端实现)

[3.3 页面展示](#3.3 页面展示)

四、改用户Id

五、修改导航头

六、总结


一、引言

上一篇写了购物车、订单、个人中心的功能,今天打算把登录注册功能写完。登录注册相比较来说还是简单一些的。😁

二、登录功能

登录主要是输入账号和密码来校验,我们在后端用户那里写一个登录的功能接口,根据用户去查寻用户的账号和密码是否正确。

2.1 后端登录功能

java 复制代码
@PostMapping("/login")
public Result loginUser(@RequestBody User user){
    //根据账号和密码查询用户
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(User::getNo,user.getNo())
            .eq(User::getPassword,user.getPassword());
    User loginUser = userService.getOne(wrapper);
    if (loginUser == null){
        return Result.fail("用户信息不存在!");
    }
    //返回用户信息
    loginUser.setPassword(null);
    return Result.success(loginUser);
}

2.2 前端实现

前端调用后端登录接口,并把用户信息存到会话存储里面,这个记住密码有一点问题:用户登录退出登录之后再次登录显示用户账号和密码,应该也没有太大问题。

html 复制代码
<template>
  <div class="login-container">
    <div class="login-card">
      <!-- 左半部分 -->
      <div class="login-left">
        <div class="left-content">
          <div class="logo-icon">
            <el-icon :size="60"><ShoppingCart/></el-icon>
          </div>
          <h2 class="slogan">购物商城</h2>
          <p class="desc">让购物更简单,让生活更美好!</p>
          <div class="feature-list">
            <div class="feature-item">
              <el-icon><Goods/></el-icon>
              <span>海量商品</span>
            </div>
            <div class="feature-item">
              <el-icon><Lock/></el-icon>
              <span>安全支付</span>
            </div>
            <div class="feature-item">
              <el-icon><Bicycle/></el-icon>
              <span>极速配送</span>
            </div>
          </div>
        </div>
      </div>
      <!-- 右半部分 -->
      <div class="login-right">
        <div class="right-content">
          <h2 class="welcome-title">欢迎回来</h2>
          <p class="welcome-sub">请登录您的账号</p>
          <el-form
              class="login-form"
              label-width="0"
              ref="loginFormRef"
              :model="loginForm"
              :rules="loginRules"
          >
            <el-form-item prop="no">
              <el-input
                  v-model="loginForm.no"
                  placeholder="请输入账号"
                  size="large"
                  :prefix-icon="User"
              />
            </el-form-item>
            <el-form-item prop="password">
              <el-input
                  v-model="loginForm.password"
                  placeholder="请输入密码"
                  size="large"
                  :prefix-icon="Lock"
                  show-password
              />
            </el-form-item>
            <div class="form-options">
              <el-checkbox v-model="rememberPassword">记住密码</el-checkbox>
              <el-link type="primary" :underline="false">忘记密码?</el-link>
            </div>
            <el-button
                class="login-btn"
                type="primary"
                size="large"
                :loading="loading"
                @click="handleLogin"
            >
              登录
            </el-button>
            <div class="register-link">
              还没有账号?
              <el-link type="primary" @click="goRegister">立即注册</el-link>
            </div>
          </el-form>
        </div>
      </div>
    </div>
  </div>
</template>
javascript 复制代码
<script setup>
  import {Bicycle, Goods, Lock, ShoppingCart, User} from "@element-plus/icons-vue";
  import {reactive, ref} from "vue";
  import axios from "axios";
  import {ElMessage} from "element-plus";
  import {useRouter} from "vue-router";

  const router = useRouter()
  const loginFormRef = ref()
  const loginForm = reactive({
    no: '',
    password: ''
  })
  const loginRules = {
    no: [{ required: true, message: '请输入账号', trigger: 'blur' }],
    password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
  }
  const loading = ref(false)
  const rememberPassword = ref(false)

  //登录功能
  const handleLogin = async () => {
    if (!loginFormRef.value) return
    await loginFormRef.value.validate(async (valid) => {
      if (!valid) return
      loading.value = true
      try {
        const res = await axios.post("http://localhost:8081/user/login",loginForm)
        if (res.data.code === 200){
          ElMessage.success("登录成功!")
          sessionStorage.setItem("CurUser", JSON.stringify(res.data.data))
          if (rememberPassword.value){
            localStorage.setItem("rememberUser", JSON.stringify({
              no: loginForm.no,
              password: loginForm.password
            }))
          }else {
            localStorage.removeItem("rememberUser")
          }
          await router.push('/')
        }else {
          ElMessage.error(res.data.msg || "登录失败!")
        }
      }catch (error) {
        ElMessage.error("网络错误,请稍后重试!")
      }finally {
        loading.value = false
      }
    })
  }

  //去注册
  const goRegister = () => {
    router.push('/register')
  }

  //记住密码
  const loadRememberUser = () => {
    const remember = localStorage.getItem("rememberUser")
    if (remember){
      const user = JSON.parse(remember)
      loginForm.no = user.no
      loginForm.password = user.password
      rememberPassword.value = true
    }
  }

  loadRememberUser()
</script>

2.3 页面展示

三、注册功能

3.1 后端注册功能

后端注册我这个设角色状态的话是因为如果后面写商家模块的时候,需要根据roleId来进行页面选择,2是普通用户,3是商家。

java 复制代码
@PostMapping("/register")
public Result registerUser(@RequestBody User user){
    //检查账户是否存在
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(User::getNo,user.getNo());
    User existUser = userService.getOne(wrapper);
    if (existUser != null){
        return Result.fail("账号已存在!");
    }
    //设角色状态
    if (user.getRoleId() == null){
        user.setRoleId(2);
    }
    // 设置性别默认值(0=男,1=女)
    if (user.getSex() == null){
        user.setSex(1);  // 默认设置为男
    }
    //保存用户
    userService.save(user);
    return Result.success("注册成功!");
}

3.2 前端实现

前端的话是增加了一项选择是用户还是商家的角色然后再去注册。

html 复制代码
<template>
  <div class="register-container">
    <div class="register-card">
      <!-- 左侧:选择角色提示 -->
      <div class="register-left">
        <div class="steps">
          <div class="step" :class="{ active: currentStep === 1 }">
            <div class="step-number">1</div>
            <div class="step-text">选择角色</div>
          </div>
          <div class="step-line" :class="{ active: currentStep >= 2 }"></div>
          <div class="step" :class="{ active: currentStep === 2 }">
            <div class="step-number">2</div>
            <div class="step-text">填写信息</div>
          </div>
        </div>
        <div class="left-bg">
          <el-icon :size="60"><User /></el-icon>
          <h2>加入购物商城</h2>
          <p>选择您的身份,开始购物之旅</p>
        </div>
      </div>

      <!-- 右侧:表单 -->
      <div class="register-right">
        <!-- 选择角色 -->
        <div v-if="currentStep === 1" class="role-select">
          <h2>选择您的身份</h2>
          <p class="subtitle">请选择您要注册的角色类型</p>

          <div class="role-cards">
            <div
                class="role-card"
                :class="{ active: selectedRole === 'user' }"
                @click="selectedRole = 'user'"
            >
              <el-icon :size="40"><User /></el-icon>
              <h3>普通用户</h3>
              <p>购物、下单、查看订单</p>
            </div>
            <div
                class="role-card"
                :class="{ active: selectedRole === 'merchant' }"
                @click="selectedRole = 'merchant'"
            >
              <el-icon :size="40"><ShoppingCart /></el-icon>
              <h3>商家</h3>
              <p>发布商品、管理订单</p>
            </div>
          </div>

          <el-button
              type="primary"
              size="large"
              class="next-btn"
              :disabled="!selectedRole"
              @click="nextStep"
          >
            下一步
          </el-button>
          <div class="login-link">
            已有账号?
            <el-link type="primary" @click="$router.push('/login')">立即登录</el-link>
          </div>
        </div>

        <!-- 填写注册信息 -->
        <div v-else class="register-form">
          <h2>{{ selectedRole === 'user' ? '用户注册' : '商家注册' }}</h2>
          <p class="subtitle">请填写以下信息完成注册</p>

          <el-form
              ref="registerFormRef"
              :model="registerForm"
              :rules="registerRules"
              label-width="0"
              class="form"
          >
            <el-form-item prop="no">
              <el-input
                  v-model="registerForm.no"
                  placeholder="请输入账号"
                  size="large"
                  :prefix-icon="User"
              />
            </el-form-item>

            <el-form-item prop="password">
              <el-input
                  v-model="registerForm.password"
                  type="password"
                  placeholder="请输入密码"
                  size="large"
                  :prefix-icon="Lock"
                  show-password
              />
            </el-form-item>

            <el-form-item prop="confirmPassword">
              <el-input
                  v-model="registerForm.confirmPassword"
                  type="password"
                  placeholder="请确认密码"
                  size="large"
                  :prefix-icon="Lock"
                  show-password
              />
            </el-form-item>

            <el-form-item prop="nickname">
              <el-input
                  v-model="registerForm.username"
                  placeholder="请输入昵称"
                  size="large"
                  :prefix-icon="User"
              />
            </el-form-item>

            <el-form-item prop="phone">
              <el-input
                  v-model="registerForm.phone"
                  placeholder="请输入手机号"
                  size="large"
                  :prefix-icon="Phone"
              />
            </el-form-item>

            <el-button
                type="primary"
                size="large"
                class="register-btn"
                :loading="loading"
                @click="handleRegister"
            >
              注 册
            </el-button>

            <el-button text class="back-btn" @click="prevStep">
              <el-icon><ArrowLeft /></el-icon> 返回选择角色
            </el-button>
          </el-form>
        </div>
      </div>
    </div>
  </div>
</template>
javascript 复制代码
<script setup>
  import { ref, reactive } from 'vue'
  import { useRouter } from 'vue-router'
  import { ElMessage } from 'element-plus'
  import { User, Lock, Phone, ShoppingCart, ArrowLeft } from '@element-plus/icons-vue'
  import axios from 'axios'

  const router = useRouter()

  const currentStep = ref(1)
  const selectedRole = ref('')
  const loading = ref(false)
  const registerFormRef = ref()

  const registerForm = reactive({
    no: '',
    password: '',
    confirmPassword: '',
    username: '',
    phone: '',
    roleId: '',
  })

  //密码检验
  const validateConfirmPassword = (rule, value, callback) => {
    if (value !== registerForm.password) {
      callback(new Error('两次输入的密码不一致'))
    } else {
      callback()
    }
  }

  //校验规则
  const registerRules = {
    no: [
      { required: true, message: '请输入账号', trigger: 'blur' },
      { min: 3, max: 20, message: '账号长度在3-20个字符', trigger: 'blur' }
    ],
    password: [
      { required: true, message: '请输入密码', trigger: 'blur' },
      { min: 3, max: 20, message: '密码长度在3-20个字符', trigger: 'blur' }
    ],
    confirmPassword: [
      { required: true, message: '请确认密码', trigger: 'blur' },
      { validator: validateConfirmPassword, trigger: 'blur' }
    ],
    username: [
      { required: true, message: '请输入昵称', trigger: 'blur' },
      { min: 2, max: 20, message: '昵称长度在2-20个字符', trigger: 'blur' }
    ],
    phone: [
      { required: true, message: '请输入手机号', trigger: 'blur' },
      { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
    ]
  }

  //下一步
  const nextStep = () => {
    if (!selectedRole.value) {
      ElMessage.warning('请选择您的身份')
      return
    }
    currentStep.value = 2
  }

  //退回步骤
  const prevStep = () => {
    currentStep.value = 1
  }

  //注册信息
  const handleRegister = async () => {
    if (!registerFormRef.value) return

    await registerFormRef.value.validate(async (valid) => {
      if (!valid) return

      loading.value = true
      const submitData = {
        no: registerForm.no,
        password: registerForm.password,
        username: registerForm.username,
        phone: registerForm.phone,
        roleId: selectedRole.value === 'user' ? 2 : 3,  // 普通用户=2,商家=3
      }

      try {
        const res = await axios.post('http://localhost:8081/user/register', submitData)
        if (res.data.code === 200) {
          ElMessage.success('注册成功,请登录')
          await router.push('/login')
        } else {
          ElMessage.error(res.data.msg || '注册失败')
        }
      } catch (error) {
        ElMessage.error('网络错误,请稍后重试')
      } finally {
        loading.value = false
      }
    })
  }
</script>

3.3 页面展示

四、改用户Id

我之前是写死的用户id,现在我们把用户信息存到sessionStorage里面了,可以直接拿到用户信息了。

新建一个JS文件里面写获取当前用户Id的方法,之后在需要修改的页面中引入该方法。

javascript 复制代码
//获取当前用户登录ID
export const getUserId = () => {
    const user = sessionStorage.getItem("CurUser")
    if (!user) return null
    const userData = JSON.parse(user)
    return userData.id
}
javascript 复制代码
import {getUserId} from "@/utils/auth";

//在方法中使用
const userId = getUserId()
    if (!userId) return
await axios.get(`http://localhost:8081/usercart/clear?userId=${userId}`)

五、修改导航头

根据不同状态显示不同页面,登录之后显示用户头像,退出登录后恢复以前未登录状态显示登录和注册按钮。

html 复制代码
<template>
  <!-- Header -->
  <div class="header">
    <div class="logo" @click="$router.push('/home')">  <!-- Logo显示图标加文字 -->
      <el-icon><Shop/></el-icon>
      <span>购物商城</span>
    </div>
    <div class="search-box" v-if="showSearch">  <!-- 搜索框 -->
      <el-input
          v-model="searchKeyword"
          placeholder="搜索商品"
          size="large"
          style="width: 400px"
          @keyup.enter="handleSearch"
      >
        <template #prefix>
          <el-icon><Search/></el-icon>
        </template>
      </el-input>
    </div>
    <div class="nav-menu">  <!-- 导航菜单 -->
      <el-button text @click="$router.push('/home')">首页</el-button>
      <el-button text @click="checkLoginAndGo('/cart')">购物车</el-button>
      <el-button text @click="checkLoginAndGo('/order')">我的订单</el-button>
      <el-button text @click="checkLoginAndGo('/personal')">个人中心</el-button>
      <el-dropdown v-if="isLogin">
        <el-avatar :src="userAvatar" :size="32" />
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item divided @click="handleLogout">退出登录</el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
      <template v-else>
        <el-button type="primary" @click="$router.push('/login')">登录</el-button>
        <el-button type="success" @click="$router.push('/register')">注册</el-button>
      </template>
    </div>
  </div>
</template>
javascript 复制代码
<script setup>
  import {Search, Shop} from "@element-plus/icons-vue";
  import {useRouter, useRoute} from "vue-router";
  import {computed, ref} from "vue";
  import {ElMessage} from "element-plus";

  const router = useRouter()
  const route = useRoute()
  const searchKeyword = ref('')

  //添加登录检查函数
  const checkLoginAndGo = (path) => {
    if (!isLogin.value) {
      ElMessage.warning('请先登录后再访问')
      router.push('/login')
      return
    }
    router.push(path)
  }

  // 根据路由判断是否显示搜索框
  const showSearch = computed(() => {
    // 个人中心页面不显示搜索框
    return route.path !== '/personal'
  })

  //判断是否登录
  const isLogin = computed(() => {
    return !!sessionStorage.getItem("CurUser")
  })

  //获取用户头像
  const userAvatar = computed(() => {
    const user = sessionStorage.getItem("CurUser")
    if (user){
      const userData = JSON.parse(user)
      return userData.avatar || "https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
    }
    return "https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
  })

  //退出登录
  const handleLogout = () => {
    sessionStorage.removeItem("CurUser")
    ElMessage.success("已退出登录!")
    router.push('/home').then(() => {
      router.go(0)
    })
  }

  //回车搜索
  const handleSearch = () => {
    const keyword = searchKeyword.value.trim()
    const currentPath = route.path
    router.push({
      path: currentPath,
      query: { ...route.query, keyword: keyword || undefined }
    })
  }
</script>

六、总结

下一篇完善商品页面的功能,然后用户模块应该就可以结束了!😁

相关推荐
ZC跨境爬虫1 小时前
跟着 MDN 学CSS day_37:(从文档流到粘性定位的底层原理)
前端·javascript·css·ui·html
码语智行1 小时前
shp文件生成
java
十九画生2 小时前
从“会用函数”到“理解函数”:JavaScript 中函数为什么也是对象?
javascript
plainGeekDev2 小时前
AlertDialog → DialogFragment
android·java·kotlin
薛定谔的悦2 小时前
光伏-储能-负荷联合预测:给 EMS 装上“预知能力“
java·数据库·人工智能·python·储能
大菜菜小个子2 小时前
template<typename T>使用
java·开发语言·算法
Refrain_zc2 小时前
Android开发: 拒绝 Activity 重建!onConfigurationChanged 实现平板横竖屏无缝切换
java
方也_arkling2 小时前
【Java-Day15】API篇-ArrayList集合
java·开发语言
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第89题】【Mysql篇】第19题:Hash 索引和 B+ 树索引的区别?它们在使用方面的区别?
java·数据库·mysql·面试·哈希算法