从0开始搭建后台管理系统-01(Login登录)

1.介绍

管理后台的入口页面,在没有登录之前,所有路由跳转都定向到login页面。
本页面特点:

  1. 输入路由,会添加redirect标识,等待登录成功后重定向到redirect记录的路由中,避免每次都跳转首页。
  2. 动画,伪2.5d动画登录表单会根据鼠标的移动而转向。

2.详解

2.1 登录表单

通过函数返回值定义登录表单,函数每次执行都会返回初始值,通过Object.assign()可以用于reset重置表单功能。

function loginSource(){
  return {
    user: 'admin', // 默认
    password: 'hanrui', // 默认
    verifyCode:''
  }
}

let loginForm = reactive(loginSource())

// 重置表单
function resetLoginForm(){
  Object.assign(loginForm,loginSource())
}

2.2 验证码处理

验证码是通过这篇文章js创建验证码创建的,通过mock来模拟传给前端。只要登录提交了并失败了都要刷新一遍验证码。
注:
验证码的验证:验证码是包括验证码的id和验证码的值,后台拿到id后去验证值是否正确。在此获取验证码是在pinia(vueX)中实现的,获取时会记录验证码的id,登录时随着表单一起提交。

代码如下:

// login.vue
const userStore = useUserStore()
const router = useRouter()
const captchaImg = ref<string>('')
async function getCaptcha(){
  const captcha = await userStore.getCaptcha(100,50)
  captchaImg.value = captcha
}
// pinia user.ts
async getCaptcha(width,height){
            const res = await loginApi.getImageCaptcha({width,height})
            this.captchaId = res.data.id
            return res.data.img
        },

2.3 提交登录

登录的逻辑:
在这里我没有做表单验证,正常情况下login中先进行验证再去提交表单,避免携带脏数据的垃圾请求。如果你使用本模板进行二次开发,这是需要添加的功能。
将用户名、密码、验证码、验证码id传递给后台,如果成功,跳转到用户之前的页面首页,如果失败,清空表单重新获取验证码。

// login.vue
function login(){
  userStore.login(
      loginForm.user,
      loginForm.password,
      loginForm.verifyCode
  ).then(res=>{
    if(res){
        router.replace((route.query.redirect as string) ?? '/')
    }else{
      resetLoginForm()
      getCaptcha()
    }
  })
}

类型如下:

// api/login/type
export type LoginParams = {
    username:string;
    password:string;
    captchaId:string;
    verifyCode:string;
}

2.4动画

这个动画放在哪里都行,我这直接就用于了整个表单。
思路:
先计算整个模块的中心点位置。再通过鼠标位置计算鼠标位于中心点的方向和距离,再根据方向和距离大小进行偏转。
perspective是用来调节z轴的视觉感,再通过rotate进行旋转,切勿旋转过多。

function setAnimation(){
  const dom = document.querySelector('.main-wrapper') as HTMLHtmlElement
  if(dom){
    // 计算dom中心点
    const {top,left} = dom.getBoundingClientRect()
    const centerX = dom.offsetWidth/2+left
    const centerY = dom.offsetHeight/2+top
    
    // 添加锁帧
    let lock = false
    dom.addEventListener('mousemove', (event:MouseEvent)=>{
      if(lock) return
      lock = true
      window.requestAnimationFrame(()=>{
        lock = false
        const reactiveX = event.pageX - centerX
        const reactiveY = event.pageY -centerY
        const rateX = reactiveX/centerX *2
        const rateY = reactiveY/centerY *2
        dom.style.transform = `perspective(800px) rotateX(${rateX}deg) rotateY(${rateY}deg)`
      })
    })
  }
}

完整代码如下:

