BCVP.VUE3系列第四课:登录页设计

科技   科技   2024-09-18 08:20   北京  
BCVP 开发者社区出品

BCVP V3开发

数字化
服务化
绿色化



放假不停歇,趁着假期学习下VUE3相关的内容,一方面是自己保持活力,另一方面也是工作需要,本系列是我的自学教程,如果有从0开始学习VUE3的,可以跟着一起练习下,毕竟前端我也是泥腿子出身,这一系列会使用Vite、TS、Pinia、Element-Plus等新知识点,既是查漏补缺,也是知识分享。

代码地址:

https://github.com/anjoy8/bcvp.vue3.git

这是每篇文章一节课一个分支,方便大家学习,会慢慢的将blog.admin项目进行翻新,使用的后端接口还是BlogCore。

系列文章:

第一课:项目初始化与核心知识点说明

第二课:基于泛型基类封装Axios请求

第三课:封装Axios拦截器



0、本文介绍



本文参考的是开源项目

https://gitee.com/HalseySpicy/Geeker-Admin/tree/template

分步骤讲解登录逻辑,主要看着页面样式简约美观,作为element-plus的案例来说明,先看效果图:






1、安装依赖


在直接安装element-plus的依赖。
安装element-plus和sass-embedded





2、注册相关服务


这个思路很像Netcore,安装完依赖,就在main.ts中注册服务:


import { createApp } from 'vue'import { createPinia } from 'pinia'
// element cssimport "element-plus/dist/index.css";// element dark cssimport "element-plus/theme-chalk/dark/css-vars.css";// element plusimport ElementPlus from "element-plus";// element iconsimport * as Icons from "@element-plus/icons-vue";
import App from './App.vue'import router from './router'
const app = createApp(App)
// register the element Icons componentObject.keys(Icons).forEach(key => {    app.component(key, Icons[key as keyof typeof Icons]);});
app.use(ElementPlus)app.use(createPinia())app.use(router)
app.mount('#app')


这种写法对于习惯vue2的开发来说,还是不太舒服,直接用setup的语法糖更好理解一些:

<template>    <div class="login">        <h1>登录</h1>        <form @submit.prevent="onSubmit">            <div>                <label for="name">用户名</label>                <input v-model="loginForm.name" id="name" type="text" required />            </div>            <div>                <label for="pass">密码</label>                <input v-model="loginForm.pass" id="pass" type="password" required />            </div>            <button type="submit" :disabled="loading">                {{ loading ? '登录中...' : '登录' }}            </button>        </form>        <p v-if="errorMessage" class="error">{{ errorMessage }}</p>    </div></template>

