Vue3+Vite+Pinia+Naive后台管理系统搭建之八:构建 login.vue 登录页

news2024/11/23 19:25:59

前言

如果对 vue3 的语法不熟悉的,可以移步Vue3.0 基础入门,快速入门。

项目所需要的图片,icon图标(推荐:阿里巴巴矢量图标库)自行获取,命名一致就行。

1. 构建 src/components/CopyRight.vue 版本号组件

<!-- src/components/CopyRight.vue  -->
<script setup>
const props = defineProps({
  color: {
    type: String,
  },
});
</script>

<template>
  <p :style="{ color }">
    版权所有 © YLD科技有限公司,保留所有权利,川F3-20233308号
  </p>
</template>

<style lang="scss" scoped>
p {
  position: absolute;
  bottom: 15px;
  left: 0;
  right: 0;
  text-align: center;
  color: #fff;
  font-family: Arial;
  font-size: 12px;
  letter-spacing: 1px;
}
</style>

2. 构建 src/components/SvgIcon.vue 图标组件

参考在 vue3 中构建 SvgIcon 组件

3. 编辑 src/pages/login.vue

<!-- src/pages/login.vue -->
<script setup>
import cookie from "js-cookie";
import { ref, reactive } from "vue";
import { NButton, NInput, NForm, NFormItem, NCheckbox } from "naive-ui";
import router from "@/router/index.js";
import { useUserStore } from "@/store/user.js";
import { encrypt, decrypt } from "@/utils/jsencrypt";
import { getCodeImg } from "@/api/login.js";

import CopeRight from "@/components/CopyRight.vue";

// 用户状态管理
let userStore = useUserStore();

// 是否需要验证码
let needCode = ref(false);
let codeUrl = ref("");
// 获取图形验证码
function getCode() {
  getCodeImg().then((res) => {
    needCode.value =
      res.captchaEnabled === undefined ? true : res.captchaEnabled;
    if (needCode.value) {
      codeUrl.value = "data:image/gif;base64," + res.img;
      form.uuid = res.uuid;
    }
  });
}
getCode();

// 登录
let formRef = ref(null);
let form = reactive({
  username: "",
  password: "",
  code: "",
  uuid: "",
});
let rules = {
  username: {
    required: true,
    trigger: ["input", "blur"],
    message: "请输入用户名",
  },
  password: {
    required: true,
    trigger: ["input", "blur"],
    message: "请输入密码",
  },
  code: {
    required: true,
    trigger: ["input", "blur"],
    message: "请输入验证码",
  },
};
let rememberMe = ref(false);
let loginBtnState = ref(false);
let handleLogin = () => {
  loginBtnState.value = true;
  formRef.value?.validate((errors) => {
    if (!errors) {
      if (rememberMe.value) {
        cookie.set("username", form.username, { expires: 30 });
        cookie.set("password", encrypt(form.password), { expires: 30 });
        cookie.set("rememberMe", rememberMe.value, { expires: 30 });
      } else {
        cookie.remove("username");
        cookie.remove("password");
        cookie.remove("rememberMe");
      }

      userStore
        .login(form)
        .then(() => {
          window.$msg.success("登录成功");
          router.push({ name: "home" });
        })
        .catch(() => {
          loginBtnState.value = false;
          getCode();
        });
    } else {
      loginBtnState.value = false;
    }
  });
};

// 获取默认登录账号
function getCookie() {
  form.username = cookie.get("username") || "";
  form.password = decrypt(cookie.get("password")) || "";
  rememberMe.value = Boolean(cookie.get("rememberMe")) || false;
}
getCookie();
</script>