<template>
  <section class="mask center-center-flex">
    <div class="main-wrapper">
      <div class="address">
      </div>
    <section class="login-wrapper ">
      <div class="logo-wrapper">
        <span class="title">登录</span>
      </div>
  <el-form :inline="false" :model="loginForm" class="    login-form">
    <el-form-item>
      <p> 用户名</p>
      <el-input v-model.trim="loginForm.user" placeholder="用户名" clearable  />
    </el-form-item>
    <el-form-item icon>
      <p>密码</p>
      <el-input v-model.trim="loginForm.password"  type="password" placeholder="密码" clearable />
    </el-form-item>
    <el-form-item>
      <p>验证码</p>
      <div class="verify-wrapper">
        <el-input  class="verify-input" v-model.trim="loginForm.verifyCode"  minlength="4"  @keyup.enter="login" placeholder="验证码" />
        <el-image @click="getCaptcha" class="verify-img" :src="captchaImg" ></el-image>
      </div>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" class="login-btn"  @click="login">登录</el-button>
    </el-form-item>
  </el-form>
    </section>
    </div>
  </section>
</template>

<script lang="ts" setup>
import {onMounted, reactive, ref} from 'vue'
import {useUserStore} from '@/store/user'
import {useRoute, useRouter} from 'vue-router'

/** 登录表单数据 */
function loginSource(){
  return {
    user: 'admin',
    password: 'hanrui',
    verifyCode:''
  }
}

let loginForm = reactive(loginSource())

// 重置表单
function resetLoginForm(){
  Object.assign(loginForm,loginSource())
}

/** 路由 */
const route = useRoute()
/** 验证码 */
const userStore = useUserStore()
const router = useRouter()
const captchaImg = ref<string>('')
async function getCaptcha(){
  const captcha = await userStore.getCaptcha(100,50)
  captchaImg.value = captcha
}

/** 登录 */
function login(){
  userStore.login(
      loginForm.user,
      loginForm.password,
      loginForm.verifyCode
  ).then(res=>{
    if(res){
        router.replace((route.query.redirect as string) ?? '/')
    }else{
      resetLoginForm()
      getCaptcha()
    }
  })
}


onMounted(()=>{
  getCaptcha()
  setAnimation()
})

// 动画
function setAnimation(){
  const dom = document.querySelector('.main-wrapper') as HTMLHtmlElement
  if(dom){
    // 计算dom中心点
    const {top,left} = dom.getBoundingClientRect()
    const centerX = dom.offsetWidth/2+left
    const centerY = dom.offsetHeight/2+top
    let lock = false
    dom.addEventListener('mousemove', (event:MouseEvent)=>{
      if(lock) return
      lock = true
      window.requestAnimationFrame(()=>{
        lock = false
        const reactiveX = event.pageX - centerX
        const reactiveY = event.pageY -centerY
        const rateX = reactiveX/centerX *2
        const rateY = reactiveY/centerY *2
        dom.style.transform = `perspective(800px) rotateX(${rateX}deg) rotateY(${rateY}deg)`
      })
    })
  }
}

</script>
<style lang="less" scoped>

@login-font-size:18px;
@login-line-height:40px;

.mask{
  position: fixed;
  width: 100vw;
  height:100vh;
  background-color: #eaeffb;
}
.main-wrapper {
  overflow: hidden;
  position: relative;
  width: 800px;
  height: 500px;
  border-radius: 20px;
  box-shadow: 0 5px 20px #999;
  .address{
    position: absolute;
    width: 520px;
    height: 100%;
    background-color: #79bbff;
    background-image: url("@/assets/image/loginLogo.png");
    background-size: cover;
  }
  .login-wrapper {
    position: absolute;
    right: 0;
    width: 300px;
    height: 500px;
    padding: 20px;
    background-color: #fff;
    border-radius: 20px;
    box-shadow: -1px 0 5px #999;

    .logo-wrapper {
      .title {
        font-size: 30px;
        line-height: 80px;
      }
    }

    .login-form {
      ::v-deep(.el-input__wrapper) {
        height: @login-line-height;
        margin-top: 10px;
        font-size: @login-font-size;
        border-radius: 0;
        border-bottom: 1px solid #000;
        box-shadow: none;
      }

      ::v-deep(.el-form-item__content) {
        font-size: @login-font-size;
      }

      .verify-wrapper {
        display: flex;
        align-items: flex-end;
        width: 100%;

        .verify-input {
          width: 50%;
          margin-right: 5%;
        }

        .verify-img {
          position: absolute;
          right: 0;
          float: right;
          width: 45%;
        }
      }

      .login-btn {
        width: 50%;
        height: @login-line-height;
        margin: 10px auto;
        border-radius: 25px;
      }
    }

  }
}
</style>

