Vue--"Vue3 Complete Guide to Creating an Extensible Project Management System Background (2)

Today I started to use vue3 + ts to build a project management background, because the article will explain the writing of code in every part of the project, so this project will be divided into several articles to explain, and I will explain it in the last article The project code is open-sourced on my GitHub , and you can download and run it yourself. I hope that friends who are helpful to this article can pay more attention to this column and learn more front-end Vue knowledge. Then, at the beginning of the article, I will briefly introduce the ones used in this project. What are the aspects of the technology stack ( read the technology that can be learned in this article ):

vite : A fast, lightweight and feature-rich front-end building tool that helps developers build modern web applications more efficiently.

pnpm : A high-performance, lightweight npm replacement that helps developers handle application dependencies more efficiently.

Vue3 : The latest version of Vue.js, a progressive JavaScript framework for building user interfaces.

TypeScript : A superset of JavaScript that provides static type checking to make the code more robust.

Animate : A JavaScript-based animation framework that enables developers to easily create various cool animation effects.

vue-router : The routing manager officially provided by Vue.js is tightly coupled with Vue.js, which is very convenient to use with Vue.js.

Pinia : A Vuex alternative built with Vue3 that is responsive and provides a very simple API for state management.

element-plus : A Vue.js 3.0-based UI component library for building high-quality responsive web applications.

axios : Promise-based HTTP client that can be used in the browser and node.js.

three : JavaScript-based WebGL library, developers can write high-performance, high-quality 3D scene rendering effects.

echarts : JavaScript-based visual charting library that supports multiple types of charts and can be installed as needed.

Of course, there are many other third-party libraries that need to be installed, so I won’t introduce them one by one here. The places used in the project will be explained by themselves. You can learn by yourself. Now let’s walk into vue3+ts. Actual project.

Table of contents

Initial routing configuration

Login page construction

Configure the Pinia repository

Login time judgment

Login form validation

routing authentication

Realize the login box animation effect


Initial routing configuration

Before we start building the background project, we must carefully consider how routing should be configured. The following are the detailed steps for configuring routing in this project. If you want to learn more about vue3 routing knowledge, I recommend referring to my previous article: Vue 3 Routing Advanced— — A complete guide from basics to advanced :

Install third-party packages that configure routing:

pnpm install vue-router

After installing the routing package, we need to create the routing component first, because the project is only a background, so we only need to enter the views file in the src directory, and then create 3 routing components:

After creating the routing component, start the related router configuration:

// 通过vue-router插件实现模板路由配置
import { createRouter, createWebHistory } from 'vue-router'
import { constantRoute } from './routes'

// 创建路由器
const router = createRouter({
  // 路由模式
  history: createWebHistory(),
  routes: constantRoute,
  // 滚动行为
  scrollBehavior() {
    return {
      left: 0,
      top: 0,
    }
  },
})
export default router

Here I separate the attribute object of routes into a js file, which is convenient for later maintenance. The code is as follows:

// 对外暴露路由(常量路由)
export const constantRoute = [
  {
    path: '/login',
    name: 'login', // 命名路由
    component: () => import('@/views/login/index.vue'),
    meta: {
      title: '登录页面',
    },
  },
  {
    path: '/',
    name: 'home',
    component: () => import('@/views/home/index.vue'),
    meta: {
      title: '后台页面',
    },
  },
  {
    path: '/404',
    name: '404',
    component: () => import('@/views/404/index.vue'),
    meta: {
      title: '404界面',
    },
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'any',
    redirect: '/404',
  },
]

If you want to display the written route, you need to use router-view as the route exit, and the components matched by the route will render the response. Here it is displayed on the App root component: 

Next, mount the router at the entry file main.ts:

Realize the progress bar of the web page : Set a progress bar at the top of the web page content to display the loading level of the page. Here I separate the progress bar into a component:

<template>
  <div class="wraps">
    <div ref="bar" class="bar"></div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
let speed = ref<number>(1)
let bar = ref<HTMLElement>()
let timer = ref<number>(0)
const startLoading = () => {
  speed.value = 1
  let dom = bar.value as HTMLElement
  timer.value = window.requestAnimationFrame(function fn() {
    if (speed.value < 90) {
      speed.value += 1
      dom.style.width = speed.value + '%'
      timer.value = window.requestAnimationFrame(fn)
    } else {
      speed.value = 1
      window.cancelAnimationFrame(timer.value)
    }
  })
}
const endLoading = () => {
  let dom = bar.value as HTMLElement
  setTimeout(() => {
    window.requestAnimationFrame(() => {
      speed.value = 100
      dom.style.width = speed.value + '%'
    })
    dom.remove()
  }, 1000)
}
defineExpose({
  startLoading,
  endLoading,
})
</script>