<template>
  <div class="login-bg c-center">
    <div class="login__box">
      <div class="login-logo__box">
        <div class="login__title login-logo">
          登 录
        </div>
      </div>
      <n-form
        ref="formRef"
        class="login-form__box"
        :model="form"
        :rules="rules"
        label-placement="left"
      >
        <n-form-item path="username">
          <n-input
            class="login-input"
            v-model:value="form.username"
            placeholder="请输入用户名/手机号"
            @blur="validate"
          >
            <template #prefix>
              <svg-icon name="username" color="grey"></svg-icon>
            </template>
          </n-input>
        </n-form-item>
        <n-form-item path="password">
          <n-input
            class="login-input"
            v-model:value="form.password"
            placeholder="请输入密码"
            type="password"
            show-password-on="mousedown"
            @keyup.enter="handleLogin"
          >
            <template #prefix>
              <svg-icon name="password" color="grey"></svg-icon>
            </template>
          </n-input>
        </n-form-item>
        <n-form-item v-if="needCode" class="login-code" path="code">
          <n-input
            v-model:value="form.code"
            class="login-input login-input_code"
            placeholder="验证码"
            @keyup.enter="handleLogin"
          >
            <template #prefix>
              <svg-icon name="code" color="grey"></svg-icon>
            </template>
          </n-input>
          <img class="login-code-img" :src="codeUrl" @click="getCode" />
        </n-form-item>
        <div class="login-checkbox_box">
          <n-checkbox
            class="login-checkbox"
            v-model:checked="rememberMe"
          ></n-checkbox>
          <span>记住密码</span>
        </div>
        <n-button
          class="login-btn_login"
          type="info"
          @click="handleLogin"
          :loading="loginBtnState"
          :disabled="loginBtnState"
          >登录</n-button
        >
      </n-form>
      <!-- <div class="login-btn_forget" @click="handleForget">忘记密码 ?</div> -->
    </div>
    <cope-right></cope-right>
  </div>
</template>

<style lang="scss">
.n-form-item {
  margin-top: -20px;
}
</style>

<style lang="scss" scoped>
.login-bg {
  width: 100vw;
  height: 100vh;
  background-image: url("@/assets/imgs/login_bg.png");
  background-size: cover;
  background-repeat: no-repeat;
  overflow: hidden;
  .login__box {
    width: 425px;
    padding: 20px 40px;
    border-radius: 10px;
    background: rgba(2, 57, 104, 0.7);
    box-shadow: 0 0 30px rgba(2, 57, 104, 0.7);
    box-sizing: border-box;
    .login__title {
      font-size: 24px;
      text-align: center;
      color: #fff;
    }
  }
}

.login-title {
  margin-bottom: 15px;
  text-align: center;
  letter-spacing: 10px;
  font-size: 18px;
  color: #fff;
  opacity: 1;
}

.login-logo__box {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 20px;

  text {
    display: flex;
    align-items: center;
    font-size: 16px;
    font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS",
      sans-serif;
    color: #1d69a3;

    &::before {
      content: "";
      display: inline-block;
      margin: 0 5px;
      width: 5px;
      height: 5px;
      background: gray;
      border-radius: 50%;
    }
  }
}

.login-logo {
  width: 55%;

  img {
    width: 100%;
    object-fit: cover;
  }
}

.login-code {
  .login-input_code {
    width: 240px;
  }

  .login-btn_code {
    color: #fff;
    margin-left: 20px;
  }
}

.login-btn_login {
  height: 40px;
  width: 100%;
}

.login-input {
  margin-top: 20px;
}

.login-checkbox_box {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin: -16px 0 10px;
  color: #71a1c5;
  font-size: 12px;
}

.login-checkbox {
  margin-right: 5px;
}

.login-btn_forget {
  margin-top: 10px;
  text-align: center;
  font-size: 12px;
  color: #71a1c5;
  cursor: pointer;
}

.login-code-img {
  margin-top: 20px;
  width: 30%;
  height: 35px;
  margin-left: 20px;
  object-fit: contain;
  cursor: pointer;
}
</style>

 

综上

login.vue 登录页构建完成。下一章:layout 动态路由布局

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/752771.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

猿人学第一届刷题18

