• 测试平台开发(二) 高逼格登录页面


    怎么样?哎哟,不错哦。本文就带大家一起用 Vue + Element-UI 把这个不错的登录页面开发出来。

    项目结构分析

    在使用 Vue-CLI 创建 2.x 的脚手架项目后,会生成如下目录文件:

    针对这个目录文件我写了一个脑图进行说明:

    (文字稍微有点多,赶时间的同学看红色部分就可以了

    红色最多的是 src 文件夹,写的代码大部分都是放在这个文件夹下面。

    重要提示,一下就刷到这里,只看文字不看图的同学,还是把图多看几秒哦,看不清楚,可以放大看,哈哈哈。

    程序执行流程

    按照我自己对 Vue 的理解,画了一张几个主要文件之间程序调用执行的流程图:

    (水平有限,有错误请指正)

    图中简单描绘了 index.html、main.js、App.vue、store\index.js、router\index.js、views\login\index.vue 这几个文件之间的调用逻辑。暂时没有用到 components,因为登录界面不涉及到功能组件,只是个页面,代码放在 views 文件夹下即可。

    哈哈我又来提醒了,只看文字不看图的同学,多看几眼,看不清楚,请放大!

    源码

    本文接下来会对这些文件逐个进行代码解析,为了不让文章变得冗长,只贴部分代码,完整代码请到 GitHub 获取:

    https://github.com/dongfanger/sprint-frontend

    如果想把这个项目复原出来,只看我写的文章是不够的,git clone顺手点个 star,学习效果更好哦。

    index.html

    index.html 是项目中唯一的 html 文件

    因为 Vue 实现的是单页面应用。单页面应用,简单理解就是只有一个页面,其他页面都是通过组件的形式挂载到这个页面上的,这样页面切换就会更快速,如桌面应用一般丝滑顺畅。

    其他页面的挂载点其实就是一个 div,其他页面都在放在这个 div 里面的:

        <div id="app"></div>
    

    views\login\index.vue

    本项目是基于 Element-UI 的,需要使用 npm 安装一下

    npm i element-ui -S
    

    .vue 文件是 Vue 框架的代码文件,分为3个部分

    <template>
        html 模板
    </template>
    
    <script>
    	javascript 脚本
    </script>
    
    <style lang="scss" scoped>
    	css 样式
    </style>
    

    为了文章简洁,本文不展示 css 样式的代码。

    template html 模板代码如下,实现了用户名、密码、登录等输入框和按钮:

    <template>
      <div class="loginbody" :style="`background-image: url(${appInfo.backgroundImageUrl})`">
        <div class="login-box">
          <div class="login-title">
            <img class="login-logo" :src="appInfo.loginLogoUrl" alt="logo" />
            <p>{{ appInfo.title }}</p>
          </div>
    
          <div class="login-info">
            <el-form ref="form" class="form-box" :model="form" :rules="formRules">
              <el-form-item :label="'用户名'" prop="username">
                <el-input v-model="form.username" placeholder="请输入用户名" @keyup.enter.native="login" ref="username-input">
                </el-input>
              </el-form-item>
              <el-form-item :label="'密码'" prop="password">
                <el-input
                        v-model="form.password"
                        placeholder="请输入密码"
                        type="password"
                        show-password
                        @keyup.enter.native="login"
                ></el-input>
              </el-form-item>
              <el-form-item>
                <div class="clear">
                  <el-checkbox
                          v-model="form.rememberMe"
                          :value="true"
                          :label="'记住密码'"
                          name="type"
                          class="remember-checkbox"
                  ></el-checkbox>
                  <span class="self-right forgetPwd" @click="forgetPwd">忘记密码?</span>
                </div>
              </el-form-item>
              <el-form-item>
                <el-button type="primary" @click="login" class="login-btn" :loading="isLoging">
                  {{ "登录" }}
                </el-button>
              </el-form-item>
            </el-form>
          </div>
        </div>
      </div>
    </template>
    

    其中页面背景图、logo 图、页面标题是通过 Vuex 来存取的。

    Vuex 是 Vue 的状态管理工具。Vue 组件之间数据传递一般是通过 export 和 import 的方式,但是对于全局数据,这种方式很难管理和维护。Vuex 作为中间媒介,帮助组件之间更好的传递数据。

    javascript 脚本代码如下,给 template 模板填充数据,定义元素行为:

    <script>
      import { mapGetters } from "vuex";
      export default {
        data() {
          return {
            form: {
              username: "",
              password: "",
              rememberMe: true
            },
            formRules: {
              username: [
                { required: true, message: "请输入用户名", trigger: "blur" },
                {
                  trigger: "blur",
                },
              ],
              password: [{ required: true, message: "请输入密码", trigger: "blur" }],
            },
            isLoging: false,
          };
        },
        created() {
        },
        mounted() {
          let autofocusElement = this.$refs["username-input"];
          if (autofocusElement) {
            autofocusElement.focus();
          }
        },
        methods: {
          login() {
          },
          forgetPwd() {
            this.$alert("请联系管理员!", "提示", {
              confirmButtonText: "确定",
              callback: action => {
                this.$message({
                  type: "info",
                  message: `action: ${action}`,
                });
              },
            });
          },
        },
        computed: {
          ...mapGetters(["appInfo"]),
        },
      };
    </script>
    

    其中最后几行的 appInfo 就是定义的全局变量,使用 Vuex 来传递数据。

    store\index.js

    appInfo 的实现代码放在 store\index.js 文件中:

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    const navBarLogoUrl = require("@/assets/image/logo.png");
    const loginLogoUrl = require("@/assets/image/logo@2x.png");
    const backgroundImageUrl = require("@/assets/image/login-bg.png");
    const favicon = require("@/assets/image/favicon.png");
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: {
        appInfo: null,
      },
      getters: {
        appInfo: state => {
          if (state.appInfo) {
            return state.appInfo;
          }
          let localAppInfo = localStorage.getItem("AppInfo");
          if (localAppInfo) {
            return JSON.parse(localAppInfo);
          }
          return {
            dsp: "这是公众号“测试老树”开发的测试平台。",
            navBarLogoUrl,
            loginLogoUrl,
            backgroundImageUrl,
            faviconUrl: favicon,
            title: "测试平台",
          };
        },
      },
      mutations: {
        commitAppInfo(state, appInfo) {
          state.appInfo = appInfo;
        },
      },
      actions: {
        setAppInfo({ commit }, appInfo) {
          let info = {
            dsp: appInfo.dsp,
            title: appInfo.title || "测试平台",
            navBarLogoUrl: appInfo.navBarLogoUrl || navBarLogoUrl,
            loginLogoUrl: appInfo.loginLogoUrl || loginLogoUrl,
            backgroundImageUrl: appInfo.backgroundImageUrl || backgroundImageUrl,
            faviconUrl: appInfo.faviconUrl || favicon,
          };
    
          commit("commitAppInfo", info);
          localStorage.setItem("AppInfo", JSON.stringify(info));
          let { faviconUrl, title } = info;
    
          let iconElement = document.querySelector("#t-icon");
          iconElement.href = faviconUrl;
    
          document.title = title;
        },
      },
      modules: {},
    });
    
    

    App.vue

    App.vue 是根组件,适合做一些初始化工作。

    从“程序执行流程”小节的逻辑图中可以看到,数据存储的操作就是在 App.vue 调用的,代码如下:

    <template>
      <div id="app">
        <router-view/>
      </div>
    </template>
    
    <script>
      import { mapActions } from "vuex";
      export default {
        name: "App",
        data() {
          return {};
        },
        created() {
        },
        methods: {
          ...mapActions(["setAppInfo"]),
        },
      };
    </script>
    

    methods 调用了 setAppInfo 方法,给 appInfo 赋值。

    router\index.js

    到这里,登录页面的代码就已经撸完了。是吗?是的!

    但是还无法访问,因为还没有給它配置路由,浏览器还不知道怎么才能跳转到这个登录页面。

    路由就是访问路径,让浏览器知道输入一个 url 该把哪个页面展示给你看。在以前,页面跳转路由都是放到后端来做的,前端请求后端,后端把渲染好的 html 返回给前端。现在时代不同了,前端直接控制了路由,前后端传递的数据变少了,访问体验也更佳。比如以前地址栏 URL 跳转可能会白屏,现在不会了。

    路由配置代码是放在 router\index.js 文件中的,默认 / 展示 Home 页面,访问 /login 展示 login 页面:

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Home from '../views/Home.vue'
    import login from "../views/login"
    
    Vue.use(VueRouter)
    
    const routes = [
      {
        path: '/',
        name: 'Home',
        component: Home,
        meta: {
          requireAuth: true,
        },
      },
      {
        path: "/login",
        meta: {
          title: "测试平台登录",
        },
        name: "login",
        component: login,
      },
    ]
    
    const router = new VueRouter({
      routes
    })
    

    但是首页不能直接就给别人看呀,得先登录!所以需要编写一个拦截器,必须登录后,才可以访问首页,否则跳转到登录页面:

    router.beforeEach((to, from, next) => {
      if (to.matched.some(auth => auth.meta.requireAuth)) {
        let token = localStorage.getItem("token");
        if (token) {
          next();
        } else {
          next({
            path: "/login",
          });
        }
      } else {
        next();
      }
    });
    

    main.js

    main.js 是程序执行入口,以上所有代码都需要在 main.js 中声明一下:

    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    import Element from "element-ui"
    import "./assets/style/global.scss";
    
    Vue.config.productionTip = false
    
    Vue.use(Element)
    new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount('#app')
    
    

    vue.config.js

    vue.config.js 是 Vue 项目配置文件。比如 index.html 中,页面 title 是通过 <title><%= htmlWebpackPlugin.options.title %></title> 来定义的,可以在配置文件中添加自定义,为 “sprint”:

    const path = require("path");
    
    function resolve(dir) {
      return path.join(__dirname, dir);
    }
    module.exports = {
      publicPath: process.env.NODE_ENV === "development" ? "./" : "/frontend/",
      chainWebpack: config => {
        config.plugin("html").tap(args => {
          args[0].title = "sprint";
          return args;
        });
      },
    };
    
    

    简要回顾

    本文首先展示了登录页面的效果,接着介绍了 Vue-CLI 初始化之后的项目结构,并对程序执行逻辑进行了分析,梳理出来了主要几个文件的调用流程,最后分别对各文件的代码进行了分析。

  • 相关阅读:
    Django 前戏
    SQL基本语句
    如何正确安装Mysql
    JQuery
    解疑答惑—解决脱离标准文档流(恶心的浮动)
    事件
    卷基于快照进行恢复
    centos7下Firewall使用详解
    基于镜像卷启动的虚机快照代码分析
    nova卸载volume源码分析
  • 原文地址:https://www.cnblogs.com/df888/p/13909048.html
Copyright © 2020-2023  润新知