3.本框架其他文章链接

GitHub开源链接:GitHub - grxynl/vue3-admin-template: vue3+TypeScript+pinia 后台管理系统模板

其他文章
从0开始搭建后台管理系统(首篇)

从0开始搭建后台管理系统-02(Layout布局)(上篇)

从0开始搭建后台管理系统-02(Layout布局)(下篇)

 

4.结束语

本框架完全免费,框架尚有不足,供前端爱好者一起讨论,一起学习。 源码和文档都制作不易,如果觉得您还可以的话,求一个stars,这是对我最大的支持,也是本框架前进的最大动力。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,一个后台管理系统可以包含以下功能: 1. 用户管理:添加、删除、修改用户信息,包括账号、密码、角色等。 2. 权限管理:设置用户访问的权限,包括模块、菜单、按钮等。 3. 日志管理:记录用户操作日志,包括登录日志、操作日志等。 4. 数据统计:按照不同的条件对系统数据进行统计和分析,如用户数量、订单数量等。 5. 系统设置:对系统进行一些基本设置,如系统名称、Logo、联系方式等。 下面是一个简单的后台管理系统的代码示例,使用了Python的Flask框架和MySQL数据库: ```python from flask import Flask, render_template, request, redirect, url_for, session import pymysql app = Flask(__name__) app.secret_key = 'secret key' # MySQL配置 mysql_host = 'localhost' mysql_user = 'root' mysql_password = 'password' mysql_db = 'db_name' # 连接MySQL def connect_mysql(): return pymysql.connect(host=mysql_host, user=mysql_user, password=mysql_password, db=mysql_db, charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) # 登录页面 @app.route('/', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] conn = connect_mysql() cursor = conn.cursor() cursor.execute("SELECT * FROM users WHERE username=%s AND password=%s", (username, password)) user = cursor.fetchone() cursor.close() conn.close() if user: session['user'] = user return redirect(url_for('home')) else: return render_template('login.html', error='用户名或密码错误') else: return render_template('login.html') # 首页 @app.route('/home') def home(): if 'user' in session: return render_template('home.html', user=session['user']) else: return redirect(url_for('login')) # 用户管理 @app.route('/users') def users(): if 'user' in session and session['user']['role'] == 'admin': conn = connect_mysql() cursor = conn.cursor() cursor.execute("SELECT * FROM users") users = cursor.fetchall() cursor.close() conn.close() return render_template('users.html', users=users) else: return redirect(url_for('login')) # 添加用户 @app.route('/add_user', methods=['POST']) def add_user(): if 'user' in session and session['user']['role'] == 'admin': username = request.form['username'] password = request.form['password'] role = request.form['role'] conn = connect_mysql() cursor = conn.cursor() cursor.execute("INSERT INTO users (username, password, role) VALUES (%s, %s, %s)", (username, password, role)) conn.commit() cursor.close() conn.close() return redirect(url_for('users')) else: return redirect(url_for('login')) # 删除用户 @app.route('/delete_user/<int:user_id>') def delete_user(user_id): if 'user' in session and session['user']['role'] == 'admin': conn = connect_mysql() cursor = conn.cursor() cursor.execute("DELETE FROM users WHERE id=%s", (user_id,)) conn.commit() cursor.close() conn.close() return redirect(url_for('users')) else: return redirect(url_for('login')) # 退出登录 @app.route('/logout') def logout(): session.pop('user', None) return redirect(url_for('login')) if __name__ == '__main__': app.run(debug=True) ``` 在这个示例中,我们使用了Flask框架来搭建Web应用,使用了MySQL数据库来存储数据。我们实现了登录、用户管理、添加用户、删除用户、退出登录等功能。当用户登录成功后,我们会将用户信息存储在session中,方便后续的操作。注意,这只是一个非常简单的示例,实际的后台管理系统需要更多的功能和安全性保证。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值