1.第十八题 jsvmp - 猿人学 问题: 1.第一页请求正常能返回数据 2.第二页开始之后出现{"error": "Unexpected token/Validation failed"} 分析&#xff1a; 1.第二页开始&#xff0c;有带加密参数&#xff0c;直接重发请求无果&#xff0c;应该带了时间戳…

优化产品知识库的 SEO 技巧

在当今数字化的商业环境中&#xff0c;为产品知识库进行搜索引擎优化&#xff08;SEO&#xff09;是至关重要的。随着用户越来越倾向于通过搜索引擎获取信息&#xff0c;优化产品知识库可以帮助你的企业在竞争激烈的市场中脱颖而出。 通过改进SEO&#xff0c;你可以帮助用户找到…

locust性能测试和分布式压测

一、工具介绍 Locust是一个开源的Python性能测试工具&#xff0c;用于模拟大量并发用户访问网站、API等&#xff0c;以测试系统的性能和稳定性。它的主要特点包括&#xff1a; 1.简单易用&#xff1a;Locust基于Python编写&#xff0c;使用方便&#xff0c;学习曲线较低。 2…

数据库基本操作--------MySQL 索引

目录 一、MySQL 索引 1&#xff0e;索引的概念 2&#xff0e;索引的作用 3&#xff0e;创建索引的原则依据 4&#xff0e;索引的分类和创建 &#xff08;1&#xff09;普通索引 ●直接创建索引 &#xff08;2&#xff09;唯一索引 &#xff08;3&#xff09;主键索引 ●创…

禁止浏览器自动填充密码功能,设置自动填充背景色。

禁止浏览器自动填充密码功能&#xff0c;设置自动填充背景色 1、禁止浏览器自动填充密码功能2、设置自动填充背景色&#xff08;阴影效果&#xff09; 1、禁止浏览器自动填充密码功能 text设置autocomplete“off” password设置 autocomplete“new-password” 两个一起设置&am…

雪花算法生成分布式ID源码分析及低频场景下全是偶数的解决办法

目录 雪花算法原理介绍 雪花算法源码分析 低频场景下都是偶数的原因 解决雪花算法的偶数问题 1、切换毫秒时使用随机数 2、抖动上限值加抖动序列号 雪花算法原理介绍 雪花算法(snowflake)最早是twitter内部使用的分布式下的唯一id生成算法&#xff0c;在2014年开源&…

【iOS】消息传递与消息转发

Objective-C是一门非常动态的语言&#xff0c;以至于确定调用哪个方法被推迟到了运行时&#xff0c;而非编译时。与之相反&#xff0c;C语言使用静态绑定&#xff0c;也就是说在编译期就能决定程序运行时所应该调用的函数&#xff0c;所以在C语言中&#xff0c; 如果某个函数没…

MySQL优化 | 如何正确使用索引

文章目录 一、简介1、索引的作用和优势2、索引的基本原理和数据结构 二、常见索引类型和适用场景1、B-Tree索引及其适用场景2、哈希索引及其适用场景 三、选择合适的索引策略1、 选择合适的列作为索引2、使用复合索引和最左前缀原则3、 覆盖索引的使用技巧 四、索引的创建和维护…

IDEA项目报错随笔记录

文章目录 1. 无效的源发行版: 172. java: 无法访问org.springframework.boot.SpringApplication3. java: 程序包org.junit.jupiter.api不存在4. SpringbootTest注解爆红5. maven命令安装本地jar包报错&#xff1a;[拒绝访问]5. maven命令安装本地jar包报错&#xff1a;Unknown …

华为OD机试真题 Java 实现【矩阵元素的边界值】【2023 B卷 100分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明4、再输入5、再输出6、再说明 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff09;》。 刷的越多&#xff…

IOS与Android APP开发的差异性

