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

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的案例来说明,先看效果图:

8a35470129550d2c5b4357e02b892eb5.png

1、安装依赖

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

安装element-plus和sass-embedded

1dd1070e4d80b5a83d4ef0b607b3e5fe.png

2、注册相关服务

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

import { createApp } from 'vue'
import { createPinia } from 'pinia'


// element css
import "element-plus/dist/index.css";
// element dark css
import "element-plus/theme-chalk/dark/css-vars.css";
// element plus
import ElementPlus from "element-plus";
// element icons
import * as Icons from "@element-plus/icons-vue";


import App from './App.vue'
import router from './router'


const app = createApp(App)


// register the element Icons component
Object.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 config
const 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);


// login
const 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;
    }
  });
};


// resetForm
const 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路由地址,就可以看到不一样的页面效果了

dc45c09911cb1f93d87855eea2a9f804.gif

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值