<style lang="scss" scoped>
.wraps {
  position: fixed;
  top: 0;
  width: 100%;
  height: 2px;
  .bar {
    height: inherit; // 继承父级高度
    width: 0;
    background: red;
  }
}
</style>

Next, introduce the progress bar component at the main.ts entry file, and then set it through the navigation guard. The code is as follows:

// 导入进度条
import loadingBarVue from '@/components/loadingBar/index.vue'

const Vnode = createVNode(loadingBarVue)
render(Vnode, document.body)
// 设置路由前置守卫
router.beforeEach((to, from, next) => {
  Vnode.component?.exposed?.startLoading()
  next()
})
// 设置路由后置守卫
router.afterEach((to, from) => {
  Vnode.component?.exposed?.endLoading()
})

Realize dynamic modification of the title :

Since I set the meta attribute in the routing table, we can dynamically modify the title by getting the value of this attribute

Guard in front of the navigation to get the title name and modify it dynamically:

Login page construction

When you visit a website or an application, you may need to enter some credentials to verify your identity, such as username and password, which is called login. Logging in is a very common operation, and almost all websites and applications require users to log in before entering their internal operations. Next, we will explain in detail the construction of the login page:

Create the background of the login page : Creating a background is very simple, just find a background image on the Internet and implement it with the following code

<template>
  <div class="login_container"></div>
</template>

<script setup lang="ts"></script>

<style lang="scss" scoped>
.login_container {
  width: 100%;
  height: 100vh;
  background: url('@/assets/images/bg1.jpg') no-repeat;
  background-size: cover; // 调整背景图片的大小,使其填充整个元素背景,并尽可能保持比例不失真
}
</style>

I just found a background picture of Ultraman, this background picture is casual:

Write the style of the login box page : here I use the form form of element, and then implement responsive layout and grid layout through the grid system el-row and el-col in Element UI, for example:

The combination of el-row and el-col can divide the grid system into a column of 24 grids. You can use the :span attribute to specify the number of columns occupied by each column container. For example, the left sidebar of the page occupies 4 columns, the middle body content occupies 16 columns, and the right advertisement column occupies 4 columns. The corresponding code can be written for:

<el-row>
  <el-col :span="4">左侧侧边栏</el-col>
  <el-col :span="16">中间主体内容</el-col>
  <el-col :span="4">右侧广告栏</el-col>
</el-row>

In the above code, the number of columns occupied by each column container is specified through the :span attribute. When the screen size changes, the page will automatically re-divide the columns and content ratio according to the grid system to adapt to different device screens and resolution.

Next, start writing the real login page, the specific code is as follows:

<template>
  <div class="login_container">
    <el-row>
      <el-col :span="12" :xs="0"></el-col>
      <el-col :span="12" :xs="24">
        <el-form class="login_form">
          <h1>欢迎来到后台管理系统</h1>
          <el-form-item>
            <el-input
              class="input"
              :prefix-icon="User"
              v-model="loginForm.username"
              placeholder="请输入用户名"
            ></el-input>
          </el-form-item>
          <el-form-item>
            <el-input
              class="input"
              :prefix-icon="Lock"
              v-model="loginForm.password"
              type="password"
              show-password
              placeholder="请输入密码"
            ></el-input>
          </el-form-item>
          <el-form-item>
            <el-button class="login_btn" type="primary">登录</el-button>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>
  </div>
</template>

<script setup lang="ts">
import { User, Lock } from '@element-plus/icons-vue'
import { reactive } from 'vue'
// 收集表单数据
let loginForm = reactive({
  username: '',
  password: '',
})
</script>