iPhone和 Android是全球最流行的两种移动平台&#xff0c;有许多不同的开发者开发了应用程序&#xff0c;并将它们发布到市场上。虽然大多数开发者都使用了这两个平台&#xff0c;但您仍然需要了解它们的差异。 虽然 iOS和 Android两个平台都是基于 Linux&#xff0c;但它们却…

流程提效80%!从3个维度搭建高效的数字化采购体系...

采购是企业经营的一个核心环节&#xff0c;也是企业获取利润和市场资源的重要部门&#xff0c;对于企业订单交付尤为关键。特别是在装备制造行业&#xff0c;项目多、零件品类多、定制化高、订单交付周期短&#xff0c;边设计边采购边生产&#xff0c;企业采购负荷重&#xff0…

读书笔记怎么写?心理学名著《乌合之众》读书笔记!

世界上这么多人&#xff0c;看似每个人是一个独立的个体&#xff0c;但其实众生如蚁&#xff0c;彼此相互影响&#xff0c;形成了复杂而神秘的群体心态。 当我们融入群体时&#xff0c;个人的特质会被群体弱化&#xff0c;变得盲从、愚昧甚至暴力&#xff0c;这就是《乌合之众》…

IP地址:超网监控

随着组织的 IT 基础架构的扩展&#xff0c;新设备会不断添加以满足不断增长的网络需求。这就需要跨多个子网管理数百个 IP 地址&#xff0c;以确保每个新添加的设备都配置了唯一的 IP 以连接到网络。为了简化此过程&#xff0c;网络管理员依赖于网络超网的概念&#xff0c;也称…

事务隔离级别是如何实现的

事务隔离级别是如何实现的 数据库系统提供了以下 4 种事务隔离级别 读未提交&#xff1a;解决了回滚覆盖类型的更新丢失&#xff0c;但可能发生脏读现象(一个事务读取到了另一个事务修改但未提交的数据)&#xff0c;也就是可能读取到其他会话中未提交事务修改的数据。 已提交…

解决报错:FUNC glfwErrorCallback GLX: Failed to create context: GLXBadFBConfig

解决报错&#xff1a;FUNC glfwErrorCallback GLX: Failed to create context: GLXBadFBConfig 执行glxinfo | grep OpenGL命令查看系统中的OpenGL信息时&#xff0c;显示以下信息&#xff1a; 根据得到的信息可以看到 OpenGL core profile version string 为 4.5 说明显卡驱…

基于web的考研信息交流平台/考研信息分享平台的设计与实现

摘 要 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;考研信息交流平台也不例外&#xff0c;但目前国内的有些平台仍然都使用人工管理&#xff0c;浏览网站人数越来越多&#xff0c;同时信息量也越来越庞大&#xff0c;人工管理显然已无法应对时…

【复杂系统】拥抱复杂性(第 2 部分数据)

如何通过三个简单&#xff08;ish&#xff09;步骤将您的组织网络化……从数据开始 您的信息不想被困在一个盒子里 在本文的第一部分中&#xff0c;我们确定了工业时代将复杂性组织成漂亮、整洁的线性盒子&#xff0c;尽管这是一种高效的机器制造方式&#xff0c;但这种方法已不…

cucumber基于BBD的自动化测试

1.1 BDD介绍 行为驱动开发(Behavior Driven Development&#xff0c;BDD)简历在测试驱动开发的基础上&#xff0c;并且优化了很多TDD实践者的良好习惯。BDD可以通过自然语言来描写自动化测试&#xff0c;增加自动化的可阅读性. 1.2 cucumber原理 reshen目前有很多BDD的框架&a…

达观曹植大模型正式对外公测!专注于长文本、多语言、垂直化发展

大模型时代到来&#xff0c;国内出现“百模大战”的局面。达观数据自23年3月宣布研发大语言模型以来&#xff0c;一直积极探索大语言模型的专业化、特长化和产品化。通过多年的高质量数据积累&#xff0c;不断精进算法创新&#xff0c;结合多年的文本处理工程实践经验&#xff…