<script setup lang="ts">import { ref } from 'vue';import { useRouter } from 'vue-router';import { login } from '@/api/loginApi';import type { LoginRequest, BaseResponse, LoginResponse } from '@/api/loginApi';
const router = useRouter();const loginForm = ref<LoginRequest>({    name: '',    pass: '',});const loading = ref(false);const errorMessage = ref<string | null>(null);
/** * 登录表单提交处理函数 */const onSubmit = async () => {    loading.value = true;    errorMessage.value = null;
    try {        const response: BaseResponse<LoginResponse> = await login(loginForm.value);
        if (response.success) {            // 登录成功,跳转到首页或者其他页面            router.push({ name: 'Home' });        } else {            // 登录失败,显示错误信息            errorMessage.value = response.msg;        }    } catch (error) {        // 请求错误处理        errorMessage.value = '登录失败,请重试';    } finally {        loading.value = false;    }};</script>
<style scoped>.login {    max-width: 400px;    margin: 0 auto;    padding: 1rem;}
.error {    color: red;    margin-top: 1rem;}</style>



3、调整页面整体模版布局



修改App.vue入口文件内容:

<template>  <el-config-provider :locale="locale" :button="buttonConfig">    <router-view></router-view>  </el-config-provider></template>
<script setup lang="ts">import { reactive } from "vue";import { ElConfigProvider } from "element-plus";
// element button configconst buttonConfig = reactive({ autoInsertSpace: false });</script>


重新构建登录页面

在views文件夹下,新增一个login文件夹,然后新增components/LoginForm.vue、index.scss、index.vue共三个文件

其中index.vue核心内容:

<template>  <div class="login-container flx-center">    <div class="login-box">      <div class="login-left">        <img class="login-left-img" src="@/assets/images/login_left.png" alt="login" />      </div>      <div class="login-form">        <div class="login-logo">          <img class="login-icon" src="@/assets/images/logo.svg" alt="" />          <h2 class="logo-text">BCVP.VUE3</h2>        </div>        <LoginForm />      </div>    </div>  </div></template>
<script setup lang="ts" name="login">import LoginForm from "./components/LoginForm.vue";</script>
<style scoped lang="scss">@import "./index.scss";</style>



4、重点设计登录表单



作为一个核心的登录布局,核心的逻辑都在Form表单LoginForm.vue中:

<template>  <el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" size="large">    <el-form-item prop="name">      <el-input v-model="loginForm.name" placeholder="账号:blogadmin">        <template #prefix>          <el-icon class="el-input__icon">            <user />          </el-icon>        </template>      </el-input>    </el-form-item>    <el-form-item prop="pass">      <el-input v-model="loginForm.pass" type="password" placeholder="密码:blogadmin" show-password        autocomplete="new-password">        <template #prefix>          <el-icon class="el-input__icon">            <lock />          </el-icon>        </template>      </el-input>    </el-form-item>  </el-form>  <div class="login-btn">    <el-button :icon="CircleClose" round size="large" @click="resetForm(loginFormRef)"> 重置 </el-button>    <el-button :icon="UserFilled" round size="large" type="primary" :loading="loading"      @click="loginModule(loginFormRef)">      登录    </el-button>  </div></template>
<script setup lang="ts">import { ref, reactive, onMounted } from "vue";import { useRouter } from "vue-router";import { ElNotification, ElMessage } from "element-plus";import { CircleClose, UserFilled } from "@element-plus/icons-vue";import type { ElForm } from "element-plus";import { login } from '@/api/loginApi';import type { LoginRequest, BaseResponse, LoginResponse } from '@/api/loginApi';import { useAuthStore } from '@/stores/auth';
const router = useRouter();const authStore = useAuthStore();const loginForm = ref<LoginRequest>({  name: '',  pass: '',});
type FormInstance = InstanceType<typeof ElForm>;const loginFormRef = ref<FormInstance>();const loginRules = reactive({  name: [{ required: true, message: "请输入用户名", trigger: "blur" }],  pass: [{ required: true, message: "请输入密码", trigger: "blur" }]});
const loading = ref(false);
// loginconst loginModule = (formEl: FormInstance | undefined) => {  if (!formEl) return;  formEl.validate(async valid => {    if (!valid) return;    loading.value = true;    try {      // 1.执行登录接口      const response: BaseResponse<LoginResponse> = await login(loginForm.value);      if (response.success) {        // 保存 token 到 Pinia        authStore.setToken(response.response.token);
        ElNotification({          title: '首页',          message: "欢迎登录 BCVP.VUE3",          type: "success",          duration: 3000        });
        router.push({ name: 'about' });      } else {        // 登录失败,显示错误信息        ElMessage.error(response.msg || "请求失败!请您稍后重试");;      }    } finally {      loading.value = false;    }  });};
// resetFormconst resetForm = (formEl: FormInstance | undefined) => {  if (!formEl) return;  formEl.resetFields();};
onMounted(() => {  // 监听 enter 事件(调用登录)  document.onkeydown = (e: KeyboardEvent) => {    e = (window.event as KeyboardEvent) || e;    if (e.code === "Enter" || e.code === "enter" || e.code === "NumpadEnter") {      if (loading.value) return;      loginModule(loginFormRef.value);    }  };});</script>
<style scoped lang="scss">@import "../index.scss";</style>


重主题逻辑和之前vue2的差别不大,主要就是用到了element-plus的一些样式和结构,整体更美观

最后修改router路由地址,就可以看到不一样的页面效果了


下篇文章我们写一下另一个重中之重的重头戏,动态渲染左侧菜单,敬请期待。

BCVP代码创新社
专注于 NetCore 相关技术栈的推广,致力于前后端之间的完全分离,从壹开始,让每一个程序员都能从这里学有所成。
 最新文章