<style lang="scss" scoped>
.login_container {
  width: 100%;
  height: 100vh;
  background: url('@/assets/images/bg1.jpg') no-repeat;
  background-size: cover; // 调整背景图片的大小,使其填充整个元素背景,并尽可能保持比例不失真
  .login_form {
    position: relative;
    top: 30vh;
    height: 300px;
    width: 60%;
    background: rgba(0, 0, 0, 0.5);
    box-shadow: 0 15px 25px rgba(50, 10, 10, 0.65);
    border-radius: 10px;
    padding: 40px;
  }
  h1 {
    font-size: 30px;
    text-align: center;
    color: white;
    margin: 25px auto 40px;
  }
  .input {
    font-size: 15px;
    height: 38px;
    margin-bottom: 10px;
  }
  .login_btn {
    position: relative;
    height: 40px;
    font-size: 14px;
    color: #fff;
    margin: 20px auto;
    width: 80%;
    background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);
    border-radius: 30px;
    z-index: 1;
    &:hover {
      animation: ani 8s linear infinite;
      height: 40px;
      border: none;
    }
    @keyframes ani {
      0% {
        background-position: 0%;
      }
      100% {
        background-position: 400%;
      }
    }
    &:before {
      content: '';
      position: absolute;
      top: -5px;
      left: -5px;
      right: -5px;
      bottom: -5px;
      z-index: -1;
      background: linear-gradient(90deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);
      background-size: 400%;
      border-radius: 35px;
      transition: 1s;
    }
    &:hover::before {
      filter: blur(20px);
    }
    &:active {
      background: linear-gradient(32deg, #03a9f4, #f441a5, #ffeb3b, #03a9f4);
    }
  }
}
</style>

Login button setting click event : The login button needs to initiate a request for the form data to the warehouse to see if the data matches successfully. If the request succeeds, it will jump to the home page to display the data. If the request fails, the failed login information will pop up. Here you need to use the state management tool pinia to obtain common public data, here first execute the following command on the terminal to install

pnpm i pinia
# 如果最新版本报错,推荐安装旧版本稳定版
pnpm i [email protected]

Therefore, the login button realizes the logical verification jump, and we will continue to talk about it. At the end of the content of this title, we will achieve this effect first. We may have the habit of not clicking the login button after entering the account password but directly clicking the Enter key. To achieve the login effect, here is a simple implementation of such a function:

When we click the Enter key, the page executes the login function. Here we simply realize this effect first, and then improve it according to the situation later:

Configure the Pinia repository

Friends who don't know pinia, I recommend reading the article I explained before: Exploring Pinia: the future of Vue state management . This project will no longer focus on explaining its specific knowledge. The specific explanation is as follows:

Create a new store folder under the src directory, create a new index.ts file under the folder, and expose the following code:

// 设置pinia仓库
import { createPinia } from 'pinia'
const pinia = createPinia()
// 对外暴露:入口文件需要安装仓库
export default pinia

Mount pinia in the main.ts entry file:

Next, create a new user.ts file in the store, which is used to store the warehouse of user information data. The code is as follows:

// 创建用户信息相关数据的仓库
import { defineStore } from 'pinia'
// 引入登录接口
import { reqLogin } from '@/api/user'
// 引入数据类型
import type { loginForm } from '@/api/user/type'

const useUserStore = defineStore('User', {
  // 存储数据
  state: () => {
    return {
      token: localStorage.getItem('TOKEN'), // 用户唯一标识token
    }
  },
  actions: {
    // 处理用户登录
    async userLogin(data: loginForm) {
      const result = await reqLogin(data)
      if (result.code === 200) {
        this.token = result.data.token
        // 本地持久化存储
        localStorage.setItem('TOKEN', result.data.token)
        // 保证当前async函数返回一个成功的promise
        return 'ok'
      } else {
        return Promise.reject(new Error(result.data.message))
      }
    },
  },
  getters: {},
})
// 对外暴露仓库方法
export default useUserStore

In the login file, we get the form data as an object to call the method we defined in pinia to process user login, and then we define the method in pinia to store the data persistently token, the above code has been explained very clearly .

Next, start to write the logic code. By entering the fake data we defined in the mock before, the login jump can be performed, otherwise the login will not be performed.

<script setup lang="ts">
import { User, Lock } from '@element-plus/icons-vue'
// 引入用户信息相关数据的pinia仓库
import userUserStore from '@/store/user'
import { useRouter } from 'vue-router'
import { ElNotification } from 'element-plus'
import { reactive, ref } from 'vue'
// 收集表单数据
let loginForm = reactive({
  username: '',
  password: '',
})
let userStore = userUserStore()
// 获取路由器
let $router = useRouter()
// 定义变量控制按钮加载效果
let loading = ref(false)

// 登录按钮点击事件
const login = async () => {
  // 开始加载按钮loading效果
  loading.value = true
  try {
    await userStore.userLogin(loginForm)
    // 编程式导航跳转到首页
    $router.push('/')
    if (ElNotification) {
      ElNotification.closeAll()
    }
    // 登录成功的提示信息
    ElNotification({
      type: 'success',
      message: '登录成功!',
    })
    // 登录成功加载效果消失
    loading.value = false
  } catch (error) {
    // 登录失败加载效果消失
    loading.value = false
    if (ElNotification) {
      ElNotification.closeAll()
    }
    ElNotification({
      type: 'error',
      message: (error as Error).message,
    })
  }
}

// 监听页面回车键的点击
window.addEventListener('keyup', (e) => {
  if (e.key === 'Enter') {
    console.log(123)
    login()
  }
})
</script>

Login time judgment

Next, we have a requirement that when we enter the correct account number and password, the prompt message of successful login is changed to the style of good morning or afternoon welcome back. The difficulty of this piece is how to judge whether the user is logged in successfully Whether it is a text login or an afternoon login, it will dynamically display the pop-up box style of successful login. The method is as follows:

We add a time.ts file under the utils tool folder to encapsulate the method of obtaining the current time:

// 封装一个函数:获取一个结果:当前早上|上午|下午|晚上
export const getTime = () => {
  let message = ''
  // 通过内置构造函数Date
  const hours = new Date().getHours()
  if (hours <= 9) {
    message = '早上'
  } else if (hours <= 12) {
    message = '上午'
  } else if (hours <= 18) {
    message = '下午'
  } else {
    message = '晚上'
  }
  return message
}

Next, we introduce the time function under the login component, and then dynamically change the current bullet box information through the template string:

Login form validation

Login form verification is the process of verifying each input box in the form after the user enters the login information to ensure that the content entered by the user meets the requirements. By checking the input box, users can be prevented from inputting invalid or illegal information, thereby improving the security and reliability of the system.

In general, login form verification needs to verify the following :

Whether the username and password are empty;

Whether the username and password are empty;

Whether the username and password match, that is, exist in the user database.

If the login form validation fails, provide the user with a message to let them know what went wrong and tell them how to fix it. If the verification is passed, the user can be allowed to log in to the system.

Because we use the element-plus component library, it is very simple to implement form verification. You can go to the official website to view the specific content. I won’t go into details here. The specific implementation steps are as follows:

The defined rules are as follows:

If you want all form items to pass the verification and then send the request, instead of having form data warnings, you have to click the button to have a prompt effect:

Custom validation : The default validation rules have many limitations, and it is not convenient to write particularly complex validation rules. Here I use custom validation rules to make the situation of form validation more complicated:

routing authentication

First explain what routing authentication is. Assuming you know the access path to log in to the background homepage, can you directly access the path to the background homepage without logging in? The answer is yes (in the absence of routing authentication), so it is extremely important to set routing authentication in the background. The specific process is as follows:

Implementation idea : Encapsulate a high-level component of routing authentication by yourself, implement non-login interception, and jump to the login page. Determine whether there is a token locally, if so, return the subcomponent after login, otherwise redirect to the Login component of login.

Here I make the route authentication judgment of the route front guard in the main.ts file. In the route configuration information, use the meta field to identify whether the current route needs to perform login authentication. If necessary, a Boolean type can be defined in the meta field The requiresAuth attribute to identify. as follows:

Realize the login box animation effect

We can set the animation transition effect in the routing. Here we can use the third-party animation library animate. The detailed operation of the specific animation library has been explained in the previous article: Vue--"realize animation and transition effects , so here is no longer To repeat, just to show how to use:

Install animate.css : You can install it with the following command

pnpm install animate.css

Introduce the Animate.css style file in the entry file of the project (such as main.ts or App.vue), for example:

import { createApp } from 'vue'
import App from './App.vue'
import 'element-plus/packages/theme-chalk/src/base.scss'
import 'animate.css'

createApp(App).mount('#app')

Next, introduce the el-form component in the Vue component and render it in the template code, for example:

The construction of the login page function of this project is explained here. The next article will explain the construction of the home page function of the project. Follow bloggers to learn more front-end Vue knowledge. Your support is the biggest motivation for bloggers to create!

Guess you like

Origin blog.csdn.net/qq_53123067/article/details/130909605